4 Implementation details (not part of API)
Here are some notes that try to explain how are things implemented. If this is out of sync or if it does not make sense at all, fixing it is not a hight priority, and definitely not a blocker.
4.1 Overview
[in package HTTP2/CORE]
There are three core groups of classes:
HTTP/2 Connections, derived from
HTTP2-CONNECTION, represent a connection as in the RFC,Streams, derived from
HTTP2-STREAM-MINIMAL, represent a HTTP/2 stream as in the RFC,Dispatchers, derived from HTTP2/server/shared:BASE-DISPATCHER, represent a server that may receive and dispatch new connections.
Dispatcher create new connection instances as they receive requests, and connections create new stream instances to serve the streams to the clients.
Clients do not need dispatchers; they create the connection to the server explicitly.
-
Function that reads and processes number of octets, and returns next function and its needed size.
4.1.1 HTTP/2 Connections
[class] HTTP2-CONNECTION FRAME-CONTEXT STREAM-COLLECTION FLOW-CONTROL-MIXIN
A simple connection instance reflecting several aspects of the connections:
connection is a collection of streams (
STREAM-COLLECTION),connection maintains flow control (
FLOW-CONTROL-MIXIN),connection maintains hpack state (
HPACK-ENDPOINT),connection maintain some internal configuration variables such as maximum frames size (
FRAME-CONTEXT),connection maintain window sizes (
FLOW-CONTROL-MIXIN),connection can create new streams and needs to track the parameters of the new stream (class, windows sizes)
At any point of time, a HTTP2-CONNECTION expects explicit number of octets as an
input, i.e., 9 octets for a header, or frame size octets of the frame
payload. This information is not stored in the connection (why?), but lexically
in a loop in functions that make the update, together with a function that acts
on the connection when the data are available. See
HTTP2/STREAM-OVERLAY:PROCESS-PENDING-FRAMES that reads the input data
from (Common Lisp) binary STREAM as one example. See Processing data frames.
[class] SERVER-HTTP2-CONNECTION HTTP2-CONNECTION
Server connection initiate even numbered streams.
[class] CLIENT-HTTP2-CONNECTION HTTP2-CONNECTION
Client connections initiate odd-numbered streams.
[generic-function] GET-PEER-NAME OBJECT
Name of the peer. Used primarily in log messages and when object is printed.
Content can vary, e.g.,
for a server connection peer IP and port,
for a client connection hostname of the peer.
The fallback is unescaped print of the object.
- [generic-function] GET-INITIAL-PEER-WINDOW-SIZE OBJECT
Data and flow control
HTTP streams can receive some data (content, body). We can see treat in several ways:
we can collect the data till all is received (end of stream from the peer) and then process it as whole. If you need this, use
BODY-COLLECTING-MIXINclass andGET-BODYfunction, orTEXT-COLLECTING-STREAMclass andHTTP-STREAM-TO-STRINGfunction.we may want to do something with each recieved chunk of data as soon as it arrives. In this case, specialize
APPLY-DATA-FRAMEorAPPLY-TEXT-DATA-FRAME.
In both cases, one may want to work either with a string or with octets. The
latter is default, for the former use UTF8-PARSER-MIXIN and/or
FALLBACK-ALL-IS-ASCII.
The body can be gzipped; in such case derive the stream from GZIP-DECODING-MIXIN.
[generic-function] APPLY-DATA-FRAME STREAM PAYLOAD START END
STREAM(a HTTP/2 stream) should process receivedPAYLOADfrom the data frame fromSTARTtoEND.
- [generic-function] APPLY-TEXT-DATA-FRAME STREAM TEXT
[method] APPLY-DATA-FRAME (STREAM
UTF8-PARSER-MIXIN) PAYLOAD START ENDWhen headers satisfy
IS-UTF8-P, convert the the binary frame to text (taking care about UTF-8 characters possibly split between frames) and callAPPLY-TEXT-DATA-FRAMEon it.
-
Mixin to collect all payload parts to one string.
[method] APPLY-DATA-FRAME (STREAM
BODY-COLLECTING-MIXIN) DATA START ENDConcatenate received data to the
BODYslot of the object.
-
Defines a method on
APPLY-DATA-FRAMEthat, ifCONTENT-TYPEindicates UTF-8, reads octets, converts them to UTF-8 text, and callsAPPLY-TEXT-DATA-FRAMEon the stream and the text.
[function] IS-UTF8-P HEADERS
Test headers to see if the encoding is UTF-8.
[method] APPLY-DATA-FRAME AROUND (STREAM
GZIP-DECODING-MIXIN) PAYLOAD START ENDWhen there is a
Content-Encoding: gzipheader, decompress the data and call nextAPPLY-DATA-FRAMEmethods.
[class] TEXT-COLLECTING-STREAM
Mixin that collect all the received body (possibly unzipped data frames converted to proper encoding) into its
TEXTslot.
Data to write are send by WRITE-DATA-FRAME-MULTI or WRITE-DATA-FRAME. These do
not take into account limits set up by the peer, so use WRITE-BINARY-PAYLOAD instead.
Send and received octets are accounted for and must be within some limits (window).
User in most cases probably prefers not to care about windows and data frames, and binary vs text and content encoding and compression of the data.
This poses several topics:
Overlay a Common Lisp stream over data frames. This is done using gray streams, see XXX.
Handle window management. The problem is that we do not want to block an individual stream handler. So the options I can see are:
We can actually ignore the issue, if the size of the output is small, it would work. You can
WRITE-DATA-FRAMEif you need to send binary data up to 16384 octets (or more if negotiated with the per)We can signal error when the output is too big, and let the application code handle that. The application would need a callback to resume operations when the window is open again. Problem - it complicates the application.
We can buffer all the input and send it when there is an opportunity automatically. Problem - potentially conses a lot.
Starting from the low-level, options are:
4.1.2 HTTP/2 Streams
[class] HTTP2-STREAM-MINIMAL FLOW-CONTROL-MIXIN
Part of representation of HTTP/2 stream needed to read and write frames.
[class] HTTP2-STREAM HTTP2-STREAM-MINIMAL FLOW-CONTROL-MIXIN
Representation of HTTP/2 stream. See RFC7540.
-
HTTP2 state. Currently a symbol from fixed list, might be a number in future.
-
Live connections can have zero, one or more HTTP/2 Streams that are not closed, and can generate new ones.
[class] SERVER-STREAM HTTP2-STREAM
Server streams need to track attributes from the client headers such as
PATH.
4.1.3 Classes
There are two parallel class hierarchies, one for HTTP2 connections, one for the HTTP2 streams.
In general, CLIENT- and SERVER- classes implement required minimal behaviour for the client, resp. server, and are supposed to be stable, while VANILLA classes implement "typical" or best practice behaviour and may change in time. They should be good for ad-hoc activities and experiments; for stable behaviour, make your class using appropriate mixins.
[class] CLIENT-HTTP2-CONNECTION HTTP2-CONNECTION
Client connections initiate odd-numbered streams.
- [generic-function] GET-STREAM-CLASS OBJECT
[function] OPEN-HTTP2-STREAM CONNECTION HEADERS &KEY END-STREAM (END-HEADERS
T) STREAM-PARSOpen HTTP/2 stream (typically, from client side) by sending headers.
STREAM-PARSare used as parameters for creating new stream instance.HEADERSare headers to be send for the client. You can use REQUEST-HEADERS to get necessary headers.END-HEADERSis aflag to the server that no more headers would be sent; true by default.END-STREAMis a flag to the server that there would be no payload.
[generic-function] GET-NETWORK-STREAM OBJECT
Get network stream for the object.
- [generic-function] GET-BODY OBJECT
- [generic-function] GET-PATH OBJECT
[function] SEND-HEADERS STREAM HEADERS &KEY END-STREAM (END-HEADERS
T) &ALLOW-OTHER-KEYSSend
HEADERSto a HTTP2 stream. The stream is returned.The
END-HEADERSandEND-STREAMallow to set the appropriate flags.Inside HANDLER macro, this names a function that has the
STREAMargument implicit and onlyHEADERSand key parameters are to be provided.
- [generic-function] GET-METHOD OBJECT
- [generic-function] GET-HEADERS OBJECT
- [generic-function] GET-SCHEME OBJECT
- [generic-function] GET-AUTHORITY OBJECT
[class] TIMESHIFT-PINGING-CONNECTION
A mixin that implements specific
DO-PINGandDO-PONGso that the RTT is printed afterDO-PINGis send.
4.1.4 Writing frames
There are two strategies how to write data that a connection produces - either
write them to a stream, or store them for future. They specialize QUEUE-FRAME
generic function to actually store the data.
[class] WRITE-BUFFER-CONNECTION-MIXIN
Stores queued frame in a per-connection write buffer. The internals of the buffer are opaque.
[class] STREAM-BASED-CONNECTION-MIXIN
A mixin for connections that read frames from and write to Common Lisp stream (in slot
NETWORK-STREAM).
[generic-function] QUEUE-FRAME CONNECTION FRAME
Send or queue
FRAME(octet vector) to the connection.Each connection has to actually implement it.
Existing implementations are for:
STREAM-BASED-CONNECTION-MIXIN, where the data are simply sent to the List stream,WRITE-BUFFER-CONNECTION-MIXIN, where the data are pushed to a buffer.
[function] PARSE-CLIENT-PREFACE CONNECTION BUFFER
Parse client preface.
Check that buffer contains a client preface, or raise an error.
Then write the initial settings frame, and expect normal frame. Actually, this should be a settings frame, but this is not enforced now.
[variable] +CLIENT-PREFACE-START+ #(80 82 73 32 42 32 72 84 84 80 47 50 46 48 13 10 13 10 83 77 13 10 13 10)
The client connection preface starts with a sequence of 24 octets, which in hex notation is this. That is, the connection preface starts with the string "PRI * HTTP/2.0rnrnSMrnrn").
4.1.5 Processing data frames
[generic-function] APPLY-DATA-FRAME STREAM PAYLOAD START END
STREAM(a HTTP/2 stream) should process receivedPAYLOADfrom the data frame fromSTARTtoEND.
[method] APPLY-DATA-FRAME STREAM PAYLOAD START END
Just ignore the data and warn about it.
-
Mixin to collect all payload parts to one string.
[method] APPLY-DATA-FRAME (STREAM
BODY-COLLECTING-MIXIN) DATA START ENDConcatenate received data to the
BODYslot of the object.
4.2 HPACK - RFC7541 implementation.
[in package HTTP2/HPACK]
HTTP2 headers can be compressed - and implementation needs to be able to decompress - by two (or maybe three) ways:
Headers (with or without value) in static headers table (defined in RFC) can be replaced by appropriate a code,
Headers being sent out may be cached and after first use in dynamic headers table and later replaced by a code,
Headers as string can be Huffman compressed.
[function] DO-DECODED-HEADERS FN CONTEXT DATA &OPTIONAL (START 0) (END (
LENGTHDATA))Call
FNon each header decoded fromDATAbetweenSTARTandEND.Return nil if the complete headers were processed, or index to first unprocessed octet.
[function] COMPILE-HEADERS HEADERS CONTEXT
Compile headers with given
CONTEXTto an array. Context can beNIL; if it is not, the headers are stored in the dynamic table and theCONTEXTit is updated appropriately.Each header is a list of form (
NAMEVALUE&KEYHUFFMANINDEX):NO-INDEXindicates that it should be stored in the dynamic table (if possible), should not, or use the NEVER encoding (value :never),HUFFMANto use huffman encoding
[variable] *USE-HUFFMAN-CODING-BY-DEFAULT* :MAYBE
Is set, the headers are by default huffman-encoded. Special value :maybe means whatever is shorter, plain encoding in case of draw.
Dynamic headers table is implemented by HPACK-CONTEXT class that should be
from API point considered opaque. Context is needed twice for each connection,
once for each direction of communication.
-
Dynamic tables implementation: they are stored in an adjustable array, with [0] element storing first element initially (indexed by s+1), and (s+k)th element after k insertions.
After deletion of D elements, element s+k is stored on index D, element s+1 on index
<---------- Index Address Space ----------> <-- Static Table --> <-- Dynamic Table --> +---+-----------+---+ +-----+-----------+-----+ | 1 | ... | s | |s+1+D| ... |s+k+D| ...deleted... +---+-----------+---+ +-----+-----------+-----+ k D 0 <----- table aref index --------------> ^ | | V Insertion Point Dropping Point
[function] UPDATE-DYNAMIC-TABLE-SIZE CONTEXT NEW-SIZE
Update dynamic table for new size that is smaller than the previous one. This is called after receiving appropriate settings update.
Zero size means evict completely; in this case the new vector can be cleaned
- [generic-function] GET-DYNAMIC-TABLE-SIZE OBJECT
- [generic-function] GET-UPDATES-NEEDED OBJECT
4.3 Data and flow control
[in package HTTP2/CORE]
HTTP streams can receive some data (content, body). We can see treat in several ways:
we can collect the data till all is received (end of stream from the peer) and then process it as whole. If you need this, use
BODY-COLLECTING-MIXINclass andGET-BODYfunction, orTEXT-COLLECTING-STREAMclass andHTTP-STREAM-TO-STRINGfunction.we may want to do something with each recieved chunk of data as soon as it arrives. In this case, specialize
APPLY-DATA-FRAMEorAPPLY-TEXT-DATA-FRAME.
In both cases, one may want to work either with a string or with octets. The
latter is default, for the former use UTF8-PARSER-MIXIN and/or
FALLBACK-ALL-IS-ASCII.
The body can be gzipped; in such case derive the stream from GZIP-DECODING-MIXIN.
[generic-function] APPLY-DATA-FRAME STREAM PAYLOAD START END
STREAM(a HTTP/2 stream) should process receivedPAYLOADfrom the data frame fromSTARTtoEND.
- [generic-function] APPLY-TEXT-DATA-FRAME STREAM TEXT
[method] APPLY-DATA-FRAME (STREAM
UTF8-PARSER-MIXIN) PAYLOAD START ENDWhen headers satisfy
IS-UTF8-P, convert the the binary frame to text (taking care about UTF-8 characters possibly split between frames) and callAPPLY-TEXT-DATA-FRAMEon it.
-
Mixin to collect all payload parts to one string.
[method] APPLY-DATA-FRAME (STREAM
BODY-COLLECTING-MIXIN) DATA START ENDConcatenate received data to the
BODYslot of the object.
-
Defines a method on
APPLY-DATA-FRAMEthat, ifCONTENT-TYPEindicates UTF-8, reads octets, converts them to UTF-8 text, and callsAPPLY-TEXT-DATA-FRAMEon the stream and the text.
[function] IS-UTF8-P HEADERS
Test headers to see if the encoding is UTF-8.
[method] APPLY-DATA-FRAME AROUND (STREAM
GZIP-DECODING-MIXIN) PAYLOAD START ENDWhen there is a
Content-Encoding: gzipheader, decompress the data and call nextAPPLY-DATA-FRAMEmethods.
[class] TEXT-COLLECTING-STREAM
Mixin that collect all the received body (possibly unzipped data frames converted to proper encoding) into its
TEXTslot.
Data to write are send by WRITE-DATA-FRAME-MULTI or WRITE-DATA-FRAME. These do
not take into account limits set up by the peer, so use WRITE-BINARY-PAYLOAD instead.
Send and received octets are accounted for and must be within some limits (window).
User in most cases probably prefers not to care about windows and data frames, and binary vs text and content encoding and compression of the data.
This poses several topics:
Overlay a Common Lisp stream over data frames. This is done using gray streams, see XXX.
Handle window management. The problem is that we do not want to block an individual stream handler. So the options I can see are:
We can actually ignore the issue, if the size of the output is small, it would work. You can
WRITE-DATA-FRAMEif you need to send binary data up to 16384 octets (or more if negotiated with the per)We can signal error when the output is too big, and let the application code handle that. The application would need a callback to resume operations when the window is open again. Problem - it complicates the application.
We can buffer all the input and send it when there is an opportunity automatically. Problem - potentially conses a lot.
Starting from the low-level, options are:
4.4 Server API reference
[in package HTTP2/SERVER with nicknames HTTP2/SERVER/SHARED, HTTP2/SERVER/POLL, HTTP2/SERVER/THREADED]
4.4.1 Server classes
The server behaviour is defined by used dispatcher (server class). The class is
specified as a parameter to the START function.
The dispatcher should be a subclass of the BASE-DISPATCHER and can use predefined mixins to specify:
How to handle multiple connections (
THREADED-DISPATCHER,POLL-DISPATCHER-MIXIN,SINGLE-CLIENT-DISPATCHER)For threaded dispatcher to add
TLSwrapping (TLS-DISPATCHER-MIXIN)To run the server in separate thread (
DETACHED-SERVER-MIXIN).
Some predefined combinations are below.
[class] DETACHED-POLL-DISPATCHER DETACHED-SERVER-MIXIN POLL-DISPATCHER
Detached version of the
POLL-DISPATCHER.
[variable] *VANILLA-SERVER-DISPATCHER* NIL
Default value of the server dispatcher. One of
DETACHED-TLS-THREADED-DISPATCHERotPOLL-DISPATCHER
Server mixins and component classes
[class] THREADED-DISPATCHER BASE-DISPATCHER
Specialize
DO-NEW-CONNECTIONto process new connections each in a separate thread.
[class] POLL-DISPATCHER-MIXIN FDSET-MANAGER
Uses poll to listen to a set of clients and handle arriving packets in a single thread.
Maximum number of clients is fixed (by default fdset-size, by default 10). Additional clients wait until one of existing client leaves.
Timeouts can be specified for polling.
[class] SINGLE-CLIENT-DISPATCHER BASE-DISPATCHER
Handle the connection while doing nothing else.
Serve just one client at time: when it connects, read the incoming requests and handle them as they arrive. When the client sends go-away frame, close the connection and be ready to serve another client.
Obviously, there is little overhead and this version is actually pretty fast - for one client and in ideal conditions (especially with request pilelining).
[class] TLS-DISPATCHER-MIXIN EASY-CERTIFICATED-CONTEXT-MIXIN H2-SERVER-CONTEXT-MIXIN BASE-DISPATCHER
Specializes
SERVER-SOCKET-STREAMto addTLSlayer to the created sockets, andSTART-SERVER-ON-SOCKETto use a context created by MAKE-HTTP2-TLS-CONTEXT.
4.5 Polling server overview
[in package HTTP2/SERVER with nicknames HTTP2/SERVER/SHARED, HTTP2/SERVER/POLL, HTTP2/SERVER/THREADED]
4.5.1 Interface to the polling server
POLL-DISPATCHER-MIXIN tracks a pool of clients that are connected to a
socket. Each client has a user registered callback function and required number
of octets to have available to call the callback. Callbacks call
SEND-UNENCRYPTED-BYTES to encrypt and send more data.
The central function is SERVE-TLS that orchestrates reading, decrypting,
encrypting and writing of data for all the sockets.
- [generic-function] GET-CLIENTS OBJECT
- [structure-accessor] CLIENT-SSL TLS-ENDPOINT
[class] POLL-DISPATCHER-MIXIN FDSET-MANAGER
Uses poll to listen to a set of clients and handle arriving packets in a single thread.
Maximum number of clients is fixed (by default fdset-size, by default 10). Additional clients wait until one of existing client leaves.
Timeouts can be specified for polling.
- [function] PROCESS-CLIENT-SOCKETS NREAD DISPATCHER
- [function] ADD-SOCKET-TO-FDSET FDSET SOCKET CLIENT FD-IDX
[function] SEND-UNENCRYPTED-BYTES CLIENT NEW-DATA COMMENT
Add new data to be encrypted and sent to client.
Data are buffered in the
ENCRYPT-BUFof the client and when there is enough of them they are encrypted.NEW-DATAare completely used up (can be dynamic-extent).
- [function] ENCRYPT-AND-SEND CLIENT
[function] SET-NEXT-ACTION CLIENT ACTION SIZE
Set action for next chunk of received data.
[accessor] GET-POLL-TIMEOUT POLL-DISPATCHER-MIXIN (:POLL-TIMEOUT)
See
*POLL-TIMEOUT*.
[accessor] GET-NO-CLIENT-POLL-TIMEOUT POLL-DISPATCHER-MIXIN (:NO-CLIENT-POLL-TIMEOUT)
[variable] *NO-CLIENT-POLL-TIMEOUT* :∞
Default timeout in seconds to use when there is no client (to limit time to a client to connect).
This is supposed to be used primarily in the automated tests, where you do not want indefinite waits in case of a problem.
On timeout signals
POLL-TIMEOUTerror.Default means an indefinite wait.
-
Default timeout in seconds to use for poll when there are connected clients, but no client communication.
On timeout signals
POLL-TIMEOUTerror.Default means an indefinite wait.
[condition] POLL-TIMEOUT COMMUNICATION-ERROR
Poll was called with a timeout and returned before any file descriptor became ready.
[function] COMPUTE-POLL-TIMEOUT-VALUE HUMAN-FORM
Compute poll timeout from human readable value (seconds or :∞) to value accepted by poll (miliseconds or -1)
4.5.2 Client actions loop (implementation)
Each TLS-ENDPOINT has a STATE that encapsulates what actions are effectively
possible.
SELECT-NEXT-ACTION selects appropriate action. When no new action is available,
next client is handled and eventually POLL called when all clients were served.
The actions are in general indicated by arrows in the diagram:
TLS endpoint
[structure] TLS-ENDPOINT TLS-ENDPOINT-CORE
Data of one
TLSendpoint that is connected to a socket that is part of aFDSET. This includes:File descriptor of underlying socket (FD).
Opaque pointer to the openssl handle (SSL). See
SSL-READandENCRYPT-SOME.Input and output BIO for exchanging data with OPENSSL (WBIO, RBIO).
Buffer of plain text octets to encrypt and send (
ENCRYPT-BUF). This buffer reducesTLSrecords overhead.Buffer of encrypted octets to send to the file descriptor (
WRITE-BUF). This buffer reduces network headers overhead and removes delays due to Nagle algorithm.Callback function when read data are available (
IO-ON-READ) and Number of octets required by it (OCTETS-NEEDED). Negative values have special handling. SeeRUN-USER-CALLBACKfor details.Client state from the low-level data flow point of view (
STATE).Application data (slot to be used by the application). This is usually some application structure, but can be also a symbol in tests. Another input to
RUN-USER-CALLBACK.
[function] MAKE-TLS-ENDPOINT SOCKET CTX APPLICATION-DATA IDX
Make a generic instance of a
TLS-ENDPOINTusable both for a client and for a server.The read and write buffers are intitialized to new
[macro] WITH-TLS-ENDPOINT (NAME CONTEXT &KEY (FD -1) APPLICATION-DATA (FDSET-IDX -1)) &BODY BODY
Run
BODYwithNAMElexically bound to INIT, and it should be aTLSendpoint.When control leaves the body, either normally or abnormally, The SSL of the endpoint is freed.
The defaults reflect the fact that the macro should not be used with
TLSendpoints used inside aFDSET.
[function] DO-AVAILABLE-ACTIONS CLIENT
Run available actions as selected by
SELECT-NEXT-ACTIONtill there is none.
Application loop
[function] ON-COMPLETE-SSL-DATA CLIENT
Read number of octets indicated in
CLIENTinto a vector and then apply client fn on it.The code assumes that single client request message is not split across several
TLSframes. If it does, well, connection error.
[function] RUN-USER-CALLBACK CLIENT VEC
Run user defined callback.
The callback is called with two parameters, client application data (opaque) and vector of
OCTETS-NEEDEDoctets. It should return two values, new callback and new expected size.
[function] ENCRYPT-DATA CLIENT
Encrypt data in client's
ENCRYPT-BUF.Do nothing if there is no data to encrypt or SSL not yet initialized (and return zero).
Otherwise, use a temporary vector to write data
[function] ENCRYPT-SOME CLIENT VECTOR FROM TO
Move octets from
VECTORto theOUTPUT-SSL.Return 0 when no data are available. Raise an error on error.
- [function] ENCRYPT-AND-SEND CLIENT
Port to TLS
[function] PROCESS-DATA-ON-SOCKET CLIENT
Read data from client socket ① and pass them to the tls buffer ② to decrypt.
[function] READ-FROM-PEER CLIENT VEC VEC-SIZE
Move up to
VEC-SIZEoctets fromPEER-INto theVEC.Return 0 when no data are available. Raise an error on error.
[function] DECRYPT-SOCKET-OCTETS CLIENT VECTOR FROM TO
Send data in the
VECTORbetweenFROMandTOto the ② openssl for decryption .
[function] WRITE-OCTETS-TO-DECRYPT CLIENT VECTOR FROM TO
Move octets from
VECTORto theOPENSSL-TO-DECRYPT.Return 0 when no data are available. Raise an error on error.
TLS to port
[function] MOVE-ENCRYPTED-BYTES CLIENT
Move data encrypted by OpenSSL to the socket write queue Ⓔ.
This should be called in a way friendly to Nagle algorithm. My understaning is this is either when we pipeline a lot of data, or when we send out somethinf that expects a response.
[function] WRITE-DATA-TO-SOCKET CLIENT
Write buffered encrypted data Ⓔ to the client socket ⑥. Update the write buffer to keep what did not fit.