194 lines
6.3 KiB
Plaintext
194 lines
6.3 KiB
Plaintext
|
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 konw :
|
||
|
- 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 (instanciates 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).
|
||
|
|