End-to-End Scenarios
Here's what happens when you send a request with different HTTP versions. The details differ, but the pipeline stages are the same — enrichment, tracing, cookies, cache, encoding, network, decoding, decompression, cookie storage, cache storage, retries, redirects.
HTTP/1.0 — Simple Request/Response
Request Path
- The application calls
SendAsynconITurboHttpClientwith anHttpRequestMessagetargeting HTTP/1.0. RequestEnricherapplies the base address and any default headers.TracingBidiStagestarts an activity span for observability.RedirectBidiStageandCookieBidiStageinject matching cookies fromCookieJar.CacheBidiStagechecks the cache — on a miss, the request continues.ContentEncodingBidiStagecompresses the request body if a compression policy is configured.Engineroutes the request toHttp10Engine.Http10ConnectionStageserialises the request to bytes withConnection: close.TcpConnectionStage(from Servus.Akka) requests a connection lease fromTcpConnectionManagerActorand sends the bytes over TCP.
Response Path
- The server's response bytes arrive via TCP and flow through
TcpConnectionStageintoHttp10ConnectionStage. Http10ConnectionStageparses the HTTP/1.0 response (body length determined byContent-Lengthor EOF) and correlates it to the pending request.ContentEncodingBidiStagedecompresses the body if needed.CacheBidiStagecaches the response if it is cacheable.RetryBidiStagepasses the response through (no retry needed for a successful response).CookieBidiStagestores anySet-Cookieheaders.RedirectBidiStagepasses the response through (no redirect needed for a200).TracingBidiStagecloses the activity span, recording the final status code.- The final
HttpResponseMessageis delivered to the application.
Key Characteristic
After step 18, the TCP connection is closed. The next HTTP/1.0 request will go through the full connection setup again. There is no keep-alive feedback loop.
HTTP/1.1 — Persistent Connection with Keep-Alive
HTTP/1.1 follows the same request/response path as HTTP/1.0 except for one critical difference: the connection can be reused after the response is delivered.
Keep-Alive Handling
After Http11ConnectionStage decodes the response and correlates it to the pending request, it evaluates the Connection header internally:
Connection: keep-alive(or HTTP/1.1 default) → the connection lease is returned toTcpConnectionManagerActorfor reuseConnection: close→ the lease is released without returning it to the idle queue; the next request triggers a new connection
On reuse, the next request to the same host can skip connection setup entirely.
Pipelining
Http11ConnectionStage uses a FIFO queue internally to correlate requests with responses, enabling HTTP/1.1 pipelining: multiple requests can be in-flight on the same connection simultaneously, and responses are matched to requests in order.
HTTP/2 — Multiplexed Streams
HTTP/2 is fundamentally different from HTTP/1.x. A single TCP connection carries many concurrent logical streams, each identified by an odd integer stream ID assigned by the client.
Request Framing
Http20ConnectionStageassigns the next available stream ID (1, 3, 5, …), HPACK-encodes the request headers into aHEADERSframe, and serialises the body (if any) intoDATAframe(s).Http20ConnectionStageapplies connection-level and stream-level flow control — it will withhold frames if the server's receive window is exhausted.TcpConnectionStage(from Servus.Akka) sends the frames over TCP (injecting the HTTP/2 connection preface on the first connection).
Connection-Level Frames
While request/response streams are active, Http20ConnectionStage also handles:
SETTINGS— initial and updated connection parameters; acknowledges serverSETTINGSwithSETTINGS ACKPING— round-trip latency measurement; responds to serverPINGwithPING ACKWINDOW_UPDATE— flow control credits; emitted automatically as the consumer reads response dataGOAWAY— graceful shutdown; after receivingGOAWAY, no new streams are opened on this connection
Response Assembly
- Raw bytes from TCP flow through
TcpConnectionStageintoHttp20ConnectionStage. Http20ConnectionStageparses the bytes into HTTP/2 frames (handling partial frames across TCP boundaries), routes connection-level frames to internal handlers, assembles per-streamHEADERS+DATAframes into anHttpResponseMessage, HPACK-decodes response headers, and correlates each assembled response back to its pending request using the stream ID.- The response continues through
ContentEncodingBidiStage,CacheBidiStage,RetryBidiStage,CookieBidiStage, andRedirectBidiStage— the same response chain as HTTP/1.x.
Stream ID Exhaustion
Client-side stream IDs are 31-bit odd integers. When the maximum (2^31 - 1) is reached, the connection sends GOAWAY and a new connection is established. This is handled transparently by the connection manager actor.
HTTP/3 — Multiplexed over QUIC
HTTP/3 replaces TCP with QUIC, a UDP-based transport that provides built-in encryption and independent stream delivery. Each request uses its own QUIC stream, so a lost packet on one stream does not block other in-flight requests.
Request Framing
Http30ConnectionStageQPACK-encodes the request headers into aHEADERSframe, and the body (if any) intoDATAframe(s).Http30ConnectionStagemanages connection-level concerns —SETTINGS,GOAWAY, and stream lifecycle.QuicConnectionStage(from Servus.Akka) requests a QUIC connection fromQuicConnectionManagerActorand sends the bytes over the network.
Connection-Level Frames
While request/response streams are active, Http30ConnectionStage handles:
SETTINGS— connection parameters exchanged at startupGOAWAY— graceful shutdown; after receivingGOAWAY, no new streams are opened on this connection
Response Assembly
- Raw bytes from QUIC flow through
QuicConnectionStageintoHttp30ConnectionStage. Http30ConnectionStageparses the bytes into HTTP/3 frames, routes connection-level frames to internal handlers, assembles per-streamHEADERS+DATAframes into anHttpResponseMessage, and QPACK-decodes response headers.- The response continues through
ContentEncodingBidiStage,CacheBidiStage,RetryBidiStage,CookieBidiStage, andRedirectBidiStage— the same response chain as HTTP/1.x and HTTP/2.
Key Differences from HTTP/2
| HTTP/2 | HTTP/3 | |
|---|---|---|
| Transport | TCP + TLS | QUIC (UDP + built-in TLS) |
| Head-of-line blocking | Yes — one lost TCP packet stalls all streams | No — each QUIC stream is independent |
| Header compression | HPACK | QPACK (adapted for out-of-order delivery) |
| Connection preface | Required (PRI * HTTP/2.0...) | Not needed — QUIC handles this |