curl/curl
HTTP/2 and HTTP/3
Active contributors: Stefan Eissing, Daniel Stenberg
Purpose
curl speaks HTTP/2 over TLS via the nghttp2 library and HTTP/3 over QUIC via either ngtcp2 (with multiple TLS bindings) or quiche. Both are exposed as connection filters, sit on top of TLS or UDP, and present a stream-multiplexed byte interface to the protocol handler in lib/http.c.
Source layout
| Subsystem | Source | Description |
|---|---|---|
| HTTP/2 filter | lib/http2.c (~97 KB) |
The whole HTTP/2 implementation: framing, multiplexing, flow control |
| HTTP/2 over proxy CONNECT | lib/cf-h2-proxy.c (~45 KB) |
HTTP/2 streams used as proxy tunnels |
| HTTP/3 + QUIC abstraction | lib/vquic/vquic.c, lib/vquic/vquic_int.h |
The vquic vtable |
| ngtcp2 backend | lib/vquic/curl_ngtcp2.c (~96 KB) |
QUIC + HTTP/3 with ngtcp2/nghttp3 |
| quiche backend | lib/vquic/curl_quiche.c (~51 KB) |
Cloudflare's quiche |
| HTTP/3 helpers | lib/http3.md (docs) |
High-level overview |
| Frame buffers | lib/dynhds.c, lib/bufq.c |
Header sets and bounded byte queues |
| HTTP/1 mostly-shared | lib/http1.c |
First-line/headers parser shared with HTTP/2 path |
Key abstractions
| Symbol | File | Description |
|---|---|---|
cf_h2_setup_default() |
lib/http2.c |
Pushes the HTTP/2 cfilter onto a connection |
Curl_http2_switch() |
lib/http2.c |
Upgrades an HTTP/1 connection to HTTP/2 (h2c) |
Curl_http2_request_upgrade() |
lib/http2.c |
Sends an h2c upgrade request |
nghttp2_* |
(external) | The library curl drives |
cf_quiche_setup / cf_ngtcp2_setup |
lib/vquic/curl_*.c |
Push the appropriate HTTP/3 cfilter |
Curl_dynhds_* |
lib/dynhds.c |
Header set used in both HTTP/2 and HTTP/3 paths |
Curl_bufq_* |
lib/bufq.c |
Bounded byte queue for incoming/outgoing frames |
struct Curl_http_conn |
lib/http.h |
Connection-level HTTP state |
ALPN selection
For HTTPS, the desired protocol is offered as ALPN during the TLS handshake (in lib/vtls/openssl.c and friends). The ordered list comes from the easy-handle config: CURL_HTTP_VERSION_3 first, then 2, then 1.1, narrowed by CURLOPT_HTTP_VERSION / --http2/--http3.
graph TD
A[connect] --> B{HTTP/3 requested?}
B -- yes --> C[push vquic cfilter]
C --> D[QUIC handshake]
D -- ALPN h3 --> E[HTTP/3 ready]
B -- no --> F[push TLS cfilter]
F --> G[ALPN negotiation]
G -- h2 --> H[push http2 cfilter]
G -- http/1.1 --> I[plain HTTP/1.1]
H --> J[multiplexed streams]For plaintext HTTP/2 (h2c), the upgrade is requested via the Upgrade: h2c header; if the server agrees, Curl_http2_switch swaps the cfilter chain in place.
Multiplexing
HTTP/2 multiplexes multiple requests over one TCP connection. curl reflects this in the connection cache: when a transfer needs HTTP/2 and one already exists for the same destination, the cache returns the existing connection and lib/http2.c allocates a new stream on it. The cap is CURLOPT_MAX_TOTAL_CONNECTIONS × CURLOPT_MAXCONNECTS × per-connection stream limit (set by the server's SETTINGS_MAX_CONCURRENT_STREAMS).
The same idea extends to HTTP/3: streams are multiplexed over a single QUIC connection.
Flow control and pacing
The HTTP/2 filter manages window-update bookkeeping in lib/http2.c. Flow control is bidirectional: outbound for body uploads (libcurl pauses when the peer window closes), inbound for response bodies (libcurl announces window-update only when the application drains its receive buffer). Pause and resume tie into curl_easy_pause and the cfilter data_pause op.
For HTTP/3, ngtcp2/quiche manages QUIC-level flow control; libcurl's job is to feed and drain bytes via Curl_bufq_*.
Server push
HTTP/2 server push is parsed by lib/http2.c via the nghttp2 callbacks. Curl exposes it via CURLMOPT_PUSHFUNCTION. By default, push streams are rejected unless the application opts in.
HTTP/3 build matrix
ngtcp2 can be paired with one of several TLS implementations (OpenSSL+QUIC, BoringSSL, GnuTLS, wolfSSL); each combination is its own configure flag. quiche bundles its own TLS via BoringSSL. The .github/workflows/http3-linux.yml workflow exercises all supported combinations.
Integration points
- TLS: ALPN result drives whether the HTTP/2 cfilter is pushed at all. See TLS backends.
- Connection filters: HTTP/2 and HTTP/3 are pure cfilters, see Connection filters.
- Connection cache: HTTP/2 connections live in the cache like any other and serve many transfers concurrently. See Connection management.
- HTTP handler:
lib/http.cis unaware of the version once the cfilter is in place — it writes request bytes viaCurl_conn_sendand reads viaCurl_conn_recv.
Entry points for modification
- HTTP/2 framing fix →
lib/http2.c(search for the relevantnghttp2_*callback) - HTTP/3 backend tweak →
lib/vquic/curl_ngtcp2.corlib/vquic/curl_quiche.c - New ALPN protocol →
lib/vtls/openssl.cetc., plus the corresponding cfilter - Server-push policy →
Curl_http2_pushed_streamfamily inlib/http2.c
Related pages
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.