nginx/nginx
HTTP/2
Active contributors: Sergey Kandaurov, Maxim Dounin, Roman Arutyunyan
Purpose
HTTP/2 (RFC 7540 / 9113) on top of nginx's HTTP request engine. Implements the binary framing layer, stream multiplexing, flow control, and HPACK header compression. Stream contents are converted into the same ngx_http_request_t objects that HTTP/1.1 produces, so the rest of the request engine doesn't need to know the protocol version.
The implementation landed in mainline on 2015-09-11 (commit message: "The HTTP/2 implementation (RFC 7240, 7241)") and replaced an earlier never-merged SPDY effort.
Directory layout
src/http/v2/
├── ngx_http_v2.{c,h} # frame parser + connection state machine
├── ngx_http_v2_filter_module.c # output filter that produces frames
├── ngx_http_v2_module.{c,h} # the directives (http2_*, etc.)
├── ngx_http_v2_table.c # the dynamic HPACK table (RFC 7541)
└── ngx_http_v2_encode.c # HPACK encode helpersngx_http_v2.c is the largest single file in src/http/v2/ at ~4,700 lines.
Key abstractions
| Type | Role |
|---|---|
ngx_http_v2_connection_t |
Per-connection state: streams rbtree, settings, flow control window |
ngx_http_v2_stream_t |
Per-stream state: window, queues, link to ngx_http_request_t |
ngx_http_v2_frame_t |
A frame on the output queue |
ngx_http_v2_state_t (function ptr) |
Current parser state — frame header, payload, settings, headers |
ngx_http_v2_send_frame() |
Append a frame to the output queue and arm the write |
ngx_http_v2_huff_decode_state_t |
HPACK static-Huffman decoder state |
Connection state machine
stateDiagram-v2
[*] --> Preface
Preface --> ReadFrameHeader: 24-byte preface received
ReadFrameHeader --> ReadFramePayload
ReadFramePayload --> Dispatch
Dispatch --> ReadFrameHeader
Dispatch --> Settings
Dispatch --> Headers
Dispatch --> Continuation
Dispatch --> Data
Dispatch --> Ping
Dispatch --> WindowUpdate
Dispatch --> RstStream
Dispatch --> Goaway
Dispatch --> Priority
Dispatch --> PushPromise
Goaway --> Drain
Drain --> [*]The parser is implemented as a state-function pointer (h2c->state.handler) rather than a switch. Each function reads what it can from c->buffer and returns:
- the next state function (advance)
- the same state function (stay; need more bytes)
This avoids re-parsing on partial data — recv() boundaries don't matter.
Frame types handled
| Frame | Action |
|---|---|
DATA |
Append to the stream's request body |
HEADERS |
Decode HPACK, build the request, dispatch to phase engine |
PRIORITY |
Update stream priority (limited use; nginx doesn't reorder) |
RST_STREAM |
Cancel the stream; finalize the request with 499/error |
SETTINGS |
Update connection settings (table size, max_concurrent, etc) |
PUSH_PROMISE |
Accepted only outbound; rejected from clients per spec |
PING |
Echo back |
GOAWAY |
Begin shutdown; reject new streams; let existing finish |
WINDOW_UPDATE |
Update flow-control windows |
CONTINUATION |
Continue a HEADERS / PUSH_PROMISE block |
HPACK
src/http/v2/ngx_http_v2_table.c implements RFC 7541's dynamic table. The static Huffman tables for the Huffman-coded header values are in src/http/ngx_http_huff_decode.c (~4,400 lines, mostly auto-generated lookup tables).
The decoder is byte-oriented and table-driven; each input byte indexes into a lookup that gives next state and emitted symbols. No inflate/deflate, no zlib involvement.
Stream → request bridge
When HEADERS (and any CONTINUATIONs) complete, the parser:
- Decodes the HPACK block, accumulating cookies, building the parsed header list.
- Allocates an
ngx_http_request_trooted in a new request pool. - Fills
r->method,r->method_name,r->uri,r->args,r->http_version = NGX_HTTP_VERSION_20, etc. - Calls
ngx_http_v2_run_request()which posts the request to the engine.
From here it's the same request engine as HTTP/1: 11 phases, content handler, output filters. The only differences:
- Body bytes come from
DATAframes, not from the socket directly. - Output goes through
ngx_http_v2_filter_module(replaces chunked + write). - Concurrency: many requests run on one connection.
Output
ngx_http_v2_filter_module sits in the body filter chain and produces DATA frames. Each filter pass either:
- Builds and queues frames for the response (header block, body chunks)
- Returns
NGX_AGAINif flow-control windows are exhausted
Frames are sent in priority order from a per-connection queue, respecting both the connection-level window and each stream's window. A separate write event handler (ngx_http_v2_send_output_queue) drains the queue.
Flow control
Two-tier:
- Connection-level window (
h2c->send_window) — global cap on bytes in flight to the peer - Stream-level window (
stream->send_window) — per-stream cap
The framework decrements both on each DATA send and increments when a WINDOW_UPDATE arrives. If a stream has bytes ready but no window, its frame stays queued.
The receive side mirrors this: nginx sends WINDOW_UPDATE based on http2_recv_buffer_size-derived thresholds.
Settings
Each side announces:
SETTINGS_HEADER_TABLE_SIZESETTINGS_MAX_CONCURRENT_STREAMSSETTINGS_INITIAL_WINDOW_SIZESETTINGS_MAX_FRAME_SIZESETTINGS_MAX_HEADER_LIST_SIZESETTINGS_ENABLE_PUSH(always 0 in modern nginx — push has been removed by browsers and is a no-op here)
Server-side defaults are set by ngx_http_v2_module directives (http2_max_concurrent_streams, http2_recv_buffer_size, etc.).
ALPN
The TLS handshake negotiates HTTP/2 via ALPN (h2). When a TLS connection negotiates h2, ngx_http_ssl_module calls ngx_http_v2_init instead of the HTTP/1 init path. Plaintext H2 (h2c) is supported via the listen ... http2; directive but rare in practice.
Integration points
- Event loop / OpenSSL — H2 reads from
c->ssllike any other connection; the framing parser is just an event handler. - HTTP request engine — bridge in
ngx_http_v2_run_request. Phases, filters, finalization are unchanged. - HPACK — connects to the existing static Huffman tables in
src/http/ngx_http_huff_*. - Variables —
$http2,$http_*(request headers via H2 trailers handled),$ssl_*for the underlying TLS.
Entry points for modification
H2 spec compliance fixes typically land in the per-frame state functions. Performance work in the recent past has focused on the encoder's frame coalescing (reducing per-stream bookkeeping). Don't change the HPACK Huffman tables — they're spec-mandated.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.