Module upstream_h3

Module upstream_h3 

Source
Expand description

HTTP/3 (QUIC) upstream transport (DW-108).

The mirror of the DW-088 H3 ingress listener: an H3 egress connector that dials upstream endpoints over QUIC and speaks HTTP/3 on bidirectional QUIC streams. A route whose upstream is configured protocol: h3 dispatches through here instead of the TCP/TLS pooled client in super::upstream.

§Transport model

QUIC multiplexes many streams over one connection, so the pool shape is inverted relative to HTTP/1.1: a QUIC connection is the pooled resource, and each request opens a fresh bidirectional stream within it. QuicStreamPool keeps a bounded set of QUIC connections per endpoint address, hands out a cheaply cloneable [h3::client::SendRequest] handle per request (one stream per send_request call), and reaps connections idle past QuicStreamPool::idle_timeout.

§TLS

QUIC mandates TLS 1.3, so every H3 upstream negotiates TLS with ALPN h3. The trust roots are the SAME ones the pooled https connector uses (#121): the Mozilla webpki public set by default, or the upstream’s trusted_ca_file bundle when configured. The shared rustls client-config shape lives in crate::security::tls (https_h3_client_config) so the connector and the QUIC active health probe can never disagree about trust.

§0-RTT

Deliberately NOT used for upstream dialing: 0-RTT early data is replayable, and a replayed non-idempotent upstream request is a footgun the gateway must not expose by default. The rustls client config disables early data (https_h3_client_config).

§Response streaming (PERF-10, #243)

The H3 transport supports two response modes:

  • h3_request: buffers the full response body (the original v1 behavior; kept for compatibility and simple use cases).
  • h3_request_streaming: streams the response body through a tokio channel as h3 recv_data chunks arrive, matching the h1/h2 zero-buffer guarantee. The driver task reads recv_data and sends each chunk through a bounded channel (capacity 1) so backpressure flows from the consumer to the upstream. The caller wraps the receiver in UpstreamBody::from_h3_streaming to get a hyper::Body-compatible streaming body with the idle/deadline/health knobs.

The request body is still sent as one DATA frame (the proxy already buffers request bodies for retries); streaming request bodies are a separate enhancement.

Everything in this module is compiled into the OSS build; H3 upstreams are always available.

Structs§

H3UpstreamHandle
Per-upstream H3 transport handle: a QuicStreamPool plus the per-upstream timeouts and shared stats. Stored on super::upstream::UpstreamHandle for the H3 transport; the LB, breaker, retry, and health layers are shared with the TCP/TLS path (they operate on endpoint addresses, not the transport).
QuicStreamPool
QUIC stream-aware connection pool: a bounded set of QUIC connections per endpoint address, each carrying many H3 streams. One QuicStreamPool serves one upstream (the per-upstream connection cap and trust roots are fixed at build time); endpoint addresses are the inner key so a multi-endpoint upstream shares one pool.

Enums§

H3Error
Errors specific to the H3/QUIC upstream transport. Mapped onto the shared UpstreamError by H3UpstreamHandle::send so the proxy, breaker, and health layers stay transport-agnostic.

Constants§

DEFAULT_H3_CONNECTION_CAP
Default maximum QUIC connections per endpoint address when the upstream’s connection_cap is absent. Lower than the TCP/TLS default (64): one QUIC connection carries many streams, so a handful of connections saturates concurrency without the per-stream head-of-line blocking that makes TCP pools need many connections.
DEFAULT_H3_IDLE_TIMEOUT_MS
Default idle timeout for a pooled QUIC connection (no streams opened through it for this long => close and reap). QUIC’s own keep-alive is not configured here; the sweep is the reaper.

Functions§

h3_request
Send an HTTP/3 request over a QUIC stream (one send_request call = one bidirectional stream) and read the full response. The request body is sent as a single DATA frame (the H3 path buffers the request body; see the module docs); the response body is collected into one [Bytes] (streaming H3 response bodies are a follow-up).
h3_request_streaming
Send an HTTP/3 request over a QUIC stream and return a STREAMING response body (PERF-10, #243). The response headers are returned immediately; the body is forwarded through a tokio channel as h3 recv_data chunks arrive, matching the h1/h2 zero-buffer guarantee.