🔗 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
RefCountReferenceto Socket (comms layer stands in for the OS here) - it cannot be freed until the OS is notified etc. - Socket holds
CallbackReferenceto 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
HttpClientConnectionto represent the Socket at the protocol layer. - Factory cals
Socket.setClient(HttpClientConnection) - Socket holds
RefCountReferenceto theHttpClientConnection(which subclassesSocketClient). -
HttpClientConnectionholdsCallbackReferenceto the Socket.
- Factory constructs
-
HttpClientConnectioncalls 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
RefCountreference to the dispatcher.
- Socket requests read from the OS (if it was not already scheduled)
- read completes
- Socket hands the
HttpClientConnectionand the read result to the dispatcher. - Dispatcher holds
CallbackReferencetoHttpClientConnection
- Socket hands the
- Dispatcher calls back
HttpClientConnection-
HttpClientConnectionfails to parse the request.
-
-
HttpClientConnectioncalls 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
RefCountReferenceto the dispatcher
- Socket issues a write to the OS (if not issued immediately)
- write completes
- Socket hands
HttpClientConnectionand the write result to the dispatcher - Dispatcher holds
CallbackReferencetoHttpClientConnection
- Socket hands
- Dispatcher calls back
HttpClientConnectionwith write status- Dispatch drops its
CallbackReference
- Dispatch drops its
-
HttpClientConnectioncalls 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
HttpClientConnectioninforming it that it has been released. - Socket drops its
CallbackReferenceto theHttpClientConnection
- Socket calls ‘socket_detached’ on
-
HttpClientConnectionhas noRefCountReferencesheld on it, and so frees. - Socket calls setClient on itself with a
LingerCloseSocketClient.- Socket holds
RefCountReferenceto theLingerCloseSocketClient
- Socket holds
-
LingerCloseSocketClientcalls read on the socket to detect EOF- socket schedules read to the OS now
-
LingerCloseSocketClientregisters a callback for time now + LINGERDELAY-
EventSchedulerholds aCallbackReferenceto theLingerCloseSocketClientand 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
LingerCloseSocketClientand the read result to the dispatcher. - Dispatcher holds
CallbackReferencetoLingerSocketClient
- Dispatch hands read result to
LingerSocketClient-
LingerSocketClientsees that EOF has been reached.
-
-
LingerSocketcalls 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
RefCountReferenceto 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
-
LingerSocketClientfrees due to no references.
Case 2: the Linger timeout fires.
- the
EventSchedulerputs theLingerSocketClientinto the dispatch queue.- Dispatcher holds
CallbackReferenceto theLingerSocketClient -
EventSchedulerdrops itsCallbackReferenceto theLingerSocketClient
- Dispatcher holds
- Dispatcher fires event to
LingerSocketClient- Dispatcher drops
CallbackReferenceto theLingerSocketClient
- Dispatcher drops
-
LingerSocketClientcalls 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
RefCountReferenceto 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
-
LingerSocketClientfrees due to no references.
🔗 Internal Request
- listening socket factory creates
SocketClientobject for an opened socket:- Socket owns the
SocketClientviaRefCount. - 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.
-
SocketClienthas 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
HTTPClientwill 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. TheSocketClientregisters itself with theClientRequest, at which point theClientRequestmay initiate its request from the core: Socket has callbacks toSocketClientSocketClientowns Socket, and owns theClientRequestit has created. -
SocketClientcallsClientRequest.atReadFront() to indicate theClientRequestis 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 toSocketClientSocketClientowns Socket, and owns theClientRequestit has created.ClientRequesthas a callback handle toSocketClient -
ClientRequestcallsSocketClient.finishedReadingRequest() to indicate it will not read any more data from theSocketClient, and that the next request can be parsed. -
SocketClientcallsClientRequest.atWriteFront() to indicate theClientRequestis now at the front of the queue for the socketClientRequesthas callbacks toSocketClientto call on events:WillNotReadAnyMore,SocketMustBeClosed,SocketMustBeReset. Socket has callbacks toSocketClientSocketClientowns Socket, and owns theClientRequestit has created.ClientRequesthas callbacks toSocketClientto call on events:WillNotReadAnyMore,SocketMustBeClosed,SocketMustBeReset, and -
ClientRequestasks for a response to this normalised request from the URL mapper at the core of squid Socket has callbacks toSocketClientSocketClientowns Socket, and owns theClientRequestit has created.ClientRequesthas calbacks toSocketClientto 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