Per-connection HTTP/2 driver. Manages the TLS handshake, client preface validation, HTTP/2 frame parsing, HPACK decoding, settings negotiation, stream dispatch, and connection-level flow control.
Each accepted TCP connection runs an independent HTTP/2 session with its own HPACK dynamic tables, settings, flow control windows, and stream slots. Isolating this state in ConnRec means the top-level server only tracks a pool of connection pointers, and each connection drives itself through an event loop watcher callback (ConnOnEvent).
CpPreface ──preface OK──▸ CpSettings ──SETTINGS ACK──▸ CpOpen
│
Drain/error ────┤
▼
CpGoaway ──all streams done──▸ CpClosed
| Phase | Value | Description |
|---|---|---|
CpPreface |
0 | Waiting for the 24-byte client connection preface |
CpSettings |
1 | Preface received; waiting for client SETTINGS frame |
CpOpen |
2 | Operational -- processing HEADERS, DATA, and control frames |
CpGoaway |
3 | GOAWAY sent, draining in-flight streams |
CpClosed |
4 | Connection closed, resources pending cleanup |
- Accept → CpPreface:
ConnCreateallocates the connection, starts TLS handshake via m2Stream. - CpPreface → CpSettings: After TLS completes and ALPN confirms
h2, the server sends its own SETTINGS and waits for the client preface (24 magic bytes). - CpSettings → CpOpen: Client sends a SETTINGS frame; server sends SETTINGS ACK. Normal frame processing begins.
- CpOpen → CpGoaway:
ConnDrainsends GOAWAY withNO_ERRORand the last processed stream ID. No new streams are accepted. - CpGoaway → CpClosed: All active streams finish (or drain timeout expires). Connection is closed.
| Constant | Value | Purpose |
|---|---|---|
CpPreface |
0 | Phase: waiting for client preface |
CpSettings |
1 | Phase: waiting for client SETTINGS |
CpOpen |
2 | Phase: operational |
CpGoaway |
3 | Phase: GOAWAY sent, draining |
CpClosed |
4 | Phase: done |
ArenaSize |
32768 | 32 KB per-connection scratch arena |
TYPE ConnRec = RECORD
id: CARDINAL; (* connection ID *)
server: ADDRESS; (* back-pointer to ServerRec *)
fd: INTEGER; (* socket file descriptor *)
phase: CARDINAL; (* CpPreface..CpClosed *)
tlsSess: ADDRESS; (* TLS session handle *)
tlsCtx: ADDRESS; (* per-conn TLS context *)
strm: ADDRESS; (* m2Stream handle *)
localSettings: Settings; (* our SETTINGS *)
remoteSettings: Settings; (* peer's SETTINGS *)
connSendWindow: INTEGER; (* connection-level send window *)
connRecvWindow: INTEGER; (* connection-level receive window *)
lastPeerStream: CARDINAL; (* highest stream ID from peer *)
goawaySent: BOOLEAN;
goawayRecvd: BOOLEAN;
prefaceRecvd: BOOLEAN;
settingsAckRecvd: BOOLEAN;
numActive: CARDINAL; (* active stream count *)
dynEnc: DynTable; (* HPACK encoder table *)
dynDec: DynTable; (* HPACK decoder table *)
streamTable: StreamTransTable; (* shared stream FSM table *)
slots: ARRAY [0..MaxStreamSlots-1] OF StreamSlot;
readBuf: Buf; (* incoming data buffer *)
writeBuf: Buf; (* outgoing data buffer *)
writeOff: CARDINAL; (* flush offset into writeBuf *)
haveHeader: BOOLEAN; (* frame header parsed? *)
curHeader: FrameHeader; (* current frame header *)
arenaBase: ADDRESS; (* arena backing memory *)
arena: Arena; (* per-connection scratch *)
idleTimerId: INTEGER; (* idle timeout timer *)
hsTimerId: INTEGER; (* handshake timeout timer *)
remoteAddr: ARRAY [0..63] OF CHAR;
startTick: INTEGER;
lastActive: INTEGER;
watching: BOOLEAN; (* registered with event loop? *)
END;
ConnPtr = POINTER TO ConnRec;
dynEnc/dynDec-- HPACK dynamic tables (per-connection, not per-stream)streamTable-- shared FSM transition table for all streams on this connectionslots-- fixed-size array of stream slots;AllocSlot/FindSlotmanage themreadBuf/writeBuf-- I/O buffers; raw TLS bytes flow through m2Streamarena-- bump allocator for per-request scratch (reset between requests)idleTimerId/hsTimerId-- event loop timers for timeout enforcement
When ConnOnEvent fires (I/O readiness from the event loop):
- Read bytes from m2Stream into
readBuf - If
phase = CpPreface: validate 24-byte client preface, advance toCpSettings - Parse 9-byte frame headers from
readBuf - Dispatch by frame type:
- SETTINGS: apply peer settings, send ACK, advance phase if needed
- PING: echo with ACK flag
- GOAWAY: record, begin closing
- WINDOW_UPDATE: adjust connection or stream send window, flush pending DATA
- RST_STREAM: reset the target stream slot
- HEADERS: HPACK decode, allocate stream slot, assemble pseudo-headers
- CONTINUATION: append to in-progress HEADERS block
- DATA: accumulate into request body
- On
END_STREAM: dispatch assembled request through middleware chain and router - Encode response as HEADERS + DATA frames into
writeBuf - Flush
writeBufthrough m2Stream
PROCEDURE ConnCreate(serverPtr: ADDRESS; connId: CARDINAL;
clientFd: INTEGER; peer: SockAddr;
VAR cp: ConnPtr): BOOLEAN;
Allocate and initialise a connection from an accepted socket. Initiates the TLS handshake asynchronously via m2Stream.
serverPtr-- back-pointer to theServerRec(for accessing router, middleware, metrics, logger)connId-- unique connection IDclientFd-- accepted socket file descriptorpeer-- remote addresscp-- on success, receives a pointer to the allocatedConnRec
Returns TRUE on success, FALSE on allocation failure.
PROCEDURE ConnOnEvent(fd, events: INTEGER; user: ADDRESS);
Event loop watcher callback. Called when the connection's file descriptor has I/O readiness. user is the ConnPtr cast to ADDRESS.
Drives the frame processing pipeline described above. Not called directly by application code.
PROCEDURE ConnDrain(cp: ConnPtr);
Initiate graceful shutdown for this connection. Sends a GOAWAY frame with NO_ERROR and the last processed stream ID. Moves phase to CpGoaway. Active streams continue to completion; no new streams are accepted.
PROCEDURE ConnClose(cp: ConnPtr);
Close the connection and free all resources: TLS session, m2Stream handle, stream slots, arena, I/O buffers, and event loop watcher. Moves phase to CpClosed.
PROCEDURE ConnFlush(cp: ConnPtr);
Flush pending data in writeBuf through m2Stream. Called after encoding response frames or when WINDOW_UPDATE restores flow control budget.
PROCEDURE ConnFeedBytes(cp: ConnPtr; data: ADDRESS; len: CARDINAL);
Append raw bytes directly to the connection's readBuf, bypassing m2Stream and TLS. Used for deterministic testing with ConnCreateTest.
PROCEDURE ConnCreateTest(serverPtr: ADDRESS; connId: CARDINAL;
VAR cp: ConnPtr): BOOLEAN;
Create an in-memory test connection with no TLS or m2Stream. Use with ConnFeedBytes and Http2ServerTestUtil frame builders for unit testing the H2 protocol logic.