§5.3
Chapter 05 · HTTP3 Frame Processing and Request Response Semantics

§5.3Request and Response Stream Behavior with Concurrency

RFC 9114

Learning objective. Follow an HTTP/3 request and response across their bidirectional stream, including interim responses, and see how many such exchanges run concurrently and interleave over one connection.

One exchange, one bidirectional stream #

A request/response exchange occupies a single client-initiated bidirectional stream (§1.3). The two halves of that stream carry the two directions (§4.1):

  1. The client sends its request as a HEADERS frame, then zero or more DATA frames for a body, then optionally trailing HEADERS, and marks the send side done with the QUIC FIN bit. A client sends exactly one request per stream ([RFC 9114 §4.1]).
  2. The server replies on the same stream: zero or more interim responses (1xx, such as 103 Early Hints), then exactly one final response (its HEADERS, DATA, and optional trailers), ending with FIN.

Interim responses are the reason the exchange is more than request-then-response: a server can send 103 Early Hints to let the client start fetching sub-resources before the final response is ready, all on the same stream before the 200 arrives.

ServerClienttwo requests in flight on independentstreamsresponses interleave by priority, eachisolated to its streamstream 0: HEADERS (GET /a) +FINstream 4: HEADERS (GET /b) +FINstream 0: HEADERS (103 EarlyHints) — interim responsestream 4: HEADERS (200), DATA(/b body), FINstream 0: HEADERS (200), DATA(/a body), FIN
Fig. 5.3-1Two concurrent requests. Each occupies its own stream; the server sends an interim response on one, then interleaves the two final responses — ordered by priority and isolated per stream.RFC 9114 §4.1

Concurrency is just more streams #

There is no separate multiplexing layer to configure: concurrency is opening more streams. A browser loading a page opens a bidirectional stream per request — 0, 4, 8, … — and they proceed in parallel. Three chapter-4 mechanisms govern how that parallelism behaves:

  • Isolation. A loss on one request's stream does not stall the others (§4.4).
  • Ordering. When the streams compete for the connection's send capacity, the server interleaves their response data by priority (§4.3), which is what lets a small critical response finish ahead of a large image already in flight.
  • Limits. The number of concurrent requests is capped by the peer's stream limit (§4.1) and their total in-flight data by connection flow control (§4.2).

Because the request half closes (FIN) as soon as the request is sent while the response half keeps flowing, a stream spends most of its life half-closed, the client done sending, the server still responding. That is the normal steady state, not an error.

Server push, briefly #

HTTP/3 keeps server push: the server sends a PUSH_PROMISE on the request stream to promise a response the client did not ask for, then delivers it on a server-initiated push stream, bounded by the client's MAX_PUSH_ID ([RFC 9114 §4.6]). In practice push is little used: the savings rarely justify the complexity and wasted bandwidth of pushing resources a client may already have, and 103 Early Hints has become the preferred way to accelerate sub-resource loading. Push is worth recognizing in a trace; it is rarely worth enabling.

Note

"Concurrent" does not mean "simultaneous on the wire." At any instant the connection is sending bytes for whichever stream the scheduler picked (§4.3); concurrency means many streams are open and making progress over time, interleaved through the one send path. The illusion of parallelism comes from fine-grained interleaving, not from parallel transmission.

In practice

editorial The most common concurrency surprise is a page that opens far more requests than the server's initial stream limit, then stalls on STREAMS_BLOCKED (§4.1) partway through loading: visible as a burst of requests, a pause, then another burst as the limit is raised. Provision initial_max_streams_bidi for the fan-out you expect. And prefer 103 Early Hints over server push for getting critical sub-resources to the client early; it achieves the latency win without guessing at the client's cache.

Takeaways #

An HTTP/3 exchange is one bidirectional stream carrying a request and, after any interim responses, a single final response; concurrency is simply many such streams, isolated by the transport, interleaved by priority, and bounded by stream and flow-control limits. When any of that goes wrong, the failure surfaces as a stream reset or a connection error, the subject of §5.4.