HTTP/2 Protocol Guide¶
HTTP/2 is the default secure multiplexed path over TLS ALPN h2. Same-origin pools reuse one connection via stream leases. Server push is unsupported; h2c, background PING, and per-request priority are explicit capabilities.
Public entry points: HTTP sync / Session config. WebSocket tunnels: WebSocket.
Summary¶
| Topic | Behavior |
|---|---|
| Default path | TLS ALPN h2 (PreferHttp2 defaults true) |
| h2c | Opt-in: SendOptions.Http2CleartextMode = PriorKnowledge or Upgrade |
| Multiplexing | One same-origin H2 connection carries multiple active requests; limited by local and peer MAX_CONCURRENT_STREAMS |
| Server push | Unsupported; any PUSH_PROMISE is a protocol error |
| Extended CONNECT | RFC 8441; default for wss (see WebSocket) |
| GOAWAY / RST | Untreated streams may map to STATUS_RETRY; safe methods get one fresh retry |
| Background PING | Session Http2KeepAlive.Enabled defaults off |
| Priority | Explicit SendOptions.Http2Priority; no local dependency-tree scheduler |
Connection and SETTINGS¶
- Client sends the preface and SETTINGS with
ENABLE_PUSH=0; ACKs immediately without blocking on the peer ACK. - Peer
ENABLE_PUSH != 0, illegal window/frame sizes, or illegalENABLE_CONNECT_PROTOCOL→ protocol/flow-control error. - Missing ACK plus a later read timeout → GOAWAY
SETTINGS_TIMEOUT. - CONTINUATION flood guards: ≤64 frames, ≤4 empty frames (CVE-2024-27316 class).
- Accumulated header block over capacity →
ENHANCE_YOUR_CALM/STATUS_BUFFER_TOO_SMALL. - Connection-specific headers (
connection/transfer-encoding/upgrade, …) are forbidden;temay only betrailers.
Streams and body¶
- The pool grants stream leases on same-origin H2 connections; the frame loop demuxes HEADERS/DATA/WINDOW_UPDATE/RST by stream id.
- Request bodies are driven by a body source and sliced into DATA under
min(connection send window, stream remote window, peer MaxFrameSize). - Request trailers: after the body, trailing HEADERS + END_STREAM (an HTTP/2 header block, separate from HTTP/1.1 chunked trailers); trailer pseudo-headers rejected; forbidden fields align with HTTP/1.1.
- Flow control: connection + per-stream;
InitialWindowSizeupdates apply to active streams; overflow →FLOW_CONTROL_ERROR. - 1xx interim: reject
:status 101and interim+END_STREAM; other interim responses reset and continue to the final response.
RFC 8441 extended CONNECT¶
- Shape:
:method: CONNECT+:protocol: websocket. - Requires peer
SETTINGS_ENABLE_CONNECT_PROTOCOL=1; otherwiseSTATUS_NOT_SUPPORTEDand no stream is allocated. - After a
2xxresponse, bidirectional DATA carries tunnel payload. - This is the HTTP/2 tunnel primitive;
wknet::websocketselects it by default forwss, or forces HTTP/1.1 Upgrade withHttp11Only.
GOAWAY / RST and retry¶
| Condition | High-level semantics |
|---|---|
| Clean GOAWAY and active stream id > lastStreamId | Stream untreated → STATUS_RETRY |
REFUSED_STREAM and provably untreated |
STATUS_RETRY |
| Non-zero GOAWAY error / other RST | Connection-disconnected failure |
The high-level layer performs one fresh retry only for safe methods (GET/HEAD/OPTIONS); non-idempotent methods with a body are not auto-replayed. Connection-level errors fan out to active streams.
h2c (off by default)¶
| Mode | Notes |
|---|---|
PriorKnowledge |
Cleartext HTTP/2 preface + SETTINGS |
Upgrade |
HTTP/1.1 Upgrade; no request body; reserves stream 1 and replays post-101 residual bytes |
| Default | http:// does not auto-select h2c |
Requests with an explicit non-HTTP ALPN, or with HTTP/2 priority set, do not select HTTP/3 (see HTTP/3 & QUIC).
Priority and PING¶
- Priority:
SendOptions.Http2Priorityattaches weight/dependency/exclusive on the first HEADERS (weight 1..256); self-dependency rejected. Complex local priority-tree scheduling is not implemented. - Background PING: enable with
SessionConfig.Http2KeepAlive.Enabled=true; only idle reusable pooled H2 connections are scanned; ACK timeout or protocol error closes that idle connection. Defaults: idle/interval 30s, ACK timeout 5s.
HPACK (behavioral bounds)¶
- Dynamic-table size updates only at the start of a block and ≤ negotiated size; header-list size is
name+value+32. - Encoder forces Never-Indexed for
authorization/cookie/proxy-authorization. - Huffman rejects overlong codes / EOS / illegal padding.
Default-off features¶
| Capability | How to enable |
|---|---|
| h2c | SendOptions.Http2CleartextMode |
| Background PING | Http2KeepAlive.Enabled=true |
| Per-request priority | SendOptions.Http2Priority |
See the capability matrix for support scope.