Skip to main content

volto/h3/
mod.rs

1//! HTTP/3 (RFC 9114) for a proxy, implemented in the tree.
2//!
3//! This is the server half of HTTP/3 and nothing else: it accepts request
4//! streams, decodes a request, sends a response, moves body bytes, and hands
5//! each inbound HTTP Datagram to the request stream it names. There is
6//! no client, no server push, no WebTransport, and no QPACK dynamic table --
7//! every one of them is either unreachable for a CONNECT proxy or refused as a
8//! protocol violation, so leaving them out costs no conformance.
9//!
10//! # Layout
11//!
12//! * [`error`] — the RFC 9114 §8.1 codes, and the two error types the rest of
13//!   the crate sees.
14//! * [`huffman`] — RFC 7541 Appendix B, decoding only.
15//! * [`qpack`] — RFC 9204 field sections against the static table.
16//! * [`frame`] — RFC 9114 §7 framing, incremental and copy-free for DATA.
17//! * [`message`] — what a request and a response are made of: a status, a
18//!   method, field lines.
19//! * [`connection`] — the connection: SETTINGS, the control stream, GOAWAY,
20//!   and inbound HTTP Datagram routing (RFC 9297).
21//! * [`stream`] — one request stream, from its HEADERS to its last byte.
22//!
23//! # Why it exists
24//!
25//! It replaces the `h3` and `h3-quinn` crates, which are no longer in the tree
26//! at all. They were pinned to a git revision because the published releases
27//! carried bugs a proxy cannot live with, and being generic over any QUIC stack
28//! cost this server -- which has exactly one -- more than it bought. What a
29//! proxy asks of HTTP/3 turned out to be small enough to state in full, which is
30//! what the modules above do.
31//!
32//! The integration suite's client is built on these same modules
33//! (`tests/common/h3client.rs`), so the check that this server has not
34//! misunderstood the wire in a way a peer would notice belongs entirely to the
35//! `interop` CI job, which drives a real server with Go's masque-go.
36//!
37//! # What holds it together
38//!
39//! * **Nothing is generic over the QUIC layer.** The types are `quinn`'s, and
40//!   the indirection a library needs in order to back any stack is gone.
41//!   `quinn::SendStream` and `quinn::RecvStream` are held directly, which also
42//!   means their `Drop` behaviour -- a finish on the send side, `STOP_SENDING`
43//!   on the receive side -- is what `tunnel::tcp` reasons about, unmediated.
44//! * **The peer's state is shared, not polled.** One background task per
45//!   connection reads every unidirectional stream and every datagram, so the
46//!   peer's SETTINGS take effect the moment they arrive rather than the next
47//!   time a request is accepted.
48//! * **A connection error is a `quinn::Connection::close`.** RFC 9114 §8 makes
49//!   an HTTP/3 connection error a QUIC CONNECTION_CLOSE carrying the HTTP/3
50//!   code, which is exactly what that call sends; every operation still in
51//!   flight then fails on its own.
52
53pub mod connection;
54pub mod error;
55pub mod frame;
56pub mod huffman;
57pub mod message;
58pub mod qpack;
59pub mod stream;
60
61use error::Code;
62
63// The one rule with no single owner among the modules above, so it is quoted
64// here once and named rather than repeated at each place that obeys it: `frame`
65// skips a frame type it does not know and ignores a SETTINGS identifier it has
66// never seen, while `stream` and `connection` pass the `Item::Skipped` that
67// produces straight over. The test client obeys the same rule for what this
68// server sends.
69//
70//= https://www.rfc-editor.org/rfc/rfc9114#section-9
71//# Implementations MUST ignore unknown or unsupported values in all
72//# extensible protocol elements.
73
74/// Longest a QUIC varint can be, in bytes (RFC 9000 §16), for sizing scratch
75/// buffers. A length, not the largest representable value -- that one is
76/// [`crate::datagram::VARINT_MAX`].
77const VARINT_MAX_LEN: usize = 8;
78
79/// Largest field section this server will decode, in bytes.
80///
81/// Advertised as `SETTINGS_MAX_FIELD_SECTION_SIZE`, so a client that respects
82/// SETTINGS never sends more, and enforced on receipt so one that does not gets
83/// no further than this. A CONNECT request's fields are a couple of hundred
84/// bytes, which leaves three hundred times the room anything legitimate needs.
85///
86/// The unit is the size formula of RFC 9114 §4.2.2: name plus value plus 32
87/// bytes for each field.
88///
89/// # What a section costs once it is decoded, and for how long
90///
91/// This bounds what is *decoded*; [`HEADERS_BUFFER_BUDGET`] bounds what is being
92/// decoded at one moment, in encoded bytes. Neither bounds what the decoded
93/// product costs after that, so the arithmetic is worth stating here, since this
94/// constant is the only knob in it.
95///
96/// A decoded section is a `message::Fields`: a vector of 32-byte entries with an
97/// allocation per name. §4.2.2's 32 bytes a field exist to model exactly that
98/// per-field cost, so a section at this limit costs about this much again once
99/// decoded — measured at ~65 KiB, and at about that whichever way the peer
100/// spends the limit: one 65000-octet value and 1900 one-byte fields are within
101/// one percent of each other
102/// (`tests/it_bounds.rs::a_tunnel_holds_its_requests_field_section_for_its_whole_life`).
103/// The figure was ~77 KiB until audit L1, where the extra was the frame
104/// decoder's own payload buffer rather than the decoded section.
105///
106/// It is held for as long as the request is: `crate::conn::handle_request` keeps
107/// the decoded [`message::Request`] for the whole life of the tunnel it opened,
108/// not merely until the target has been named. So the multiplier is
109/// `max_targets_per_conn` times `max_connections` — ~19 MiB per connection and
110/// ~4.8 GiB across a server at the shipped defaults — with a further transient
111/// while requests are being refused rather than served, where the multiplier is
112/// `max_streams_bidi` instead and each refusal write is bounded by one
113/// `max_idle_timeout`.
114pub const MAX_FIELD_SECTION_SIZE: u64 = 64 * 1024;
115
116/// Most encoded frame payload one connection may hold buffered at once, in
117/// bytes (D77).
118///
119/// [`MAX_FIELD_SECTION_SIZE`] bounds one frame; this bounds their sum, and the
120/// two are reached by different peers. A client held to the per-frame bound may
121/// still open every request stream its transport parameters allow -- 1024 by
122/// default -- announce a 64 KiB HEADERS frame on each and stop one byte short
123/// of finishing any of them. Nothing in that is a rule broken on any single
124/// stream, and the frames are held because they cannot be acted on piecewise:
125/// 64 MiB of a connection's memory, from a peer that has not authenticated, for
126/// as long as the transport is willing to call it alive, and available again
127/// the moment the streams are reset. Multiplied by `limits.max_connections` it
128/// is the whole of the machine. D76 bounds how long one stream may wait; this
129/// bounds how much waiting costs.
130///
131/// A megabyte is sixteen frames of the largest size this server will buffer at
132/// all, and some four thousand of the size a real one is -- a CONNECT request
133/// carrying Basic credentials is under 300 bytes, so all 1024 request streams
134/// the transport allows could be mid-HEADERS at once and still be using a tenth
135/// of this.
136///
137/// The exact boundary is worth stating, because it is not "a client that
138/// finishes what it starts": what is counted is the announced length of the
139/// frames being buffered *at one moment*, so sixteen concurrent field sections
140/// of the largest advertised size fill it and the seventeenth does not fit --
141/// whether or not every one of them is completed a moment later. A peer that
142/// reaches it loses that one request, which is answered with 431 and stopped
143/// (`frame::BufferBudget::charge`); the connection and every tunnel on it carry
144/// on. Bounding a moment rather than a rate is what makes the value a constant
145/// here rather than a knob in `[limits]`: it is a ceiling on memory, and the
146/// machine's is not configurable either.
147///
148/// The peer's control stream is not counted here. There is one of it per
149/// connection and it buffers one frame at a time, so `frame::MAX_BUFFERED_FRAME`
150/// bounds it on its own -- and a peer that had filled this budget with request
151/// streams must not thereby have its own SETTINGS or GOAWAY refused, since
152/// nothing on that stream can be refused stream by stream.
153///
154/// The unit is encoded octets as they arrived, the same count
155/// `frame::MAX_BUFFERED_FRAME` applies per frame -- not RFC 9114 §4.2.2's
156/// field-section size, which is what [`MAX_FIELD_SECTION_SIZE`] measures.
157pub const HEADERS_BUFFER_BUDGET: usize = 1024 * 1024;
158
159/// An HTTP/3 error code as the QUIC application error code that carries it.
160///
161/// RFC 9114 §8 defines the two to be the same number; every registered code is
162/// far below the varint maximum, so the conversion cannot fail for anything
163/// this server sends.
164///
165/// Public because the suite's client resets streams with the same codes and had
166/// grown a byte-identical copy of this.
167pub fn varint(code: Code) -> quinn::VarInt {
168    quinn::VarInt::from_u64(code.value()).unwrap_or(quinn::VarInt::MAX)
169}