Open-Source Wikis

/

curl

/

Features

/

HTTP/2 and HTTP/3

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.c is unaware of the version once the cfilter is in place — it writes request bytes via Curl_conn_send and reads via Curl_conn_recv.

Entry points for modification

  • HTTP/2 framing fix → lib/http2.c (search for the relevant nghttp2_* callback)
  • HTTP/3 backend tweak → lib/vquic/curl_ngtcp2.c or lib/vquic/curl_quiche.c
  • New ALPN protocol → lib/vtls/openssl.c etc., plus the corresponding cfilter
  • Server-push policy → Curl_http2_pushed_stream family in lib/http2.c

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

HTTP/2 and HTTP/3 – curl wiki | Factory