MEDIUM: stream-int: make sure to try to immediately validate the connection
authorWilly Tarreau <w@1wt.eu>
Wed, 4 Mar 2020 15:42:03 +0000 (16:42 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 4 Mar 2020 18:29:12 +0000 (19:29 +0100)
In the rare case of immediate connect() (unix sockets, socket pairs, and
occasionally TCP over the loopback), it is counter-productive to subscribe
for sending and then getting immediately back to process_stream() after
having passed through si_cs_process() just to update the connection. We
already know it is established and it doesn't have any handshake anymore
so we just have to complete it and return to process_stream() with the
stream_interface in the SI_ST_RDY state. In this case, process_stream will
simply loop back to the beginning to synchronize the state and turn it to
SI_ST_EST/ASS/CLO/TAR etc.

This will save us from having to needlessly subscribe in the connect()
code, something which in addition cannot work with edge-triggered pollers.

src/backend.c
src/stream.c

index 39791f6..251a7a5 100644 (file)
@@ -1525,6 +1525,40 @@ int connect_server(struct stream *s)
 
        }
 
+       /* Now handle synchronously connected sockets. We know the stream-int
+        * is at least in state SI_ST_CON. These ones typically are UNIX
+        * sockets, socket pairs, and occasionally TCP connections on the
+        * loopback on a heavily loaded system.
+        */
+       if ((srv_conn->flags & CO_FL_ERROR || srv_cs->flags & CS_FL_ERROR))
+               s->si[1].flags |= SI_FL_ERR;
+
+       /* If we had early data, and the handshake ended, then
+        * we can remove the flag, and attempt to wake the task up,
+        * in the event there's an analyser waiting for the end of
+        * the handshake.
+        */
+       if (!(srv_conn->flags & (CO_FL_WAIT_XPRT | CO_FL_EARLY_SSL_HS)))
+               srv_cs->flags &= ~CS_FL_WAIT_FOR_HS;
+
+       if (!si_state_in(s->si[1].state, SI_SB_EST|SI_SB_DIS|SI_SB_CLO) &&
+           (srv_conn->flags & CO_FL_WAIT_XPRT) == 0) {
+               s->si[1].exp = TICK_ETERNITY;
+               si_oc(&s->si[1])->flags |= CF_WRITE_NULL;
+               if (s->si[1].state == SI_ST_CON)
+                       s->si[1].state = SI_ST_RDY;
+       }
+
+       /* Report EOI on the channel if it was reached from the mux point of
+        * view.
+        *
+        * Note: This test is only required because si_cs_process is also the SI
+        *       wake callback. Otherwise si_cs_recv()/si_cs_send() already take
+        *       care of it.
+        */
+       if ((srv_cs->flags & CS_FL_EOI) && !(si_ic(&s->si[1])->flags & CF_EOI))
+               si_ic(&s->si[1])->flags |= (CF_EOI|CF_READ_PARTIAL);
+
        return SF_ERR_NONE;  /* connection is OK */
 }
 
index 9798c5f..a54f523 100644 (file)
@@ -2085,6 +2085,10 @@ struct task *process_stream(struct task *t, void *context, unsigned short state)
                        if (si_b->state == SI_ST_REQ)
                                back_handle_st_req(s);
 
+                       /* get a chance to complete an immediate connection setup */
+                       if (si_b->state == SI_ST_RDY)
+                               goto resync_stream_interface;
+
                        /* applets directly go to the ESTABLISHED state. Similarly,
                         * servers experience the same fate when their connection
                         * is reused.