An FD has a state : - CLOSED - READY - ERROR (?) - LISTEN (?) A connection has a state : - CLOSED - ACCEPTED - CONNECTING - ESTABLISHED - ERROR A stream interface has a state : - INI, REQ, QUE, TAR, ASS, CON, CER, EST, DIS, CLO Note that CON and CER might be replaced by EST if the connection state is used instead. CON might even be more suited than EST to indicate that a connection is known. si_shutw() must do : data_shutw() if (shutr) { data_close() ctrl_shutw() ctrl_close() } si_shutr() must do : data_shutr() if (shutw) { data_close() ctrl_shutr() ctrl_close() } Each of these steps may fail, in which case the step must be retained and the operations postponed in an asynchronous task. The first asynchronous data_shut() might already fail so it is mandatory to save the other side's status with the connection in order to let the async task know whether the 3 next steps must be performed. The connection (or perhaps the FD) needs to know : - the desired close operations : DSHR, DSHW, CSHR, CSHW - the completed close operations : DSHR, DSHW, CSHR, CSHW On the accept() side, we probably need to know : - if a header is expected (eg: accept-proxy) - if this header is still being waited for => maybe both info might be combined into one bit - if a data-layer accept() is expected - if a data-layer accept() has been started - if a data-layer accept() has been performed => possibly 2 bits, to indicate the need to free() On the connect() side, we need to know : - the desire to send a header (eg: send-proxy) - if this header has been sent => maybe both info might be combined - if a data-layer connect() is expected - if a data-layer connect() has been started - if a data-layer connect() has been completed => possibly 2 bits, to indicate the need to free() On the response side, we also need to know : - the desire to send a header (eg: health check response for monitor-net) - if this header was sent => might be the same as sending a header over a new connection Note: monitor-net has precedence over proxy proto and data layers. Same for health mode. For multi-step operations, use 2 bits : 00 = operation not desired, not performed 10 = operation desired, not started 11 = operation desired, started but not completed 01 = operation desired, started and completed => X != 00 ==> operation desired X & 01 ==> operation at least started X & 10 ==> operation not completed Note: no way to store status information for error reporting. Note2: it would be nice if "tcp-request connection" rules could work at the connection level, just after headers ! This means support for tracking stick tables, possibly not too much complicated. Proposal for incoming connection sequence : - accept() - if monitor-net matches or if mode health => try to send response - if accept-proxy, wait for proxy request - if tcp-request connection, process tcp rules and possibly keep the pointer to stick-table - if SSL is enabled, switch to SSL handshake - then switch to DATA state and instantiate a session We just need a map of handshake handlers on the connection. They all manage the FD status themselves and set the callbacks themselves. If their work succeeds, they remove themselves from the list. If it fails, they remain subscribed and enable the required polling until they are woken up again or the timeout strikes. Identified handshake handlers for incoming connections : - HH_HEALTH (tries to send OK and dies) - HH_MONITOR_IN (matches src IP and adds/removes HH_SEND_OK/HH_SEND_HTTP_OK) - HH_SEND_OK (tries to send "OK" and dies) - HH_SEND_HTTP_OK (tries to send "HTTP/1.0 200 OK" and dies) - HH_ACCEPT_PROXY (waits for PROXY line and parses it) - HH_TCP_RULES (processes TCP rules) - HH_SSL_HS (starts SSL handshake) - HH_ACCEPT_SESSION (instantiates a session) Identified handshake handlers for outgoing connections : - HH_SEND_PROXY (tries to build and send the PROXY line) - HH_SSL_HS (starts SSL handshake) For the pollers, we could check that handshake handlers are not 0 and decide to call a generic connection handshake handler instead of usual callbacks. Problem is that pollers don't know connections, they know fds. So entities which manage handlers should update change the FD callbacks accordingly. With a bit of care, we could have : - HH_SEND_LAST_CHUNK (sends the chunk pointed to by a pointer and dies) => merges HEALTH, SEND_OK and SEND_HTTP_OK It sounds like the ctrl vs data state for the connection are per-direction (eg: support an async ctrl shutw while still reading data). Also support shutr/shutw status at L4/L7. In practice, what we really need is : shutdown(conn) = conn.data.shut() conn.ctrl.shut() conn.fd.shut() close(conn) = conn.data.close() conn.ctrl.close() conn.fd.close() With SSL over Remote TCP (RTCP + RSSL) to reach the server, we would have : HTTP -> RTCP+RSSL connection <-> RTCP+RRAW connection -> TCP+SSL connection The connection has to be closed at 3 places after a successful response : - DATA (RSSL over RTCP) - CTRL (RTCP to close connection to server) - SOCK (FD to close connection to second process) Externally, the connection is seen with very few flags : - SHR - SHW - ERR We don't need a CLOSED flag as a connection must always be detached when it's closed. The internal status doesn't need to be exposed : - FD allocated (Y/N) - CTRL initialized (Y/N) - CTRL connected (Y/N) - CTRL handlers done (Y/N) - CTRL failed (Y/N) - CTRL shutr (Y/N) - CTRL shutw (Y/N) - DATA initialized (Y/N) - DATA connected (Y/N) - DATA handlers done (Y/N) - DATA failed (Y/N) - DATA shutr (Y/N) - DATA shutw (Y/N) (note that having flags for operations needing to be completed might be easier) -------------- Maybe we need to be able to call conn->fdset() and conn->fdclr() but it sounds very unlikely since the only functions manipulating this are in the code of the data/ctrl handlers. FDSET/FDCLR cannot be directly controlled by the stream interface since it also depends on the DATA layer (WANT_READ/wANT_WRITE). But FDSET/FDCLR is probably controlled by who owns the connection (eg: DATA).