🔗 Wishlist for internal subsystem interactions
This is an attempt to document how the various parts of squid should interact for a number of use cases.
Currently draft, read in fear for your mind
🔗 Guiding principles
- Objects that need to make arbitrary calls on other objects should
hold
RefCountReferences
- Objects that need to inform of completion of events should hold
CallbackReferences
- One Class, One Mission
- Clear Owners - try to have only clear owners for objects
🔗 General Ownership notes(draft still)
- On the client side of squid, there are listening sockets and individual client sockets. The squid configuration owns the listening client side sockets - it determines lifetime. The OS also owns those sockets, but no events from it will cause closedown in the normal run of things, so there are two owners: the squid config and the os. For individual client sockets, there is only one owner - the remote client, which is represented in our process by the OS.
- Requests on a client side connection are owned by the connection.
- The data source from the store or upstream to satisfy a connection is owned by the request.
- Upstream requests are owned by the requestor - the client request when the request is not being diverted to the store, and the store when it is being copied to cache.
- The server-side connection is owned by the OS - it must remain a valid object while the FD is open Its also owned by a protocol dispatcher for that protocol - one that puts a series of requests onto it.
- The protocol dispatcher is a global resource owned by the configuration and all current requests going through it.
- The store is a global resource owned by the configuration. it owns requests it is making on behalf of the client side.
- a store client is owned by the client reading data from it
🔗 Use Cases
🔗 Request that is not parsable
In this example, Socket refers to an object representing a single OS Socket - an fd on unix, a HANDLE on windows.
HttpClientConnection
refers to a single HttpClientConnection
object
- which represents the use of a single socket for HTTP.
- OS reports new socket available.
- Comms layer constructs Socket object.
- Comms layer holds
RefCountReference
to Socket (comms layer stands in for the OS here) - it cannot be freed until the OS is notified etc. - Socket holds
CallbackReference
to the comms layer to notify it of close.
- New Socket is passed to the listening factory for the port it was
recieved on.
- Factory constructs
HttpClientConnection
to represent the Socket at the protocol layer. - Factory cals
Socket.setClient(HttpClientConnection)
- Socket holds
RefCountReference
to theHttpClientConnection
(which subclassesSocketClient
). -
HttpClientConnection
holdsCallbackReference
to the Socket.
- Factory constructs
-
HttpClientConnection
calls read() on the Socket- For some systems, the read is scheduled on the socket now. For others, when the next event loop occurs, the read willl be done.
- Socket gets a
RefCount
reference to the dispatcher.
- Socket requests read from the OS (if it was not already scheduled)
- read completes
- Socket hands the
HttpClientConnection
and the read result to the dispatcher. - Dispatcher holds
CallbackReference
toHttpClientConnection
- Socket hands the
- Dispatcher calls back
HttpClientConnection
-
HttpClientConnection
fails to parse the request.
-
-
HttpClientConnection
calls write on the Socket to send an error page- depending on the socket logic, a write may be issued immediately, or it may wait for the next event loop.
- Socket gets a
RefCountReference
to the dispatcher
- Socket issues a write to the OS (if not issued immediately)
- write completes
- Socket hands
HttpClientConnection
and the write result to the dispatcher - Dispatcher holds
CallbackReference
toHttpClientConnection
- Socket hands
- Dispatcher calls back
HttpClientConnection
with write status- Dispatch drops its
CallbackReference
- Dispatch drops its
-
HttpClientConnection
calls clean_close on Socket- The Socket checks for outstanding reads or writes
- Socket calls shutdown(SD_SEND) to the os
- Socket calls ‘socket_detached’ on
HttpClientConnection
informing it that it has been released. - Socket drops its
CallbackReference
to theHttpClientConnection
- Socket calls ‘socket_detached’ on
-
HttpClientConnection
has noRefCountReferences
held on it, and so frees. - Socket calls setClient on itself with a
LingerCloseSocketClient
.- Socket holds
RefCountReference
to theLingerCloseSocketClient
- Socket holds
-
LingerCloseSocketClient
calls read on the socket to detect EOF- socket schedules read to the OS now
-
LingerCloseSocketClient
registers a callback for time now + LINGERDELAY-
EventScheduler
holds aCallbackReference
to theLingerCloseSocketClient
and dispatcher
-
- Or Socket may schedule read to the OS now, on the next event loop.
Case 1: the read gets EOF first (the shutdown was acked by the far end)
- the read completes
- Socket marks its read channel as closed.
- Socket hands the
LingerCloseSocketClient
and the read result to the dispatcher. - Dispatcher holds
CallbackReference
toLingerSocketClient
- Dispatch hands read result to
LingerSocketClient
-
LingerSocketClient
sees that EOF has been reached.
-
-
LingerSocket
calls close on Socket.- Socket does sd_shutdown(SD_BOTH) and close(fd).
- Socket calls back the comms layer callback noting its finished with
- Comms layer drops its
RefCountReference
to the socket.
- Comms layer drops its
- Socket frees due to no references
- Socket calls ‘socket_detached’ on the
LingerSocketClient
.
- Socket calls ‘socket_detached’ on the
-
LingerSocketClient
frees due to no references.
Case 2: the Linger timeout fires.
- the
EventScheduler
puts theLingerSocketClient
into the dispatch queue.- Dispatcher holds
CallbackReference
to theLingerSocketClient
-
EventScheduler
drops itsCallbackReference
to theLingerSocketClient
- Dispatcher holds
- Dispatcher fires event to
LingerSocketClient
- Dispatcher drops
CallbackReference
to theLingerSocketClient
- Dispatcher drops
-
LingerSocketClient
calls socket.force_close()- Socket does sd_shutdown(SD_BOTH) and close(fd).
- Socket calls back the comms layer callback noting its finished with
- Comms layer drops its
RefCountReference
to the socket.
- Comms layer drops its
- Socket frees due to no references
- Socket calls ‘socket_detached’ on the
LingerSocketClient
.
- Socket calls ‘socket_detached’ on the
-
LingerSocketClient
frees due to no references.
🔗 Internal Request
- listening socket factory creates
SocketClient
object for an opened socket:- Socket owns the
SocketClient
viaRefCount
. - Socket is owned by the comms layer. If FD based, its in a table. If HANDLE based its put into a set of open sockets.
-
SocketClient
has a weak reference to the Socket: It new Client owns the socket. Nothing owns the Client. Socket has callback to the client to notify on events :ReadPossible
(data has arrived), Close(by request or external occurence). Other events get callbacks as each is queued - ask the socket to read and hand the callback to be called in. This could be ‘this’ if we structure the ap well, or it could be some other thing.needs more detail/care.
- Socket owns the
- Client parses the URL into a normalised request using its native
protocol : an
HTTPClient
will parse the URL using HTTP rules, a FTP client would do whatever FTP proxies do to get a target server etc. This creates a new object, to handle that one request - aClientRequest
. TheSocketClient
registers itself with theClientRequest
, at which point theClientRequest
may initiate its request from the core: Socket has callbacks toSocketClient
SocketClient
owns Socket, and owns theClientRequest
it has created. -
SocketClient
callsClientRequest
.atReadFront
() to indicate theClientRequest
is now at the front of the queue for the socket and is able to start reading body data if it wants to. Socket has callbacks toSocketClient
SocketClient
owns Socket, and owns theClientRequest
it has created.ClientRequest
has a callback handle toSocketClient
-
ClientRequest
callsSocketClient
.finishedReadingRequest() to indicate it will not read any more data from theSocketClient
, and that the next request can be parsed. -
SocketClient
callsClientRequest
.atWriteFront() to indicate theClientRequest
is now at the front of the queue for the socketClientRequest
has callbacks toSocketClient
to call on events:WillNotReadAnyMore
,SocketMustBeClosed
,SocketMustBeReset
. Socket has callbacks toSocketClient
SocketClient
owns Socket, and owns theClientRequest
it has created.ClientRequest
has callbacks toSocketClient
to call on events:WillNotReadAnyMore
,SocketMustBeClosed
,SocketMustBeReset
, and -
ClientRequest
asks for a response to this normalised request from the URL mapper at the core of squid Socket has callbacks toSocketClient
SocketClient
owns Socket, and owns theClientRequest
it has created.ClientRequest
has calbacks toSocketClient
to call on events:WillNotReadAnyMore
,SocketMustBeClosed
,SocketMustBeReset
. - the URL mapper determines (based on the scheme or url path) that the request is for an internal resource
- The request is forwarded to the internal resource to satisfy. An object is given to the Client which represents the ‘source’ of the data - this has methods on it to allow requesting the response headers, pulling of the data stream, signalling cancellation of the clients request.
- The internal resource object is called by the client to initiate transfer, it then delivers the internal headers, and the internally generated data.
- The internal resource signals end of file to the client in its last request to read data.
- the client