Skip to main content

simoom_h3/
lib.rs

1//! Sans-io HTTP/3 (RFC 9114) for simoom, on top of [`simoom_quic::Connection`] streams.
2//!
3//! [`Http3`] owns the HTTP/3 state of one QUIC connection: the control and QPACK streams,
4//! SETTINGS, GOAWAY, and the frame parser of every request stream. Feed it the QUIC events
5//! of its connection with [`Http3::handle`], then drain [`Http3::poll_event`]. A server
6//! answers [`H3Event::Headers`] with [`Http3::send_headers`] and [`Http3::send_body`]; body
7//! sending reports how much was accepted, so a slow client pushes back on the handler.
8//!
9//! The same type acts as a client ([`Http3::send_request`]), used by the tests.
10
11#![forbid(unsafe_code)]
12
13mod frame;
14mod validate;
15
16pub use frame::Settings;
17pub use simoom_qpack::Field;
18pub use validate::{pseudo, validate_request, validate_response, validate_trailers, Validated};
19
20use simoom_qpack::{Decoded, Decoder, Encoder};
21use simoom_quic::coding::{put_varint, varint_len, Reader};
22use simoom_quic::stream::{is_bidi, is_server_initiated, IdMap};
23use simoom_quic::{Connection, Event, StreamError};
24use std::collections::VecDeque;
25use std::time::Instant;
26
27/// The DATA frame queued when a stream has room for less than a frame header and a byte
28/// ([`Http3::send_body`]).
29const SMALL_FRAME: usize = 16 * 1024;
30
31/// Registers with QUIC that a stream with no room left wants to write, so it reports
32/// [`Event::StreamWritable`] when credit arrives. Offering a byte to a stream with no room is
33/// refused (`Blocked`) and does exactly that; nothing is written.
34fn mark_blocked(conn: &mut Connection, stream: u64) {
35    if conn.stream_writable(stream) == Some(0) {
36        let r = conn.stream_send(stream, &[0], false);
37        debug_assert!(!matches!(r, Ok(n) if n > 0), "a stream with no room accepted data");
38    }
39}
40
41/// HTTP/3 error codes (RFC 9114 §8.1).
42pub mod code {
43    pub const NO_ERROR: u64 = 0x100;
44    pub const GENERAL_PROTOCOL_ERROR: u64 = 0x101;
45    pub const INTERNAL_ERROR: u64 = 0x102;
46    pub const STREAM_CREATION_ERROR: u64 = 0x103;
47    pub const CLOSED_CRITICAL_STREAM: u64 = 0x104;
48    pub const FRAME_UNEXPECTED: u64 = 0x105;
49    pub const FRAME_ERROR: u64 = 0x106;
50    pub const EXCESSIVE_LOAD: u64 = 0x107;
51    pub const ID_ERROR: u64 = 0x108;
52    pub const SETTINGS_ERROR: u64 = 0x109;
53    pub const MISSING_SETTINGS: u64 = 0x10a;
54    pub const REQUEST_REJECTED: u64 = 0x10b;
55    pub const REQUEST_CANCELLED: u64 = 0x10c;
56    pub const REQUEST_INCOMPLETE: u64 = 0x10d;
57    pub const MESSAGE_ERROR: u64 = 0x10e;
58    pub const CONNECT_ERROR: u64 = 0x10f;
59    pub const VERSION_FALLBACK: u64 = 0x110;
60}
61
62/// A connection-level HTTP/3 error; the connection is closed with this code.
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub struct H3Error {
65    pub code: u64,
66    pub reason: &'static str,
67}
68
69impl H3Error {
70    pub const fn new(code: u64, reason: &'static str) -> Self {
71        H3Error { code, reason }
72    }
73}
74
75impl std::fmt::Display for H3Error {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "HTTP/3 error 0x{:x}: {}", self.code, self.reason)
78    }
79}
80
81impl std::error::Error for H3Error {}
82
83/// Errors of the per-request API.
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub enum SendError {
86    /// Unknown or finished stream.
87    UnknownStream,
88    /// Headers were already sent, or not yet sent before a body.
89    InvalidState,
90    /// The field section is larger than the peer allows.
91    HeadersTooLarge,
92    /// The peer sent GOAWAY: no new requests.
93    GoingAway,
94    Stream(StreamError),
95}
96
97impl From<StreamError> for SendError {
98    fn from(e: StreamError) -> Self {
99        SendError::Stream(e)
100    }
101}
102
103#[derive(Clone, Debug)]
104pub struct Config {
105    /// SETTINGS_MAX_FIELD_SECTION_SIZE we announce and enforce.
106    pub max_field_section_size: u64,
107    /// SETTINGS_QPACK_MAX_TABLE_CAPACITY we announce (decoder side).
108    pub qpack_max_table_capacity: u64,
109    /// SETTINGS_QPACK_BLOCKED_STREAMS we announce.
110    pub qpack_blocked_streams: u64,
111    /// Use a dynamic table when encoding, up to this capacity (0 = static table only).
112    pub qpack_encoder_capacity: u64,
113    /// Body bytes buffered per stream before reading from QUIC stops (backpressure).
114    pub body_buffer: usize,
115    /// Server: announce SETTINGS_ENABLE_CONNECT_PROTOCOL and accept extended CONNECT with
116    /// `:protocol` (RFC 9220, WebSocket over HTTP/3). Without it such requests are malformed.
117    pub enable_connect_protocol: bool,
118}
119
120impl Default for Config {
121    fn default() -> Self {
122        Config {
123            max_field_section_size: 16 << 10,
124            qpack_max_table_capacity: 4096,
125            qpack_blocked_streams: 16,
126            qpack_encoder_capacity: 0,
127            body_buffer: 64 << 10,
128            enable_connect_protocol: false,
129        }
130    }
131}
132
133#[derive(Clone, Debug, PartialEq, Eq)]
134pub enum H3Event {
135    /// Request headers (server) or response headers (client), validated.
136    Headers { stream: u64, fields: Vec<Field> },
137    /// Body bytes are available on `stream`: call [`Http3::recv_body`].
138    Data { stream: u64 },
139    Trailers { stream: u64, fields: Vec<Field> },
140    /// The peer finished the message (all body bytes have been read).
141    Finished { stream: u64 },
142    /// The peer abandoned the stream, or we rejected a malformed message with `code`.
143    Reset { stream: u64, code: u64 },
144    /// More body may be sent on `stream`.
145    Writable { stream: u64 },
146    /// The peer sent GOAWAY.
147    GoAway { id: u64 },
148    /// The connection failed with this HTTP/3 error and is closing.
149    Error(H3Error),
150}
151
152#[derive(Clone, Copy, PartialEq, Eq, Debug)]
153enum UniKind {
154    Unknown,
155    Control,
156    Encoder,
157    Decoder,
158}
159
160struct UniIn {
161    kind: UniKind,
162    buf: Vec<u8>,
163}
164
165#[derive(Default)]
166struct Req {
167    inbuf: Vec<u8>,
168    body: VecDeque<u8>,
169    /// Bytes left in the current DATA frame, or in an unknown frame being skipped.
170    data_left: u64,
171    skip_left: u64,
172    headers_done: bool,
173    trailers_done: bool,
174    /// Waiting for QPACK encoder-stream data.
175    blocked: bool,
176    fin_seen: bool,
177    finished: bool,
178    content_length: Option<u64>,
179    body_len: u64,
180    data_event: bool,
181    // Sending.
182    out: Vec<u8>,
183    headers_sent: bool,
184    finish_after_out: bool,
185    send_done: bool,
186    want_writable: bool,
187}
188
189pub struct Http3 {
190    is_server: bool,
191    cfg: Config,
192    encoder: Encoder,
193    decoder: Decoder,
194    control_out: Option<u64>,
195    encoder_out: Option<u64>,
196    decoder_out: Option<u64>,
197    pending_uni: VecDeque<(u64, Vec<u8>)>,
198    uni: IdMap<UniIn>,
199    have: [bool; 3],
200    peer_settings: Option<Settings>,
201    reqs: IdMap<Req>,
202    events: VecDeque<H3Event>,
203    pending_fields: VecDeque<(u64, Option<Vec<Field>>)>,
204    goaway_sent: Option<u64>,
205    goaway_recv: Option<u64>,
206    max_request: Option<u64>,
207    failed: bool,
208    scratch: Vec<u8>,
209}
210
211impl Http3 {
212    pub fn new(is_server: bool, cfg: Config) -> Self {
213        let encoder = if cfg.qpack_encoder_capacity > 0 {
214            Encoder::with_dynamic_table(cfg.qpack_encoder_capacity)
215        } else {
216            Encoder::new()
217        };
218        let decoder = Decoder::new(cfg.qpack_max_table_capacity, cfg.qpack_blocked_streams, cfg.max_field_section_size);
219        Http3 {
220            is_server,
221            encoder,
222            decoder,
223            control_out: None,
224            encoder_out: None,
225            decoder_out: None,
226            pending_uni: VecDeque::new(),
227            uni: IdMap::default(),
228            have: [false; 3],
229            peer_settings: None,
230            reqs: IdMap::default(),
231            events: VecDeque::new(),
232            pending_fields: VecDeque::new(),
233            goaway_sent: None,
234            goaway_recv: None,
235            max_request: None,
236            failed: false,
237            scratch: vec![0; 16 << 10],
238            cfg,
239        }
240    }
241
242    pub fn poll_event(&mut self) -> Option<H3Event> {
243        self.events.pop_front()
244    }
245
246    /// The peer (a server) accepts extended CONNECT (RFC 9220): its SETTINGS carried
247    /// SETTINGS_ENABLE_CONNECT_PROTOCOL = 1. `None` until its SETTINGS arrived.
248    pub fn peer_connect_protocol(&self) -> Option<bool> {
249        self.peer_settings.as_ref().map(|s| s.enable_connect_protocol == Some(1))
250    }
251
252    /// The peer's SETTINGS, once received.
253    pub fn peer_settings(&self) -> Option<&Settings> {
254        self.peer_settings.as_ref()
255    }
256
257    /// Request streams currently tracked.
258    pub fn active_streams(&self) -> usize {
259        self.reqs.len()
260    }
261
262    /// Opens the control and QPACK streams and sends SETTINGS. [`Http3::handle`] does this
263    /// on [`Event::Connected`]; call it directly if the connection was established before.
264    pub fn open(&mut self, conn: &mut Connection) {
265        if self.control_out.is_some() {
266            return;
267        }
268        let mut b = Vec::with_capacity(32);
269        put_varint(&mut b, 0x00);
270        Settings {
271            max_field_section_size: Some(self.cfg.max_field_section_size),
272            qpack_max_table_capacity: Some(self.cfg.qpack_max_table_capacity),
273            qpack_blocked_streams: Some(self.cfg.qpack_blocked_streams),
274            enable_connect_protocol: (self.is_server && self.cfg.enable_connect_protocol).then_some(1),
275        }
276        .encode(&mut b);
277        self.control_out = self.open_uni(conn, b);
278        self.encoder_out = self.open_uni(conn, vec![0x02]);
279        self.decoder_out = self.open_uni(conn, vec![0x03]);
280    }
281
282    fn open_uni(&mut self, conn: &mut Connection, prefix: Vec<u8>) -> Option<u64> {
283        let id = conn.open_uni().ok()?;
284        self.pending_uni.push_back((id, prefix));
285        self.flush_uni(conn);
286        Some(id)
287    }
288
289    fn flush_uni(&mut self, conn: &mut Connection) {
290        for e in self.pending_uni.iter_mut() {
291            if let Ok(k) = conn.stream_send(e.0, &e.1, false) {
292                e.1.drain(..k);
293            }
294        }
295        self.pending_uni.retain(|e| !e.1.is_empty());
296    }
297
298    fn send_uni(&mut self, conn: &mut Connection, id: Option<u64>, data: &[u8]) {
299        let Some(id) = id else { return };
300        if data.is_empty() {
301            return;
302        }
303        if let Some(e) = self.pending_uni.iter_mut().find(|e| e.0 == id) {
304            e.1.extend_from_slice(data);
305            return;
306        }
307        self.pending_uni.push_back((id, data.to_vec()));
308        self.flush_uni(conn);
309    }
310
311    /// Writes queued QPACK instructions.
312    fn flush_qpack(&mut self, conn: &mut Connection) {
313        let mut b = Vec::new();
314        self.decoder.take_decoder_stream(&mut b);
315        let d = self.decoder_out;
316        self.send_uni(conn, d, &b);
317        b.clear();
318        self.encoder.take_encoder_stream(&mut b);
319        let e = self.encoder_out;
320        self.send_uni(conn, e, &b);
321    }
322
323    fn fail(&mut self, now: Instant, conn: &mut Connection, e: H3Error) {
324        if self.failed {
325            return;
326        }
327        self.failed = true;
328        conn.close(now, e.code, e.reason.as_bytes());
329        self.events.push_back(H3Event::Error(e));
330    }
331
332    /// A stream error: abandon both directions of a request with `code`.
333    fn reject(&mut self, conn: &mut Connection, id: u64, code: u64) {
334        self.reset_stream(conn, id, code);
335        self.events.push_back(H3Event::Reset { stream: id, code });
336    }
337
338    /// Feeds one QUIC event of this connection.
339    pub fn handle(&mut self, now: Instant, conn: &mut Connection, ev: &Event) {
340        if self.failed {
341            return;
342        }
343        let r = match *ev {
344            Event::Connected => {
345                self.open(conn);
346                Ok(())
347            }
348            Event::StreamOpened(id) => self.on_opened(conn, id),
349            Event::StreamReadable(id) => self.on_readable(conn, id),
350            Event::StreamWritable(id) => {
351                self.on_writable(conn, id);
352                Ok(())
353            }
354            Event::StreamReset { id, code } => self.on_reset(conn, id, code),
355            Event::StreamStopped { id, code } => self.on_stopped(id, code),
356            Event::Closed(_) | Event::StreamsAvailable(_) | Event::PathMigrated(_) => Ok(()),
357        };
358        let r = r.and_then(|_| self.resume_unblocked(conn));
359        if let Err(e) = r {
360            self.fail(now, conn, e);
361            return;
362        }
363        self.flush_qpack(conn);
364    }
365
366    fn is_local_critical(&self, id: u64) -> bool {
367        Some(id) == self.control_out || Some(id) == self.encoder_out || Some(id) == self.decoder_out
368    }
369
370    fn on_stopped(&mut self, id: u64, code_: u64) -> Result<(), H3Error> {
371        if self.is_local_critical(id) {
372            return Err(H3Error::new(code::CLOSED_CRITICAL_STREAM, "critical stream stopped"));
373        }
374        if let Some(r) = self.reqs.get_mut(&id) {
375            r.send_done = true;
376            r.out.clear();
377            self.events.push_back(H3Event::Reset { stream: id, code: code_ });
378            self.maybe_drop(id);
379        }
380        Ok(())
381    }
382
383    fn on_opened(&mut self, conn: &mut Connection, id: u64) -> Result<(), H3Error> {
384        if is_bidi(id) {
385            if !self.is_server || is_server_initiated(id) {
386                return Err(H3Error::new(code::STREAM_CREATION_ERROR, "unexpected bidirectional stream"));
387            }
388            if self.goaway_sent.is_some_and(|g| id >= g) {
389                let _ = conn.stream_stop(id, code::REQUEST_REJECTED);
390                let _ = conn.stream_reset(id, code::REQUEST_REJECTED);
391                return Ok(());
392            }
393            self.max_request = Some(self.max_request.map_or(id, |m| m.max(id)));
394            self.reqs.insert(id, Req::default());
395        } else {
396            self.uni.insert(id, UniIn { kind: UniKind::Unknown, buf: Vec::new() });
397        }
398        Ok(())
399    }
400
401    fn on_reset(&mut self, conn: &mut Connection, id: u64, code_: u64) -> Result<(), H3Error> {
402        if let Some(u) = self.uni.get(&id) {
403            if u.kind != UniKind::Unknown {
404                return Err(H3Error::new(code::CLOSED_CRITICAL_STREAM, "critical stream reset"));
405            }
406            self.uni.remove(&id);
407            return Ok(());
408        }
409        if let Some(r) = self.reqs.remove(&id) {
410            if !r.headers_done || r.blocked {
411                self.decoder.cancel_stream(id);
412            }
413            self.events.push_back(H3Event::Reset { stream: id, code: code_ });
414            // Our side of a reset request is abandoned too (RFC 9114 §4.1.1).
415            if !r.send_done {
416                let _ = conn.stream_reset(id, code::REQUEST_CANCELLED);
417            }
418        }
419        Ok(())
420    }
421
422    fn on_writable(&mut self, conn: &mut Connection, id: u64) {
423        if self.pending_uni.iter().any(|e| e.0 == id) {
424            self.flush_uni(conn);
425            return;
426        }
427        let Some(r) = self.reqs.get_mut(&id) else { return };
428        if !r.out.is_empty() {
429            if let Ok(k) = conn.stream_send(id, &r.out, false) {
430                r.out.drain(..k);
431            }
432            if r.out.is_empty() && r.finish_after_out {
433                let _ = conn.stream_finish(id);
434                r.send_done = true;
435            }
436        }
437        if r.out.is_empty() && r.want_writable && !r.send_done {
438            r.want_writable = false;
439            self.events.push_back(H3Event::Writable { stream: id });
440        }
441        self.maybe_drop(id);
442    }
443
444    fn on_readable(&mut self, conn: &mut Connection, id: u64) -> Result<(), H3Error> {
445        if self.uni.contains_key(&id) {
446            return self.read_uni(conn, id);
447        }
448        if self.reqs.contains_key(&id) {
449            return self.pump(conn, id);
450        }
451        // A stream we rejected or no longer track: discard what arrives.
452        let mut b = std::mem::take(&mut self.scratch);
453        while let Ok((n, fin)) = conn.stream_recv(id, &mut b) {
454            if n == 0 || fin {
455                break;
456            }
457        }
458        self.scratch = b;
459        Ok(())
460    }
461
462    // ------------------------------------------------------------- unidirectional
463
464    fn read_uni(&mut self, conn: &mut Connection, id: u64) -> Result<(), H3Error> {
465        let mut b = std::mem::take(&mut self.scratch);
466        let res = self.read_uni_inner(conn, id, &mut b);
467        self.scratch = b;
468        res
469    }
470
471    fn read_uni_inner(&mut self, conn: &mut Connection, id: u64, b: &mut [u8]) -> Result<(), H3Error> {
472        loop {
473            let (n, fin) = match conn.stream_recv(id, b) {
474                Ok(x) => x,
475                Err(_) => return Ok(()),
476            };
477            let Some(u) = self.uni.get_mut(&id) else { return Ok(()) };
478            if u.kind == UniKind::Unknown {
479                u.buf.extend_from_slice(&b[..n]);
480                let Some((ty, len)) = Reader::new(&u.buf).varint_with_len() else {
481                    if fin {
482                        self.uni.remove(&id);
483                        return Ok(());
484                    }
485                    if n == 0 {
486                        return Ok(());
487                    }
488                    continue;
489                };
490                let kind = match ty {
491                    0x00 => UniKind::Control,
492                    0x02 => UniKind::Encoder,
493                    0x03 => UniKind::Decoder,
494                    0x01 => {
495                        return Err(if self.is_server {
496                            H3Error::new(code::STREAM_CREATION_ERROR, "push stream from a client")
497                        } else {
498                            H3Error::new(code::ID_ERROR, "push stream without MAX_PUSH_ID")
499                        })
500                    }
501                    _ => {
502                        // Unknown stream types are ignored (RFC 9114 §6.2).
503                        let _ = conn.stream_stop(id, code::STREAM_CREATION_ERROR);
504                        self.uni.remove(&id);
505                        return Ok(());
506                    }
507                };
508                let slot = kind as usize - 1;
509                if self.have[slot] {
510                    return Err(H3Error::new(code::STREAM_CREATION_ERROR, "duplicate critical stream"));
511                }
512                self.have[slot] = true;
513                let u = self.uni.get_mut(&id).unwrap();
514                u.kind = kind;
515                let pending = u.buf.split_off(len);
516                u.buf.clear();
517                self.uni_data(id, kind, &pending)?;
518            } else if n > 0 {
519                let kind = u.kind;
520                self.uni_data(id, kind, &b[..n])?;
521            }
522            if fin {
523                return Err(H3Error::new(code::CLOSED_CRITICAL_STREAM, "critical stream closed"));
524            }
525            if n == 0 {
526                return Ok(());
527            }
528        }
529    }
530
531    fn uni_data(&mut self, id: u64, kind: UniKind, data: &[u8]) -> Result<(), H3Error> {
532        match kind {
533            UniKind::Encoder => {
534                let unblocked = self
535                    .decoder
536                    .on_encoder_stream(data)
537                    .map_err(|e| H3Error::new(e.code(), "QPACK encoder stream error"))?;
538                for (sid, res) in unblocked {
539                    if let Some(r) = self.reqs.get_mut(&sid) {
540                        r.blocked = false;
541                    }
542                    match res {
543                        Ok(f) => self.pending_fields.push_back((sid, Some(f))),
544                        Err(simoom_qpack::Error::FieldSectionTooLarge) => self.pending_fields.push_back((sid, None)),
545                        Err(e) => return Err(H3Error::new(e.code(), "QPACK decompression failed")),
546                    }
547                }
548                Ok(())
549            }
550            UniKind::Decoder => self
551                .encoder
552                .on_decoder_stream(data)
553                .map_err(|e| H3Error::new(e.code(), "QPACK decoder stream error")),
554            UniKind::Control => {
555                let u = self.uni.get_mut(&id).unwrap();
556                u.buf.extend_from_slice(data);
557                let buf = std::mem::take(&mut u.buf);
558                let used = self.control_frames(&buf)?;
559                let u = self.uni.get_mut(&id).unwrap();
560                u.buf = buf[used..].to_vec();
561                Ok(())
562            }
563            UniKind::Unknown => Ok(()),
564        }
565    }
566
567    /// Parses complete control-stream frames; returns the bytes consumed.
568    fn control_frames(&mut self, buf: &[u8]) -> Result<usize, H3Error> {
569        let mut pos = 0;
570        loop {
571            let mut r = Reader::new(&buf[pos..]);
572            let (Some(ty), Some(len)) = (r.varint(), r.varint()) else {
573                if buf.len() - pos > 16 {
574                    return Err(H3Error::new(code::FRAME_ERROR, "bad frame header"));
575                }
576                return Ok(pos);
577            };
578            if self.peer_settings.is_none() && ty != frame::SETTINGS {
579                return Err(H3Error::new(code::MISSING_SETTINGS, "first control frame is not SETTINGS"));
580            }
581            // Control frames are small; refuse to buffer large ones.
582            if len > 16 << 10 {
583                return Err(H3Error::new(code::EXCESSIVE_LOAD, "control frame too large"));
584            }
585            if (r.remaining() as u64) < len {
586                return Ok(pos);
587            }
588            let payload = r.bytes(len as usize).unwrap();
589            pos += r.position();
590            match ty {
591                frame::SETTINGS => {
592                    if self.peer_settings.is_some() {
593                        return Err(H3Error::new(code::FRAME_UNEXPECTED, "second SETTINGS"));
594                    }
595                    let s = Settings::decode(payload)?;
596                    self.encoder
597                        .set_peer_settings(s.qpack_max_table_capacity.unwrap_or(0), s.qpack_blocked_streams.unwrap_or(0));
598                    self.peer_settings = Some(s);
599                }
600                frame::GOAWAY => {
601                    let id = frame::single_varint(payload)?;
602                    if !self.is_server && (is_server_initiated(id) || !is_bidi(id)) {
603                        return Err(H3Error::new(code::ID_ERROR, "GOAWAY with a non-request stream ID"));
604                    }
605                    if self.goaway_recv.is_some_and(|g| id > g) {
606                        return Err(H3Error::new(code::ID_ERROR, "GOAWAY ID increased"));
607                    }
608                    self.goaway_recv = Some(id);
609                    self.events.push_back(H3Event::GoAway { id });
610                }
611                frame::MAX_PUSH_ID => {
612                    if !self.is_server {
613                        return Err(H3Error::new(code::FRAME_UNEXPECTED, "MAX_PUSH_ID from a server"));
614                    }
615                    frame::single_varint(payload)?;
616                }
617                frame::CANCEL_PUSH => {
618                    frame::single_varint(payload)?;
619                }
620                frame::DATA | frame::HEADERS | frame::PUSH_PROMISE => {
621                    return Err(H3Error::new(code::FRAME_UNEXPECTED, "frame not allowed on the control stream"))
622                }
623                t if frame::is_reserved_h2(t) => return Err(H3Error::new(code::FRAME_UNEXPECTED, "HTTP/2 frame type")),
624                _ => {} // Unknown and reserved (grease) frames are ignored.
625            }
626        }
627    }
628
629    // ------------------------------------------------------------- request streams
630
631    fn pump(&mut self, conn: &mut Connection, id: u64) -> Result<(), H3Error> {
632        let mut b = std::mem::take(&mut self.scratch);
633        let res = self.pump_inner(conn, id, &mut b);
634        self.scratch = b;
635        res
636    }
637
638    /// Reads from QUIC into the request's buffers and parses frames, stopping when the body
639    /// buffer is full so QUIC flow control pushes back on the peer.
640    fn pump_inner(&mut self, conn: &mut Connection, id: u64, b: &mut [u8]) -> Result<(), H3Error> {
641        loop {
642            let Some(r) = self.reqs.get_mut(&id) else { return Ok(()) };
643            if r.fin_seen || r.blocked || r.body.len() >= self.cfg.body_buffer || r.inbuf.len() > b.len() * 2 {
644                break;
645            }
646            match conn.stream_recv(id, b) {
647                Ok((n, fin)) => {
648                    r.inbuf.extend_from_slice(&b[..n]);
649                    r.fin_seen = fin;
650                    self.parse(conn, id)?;
651                    if n == 0 && !fin {
652                        break;
653                    }
654                }
655                Err(StreamError::Reset(c)) => return self.on_reset(conn, id, c),
656                Err(_) => break,
657            }
658        }
659        self.parse(conn, id)?;
660        self.after_parse(conn, id);
661        Ok(())
662    }
663
664    fn after_parse(&mut self, conn: &mut Connection, id: u64) {
665        let Some(r) = self.reqs.get_mut(&id) else { return };
666        if !r.body.is_empty() && !r.data_event {
667            r.data_event = true;
668            self.events.push_back(H3Event::Data { stream: id });
669        }
670        if r.fin_seen && !r.finished && r.inbuf.is_empty() && !r.blocked {
671            if r.data_left > 0 || r.skip_left > 0 {
672                self.reject(conn, id, code::FRAME_ERROR);
673            } else if !r.headers_done {
674                self.reject(conn, id, code::REQUEST_INCOMPLETE);
675            } else if r.content_length.is_some_and(|cl| cl != r.body_len) {
676                self.reject(conn, id, code::MESSAGE_ERROR);
677            } else if r.body.is_empty() {
678                r.finished = true;
679                self.events.push_back(H3Event::Finished { stream: id });
680                self.maybe_drop(id);
681            }
682        }
683    }
684
685    fn parse(&mut self, conn: &mut Connection, id: u64) -> Result<(), H3Error> {
686        loop {
687            let max_fs = self.cfg.max_field_section_size;
688            let body_cap = self.cfg.body_buffer;
689            let Some(r) = self.reqs.get_mut(&id) else { return Ok(()) };
690            if r.blocked || r.inbuf.is_empty() {
691                return Ok(());
692            }
693            if r.data_left > 0 {
694                if r.body.len() >= body_cap {
695                    return Ok(());
696                }
697                let n = (r.data_left as usize).min(r.inbuf.len()).min(body_cap - r.body.len());
698                r.body.extend(&r.inbuf[..n]);
699                r.inbuf.drain(..n);
700                r.data_left -= n as u64;
701                r.body_len += n as u64;
702                if r.content_length.is_some_and(|cl| r.body_len > cl) {
703                    self.reject(conn, id, code::MESSAGE_ERROR);
704                    return Ok(());
705                }
706                continue;
707            }
708            if r.skip_left > 0 {
709                let n = (r.skip_left as usize).min(r.inbuf.len());
710                r.inbuf.drain(..n);
711                r.skip_left -= n as u64;
712                continue;
713            }
714            let mut rd = Reader::new(&r.inbuf);
715            let (Some(ty), Some(len)) = (rd.varint(), rd.varint()) else {
716                if r.inbuf.len() > 16 {
717                    return Err(H3Error::new(code::FRAME_ERROR, "bad frame header"));
718                }
719                return Ok(());
720            };
721            let hdr = rd.position();
722            match ty {
723                frame::DATA => {
724                    if !r.headers_done || r.trailers_done {
725                        return Err(H3Error::new(code::FRAME_UNEXPECTED, "DATA before HEADERS or after trailers"));
726                    }
727                    r.inbuf.drain(..hdr);
728                    r.data_left = len;
729                }
730                frame::HEADERS => {
731                    if r.trailers_done {
732                        return Err(H3Error::new(code::FRAME_UNEXPECTED, "HEADERS after trailers"));
733                    }
734                    if len > max_fs + 1024 {
735                        self.reject(conn, id, code::EXCESSIVE_LOAD);
736                        return Ok(());
737                    }
738                    if ((r.inbuf.len() - hdr) as u64) < len {
739                        return Ok(());
740                    }
741                    let end = hdr + len as usize;
742                    let block: Vec<u8> = r.inbuf[hdr..end].to_vec();
743                    r.inbuf.drain(..end);
744                    match self.decoder.decode(id, &block) {
745                        Ok(Decoded::Fields(f)) => self.on_fields(conn, id, f),
746                        Ok(Decoded::Blocked) => {
747                            self.reqs.get_mut(&id).unwrap().blocked = true;
748                            return Ok(());
749                        }
750                        Err(simoom_qpack::Error::FieldSectionTooLarge) => {
751                            self.reject(conn, id, code::EXCESSIVE_LOAD);
752                            return Ok(());
753                        }
754                        Err(e) => return Err(H3Error::new(e.code(), "QPACK decompression failed")),
755                    }
756                }
757                frame::PUSH_PROMISE => {
758                    return Err(if self.is_server {
759                        H3Error::new(code::FRAME_UNEXPECTED, "PUSH_PROMISE from a client")
760                    } else {
761                        H3Error::new(code::ID_ERROR, "PUSH_PROMISE without MAX_PUSH_ID")
762                    })
763                }
764                frame::SETTINGS | frame::GOAWAY | frame::MAX_PUSH_ID | frame::CANCEL_PUSH => {
765                    return Err(H3Error::new(code::FRAME_UNEXPECTED, "control frame on a request stream"))
766                }
767                t if frame::is_reserved_h2(t) => return Err(H3Error::new(code::FRAME_UNEXPECTED, "HTTP/2 frame type")),
768                _ => {
769                    r.inbuf.drain(..hdr);
770                    r.skip_left = len;
771                }
772            }
773        }
774    }
775
776    fn on_fields(&mut self, conn: &mut Connection, id: u64, fields: Vec<Field>) {
777        let is_server = self.is_server;
778        let Some(r) = self.reqs.get_mut(&id) else { return };
779        if !r.headers_done {
780            let mut res = if is_server { validate_request(&fields) } else { validate_response(&fields) };
781            if is_server && !self.cfg.enable_connect_protocol && fields.iter().any(|f| f.name == b":protocol") {
782                res = Err(());
783            }
784            match res {
785                Ok(v) => {
786                    if !v.informational {
787                        r.headers_done = true;
788                        r.content_length = v.content_length;
789                    }
790                    self.events.push_back(H3Event::Headers { stream: id, fields });
791                }
792                Err(()) => self.reject(conn, id, code::MESSAGE_ERROR),
793            }
794        } else if validate_trailers(&fields).is_err() {
795            self.reject(conn, id, code::MESSAGE_ERROR);
796        } else {
797            r.trailers_done = true;
798            self.events.push_back(H3Event::Trailers { stream: id, fields });
799        }
800    }
801
802    /// Applies field sections that QPACK unblocked and resumes their streams.
803    fn resume_unblocked(&mut self, conn: &mut Connection) -> Result<(), H3Error> {
804        while let Some((id, f)) = self.pending_fields.pop_front() {
805            match f {
806                None => self.reject(conn, id, code::EXCESSIVE_LOAD),
807                Some(f) => {
808                    self.on_fields(conn, id, f);
809                    self.pump(conn, id)?;
810                }
811            }
812        }
813        Ok(())
814    }
815
816    /// Reads body bytes; returns (bytes, finished). Finished is reported once the peer's
817    /// fin was received and every body byte was read.
818    pub fn recv_body(&mut self, now: Instant, conn: &mut Connection, stream: u64, out: &mut [u8]) -> Result<(usize, bool), SendError> {
819        let r = self.reqs.get_mut(&stream).ok_or(SendError::UnknownStream)?;
820        let n = out.len().min(r.body.len());
821        let (a, b) = r.body.as_slices();
822        if n <= a.len() {
823            out[..n].copy_from_slice(&a[..n]);
824        } else {
825            out[..a.len()].copy_from_slice(a);
826            out[a.len()..n].copy_from_slice(&b[..n - a.len()]);
827        }
828        r.body.drain(..n);
829        if r.body.is_empty() {
830            r.data_event = false;
831        }
832        // Refill from QUIC now that there is room; a parse error here closes later.
833        if let Err(e) = self.pump(conn, stream) {
834            self.fail(now, conn, e);
835        }
836        let Some(r) = self.reqs.get_mut(&stream) else { return Ok((n, true)) };
837        let fin = r.finished || (r.fin_seen && r.inbuf.is_empty() && r.body.is_empty() && r.data_left == 0 && r.headers_done);
838        if fin && !r.finished {
839            r.finished = true;
840            self.events.push_back(H3Event::Finished { stream });
841            self.maybe_drop(stream);
842        }
843        Ok((n, fin))
844    }
845
846    fn maybe_drop(&mut self, id: u64) {
847        if self.reqs.get(&id).is_some_and(|r| r.finished && r.send_done && r.out.is_empty()) {
848            self.reqs.remove(&id);
849        }
850    }
851
852    // ------------------------------------------------------------- sending
853
854    fn encode_headers(&mut self, id: u64, fields: &[(&[u8], &[u8])], out: &mut Vec<u8>) -> Result<(), SendError> {
855        if let Some(max) = self.peer_settings.as_ref().and_then(|s| s.max_field_section_size) {
856            let size: u64 = fields.iter().map(|(n, v)| n.len() as u64 + v.len() as u64 + 32).sum();
857            if size > max {
858                return Err(SendError::HeadersTooLarge);
859            }
860        }
861        let mut block = Vec::with_capacity(64);
862        self.encoder.encode(id, fields, &mut block);
863        put_varint(out, frame::HEADERS);
864        put_varint(out, block.len() as u64);
865        out.extend_from_slice(&block);
866        Ok(())
867    }
868
869    fn write_frame(&mut self, conn: &mut Connection, id: u64, bytes: Vec<u8>, fin: bool) -> Result<(), SendError> {
870        let r = self.reqs.get_mut(&id).ok_or(SendError::UnknownStream)?;
871        if r.out.is_empty() {
872            let k = match conn.stream_send(id, &bytes, fin) {
873                Ok(k) => k,
874                Err(StreamError::Blocked) => 0,
875                Err(e) => return Err(e.into()),
876            };
877            if k < bytes.len() {
878                r.out.extend_from_slice(&bytes[k..]);
879                r.finish_after_out = fin;
880            } else if fin {
881                r.send_done = true;
882            }
883        } else {
884            r.out.extend_from_slice(&bytes);
885            r.finish_after_out = fin;
886        }
887        Ok(())
888    }
889
890    /// Sends response headers (server) or request headers (client). Interim 1xx
891    /// responses may precede the final one.
892    pub fn send_headers(&mut self, conn: &mut Connection, stream: u64, fields: &[(&[u8], &[u8])], fin: bool) -> Result<(), SendError> {
893        let r = self.reqs.get(&stream).ok_or(SendError::UnknownStream)?;
894        if r.send_done || r.headers_sent {
895            return Err(SendError::InvalidState);
896        }
897        let interim = self.is_server && fields.iter().any(|(n, v)| *n == b":status" && v.first() == Some(&b'1'));
898        let mut b = Vec::with_capacity(128);
899        self.encode_headers(stream, fields, &mut b)?;
900        self.write_frame(conn, stream, b, fin && !interim)?;
901        if !interim {
902            self.reqs.get_mut(&stream).unwrap().headers_sent = true;
903        }
904        self.flush_qpack(conn);
905        self.maybe_drop(stream);
906        Ok(())
907    }
908
909    /// Sends body bytes in a DATA frame; returns how many were accepted. Zero means the
910    /// stream is blocked: wait for [`H3Event::Writable`].
911    pub fn send_body(&mut self, conn: &mut Connection, stream: u64, data: &[u8], fin: bool) -> Result<usize, SendError> {
912        let r = self.reqs.get_mut(&stream).ok_or(SendError::UnknownStream)?;
913        if !r.headers_sent || r.send_done {
914            return Err(SendError::InvalidState);
915        }
916        if !r.out.is_empty() {
917            r.want_writable = true;
918            return Ok(0);
919        }
920        if data.is_empty() {
921            if fin {
922                conn.stream_finish(stream)?;
923                r.send_done = true;
924                self.maybe_drop(stream);
925            }
926            return Ok(0);
927        }
928        let room = conn.stream_writable(stream).unwrap_or(0) as usize;
929        let hdr_len = 1 + varint_len(data.len() as u64);
930        if room == 0 {
931            r.want_writable = true;
932            mark_blocked(conn, stream);
933            return Ok(0);
934        }
935        if room <= hdr_len {
936            // Room for less than a frame header and a byte: queue a small frame and offer it.
937            // QUIC takes what fits and remembers that the stream wants to write; the rest
938            // leaves from `out` when credit arrives.
939            let n = data.len().min(SMALL_FRAME);
940            let all = n == data.len() && fin;
941            let mut b = Vec::with_capacity(9 + n);
942            put_varint(&mut b, frame::DATA);
943            put_varint(&mut b, n as u64);
944            b.extend_from_slice(&data[..n]);
945            if n < data.len() {
946                r.want_writable = true;
947            }
948            self.write_frame(conn, stream, b, all)?;
949            if all {
950                self.maybe_drop(stream);
951            }
952            return Ok(n);
953        }
954        let n = data.len().min(room - hdr_len);
955        let mut h = Vec::with_capacity(9);
956        put_varint(&mut h, frame::DATA);
957        put_varint(&mut h, n as u64);
958        let all = n == data.len() && fin;
959        conn.stream_send(stream, &h, false)?;
960        conn.stream_send(stream, &data[..n], all)?;
961        if all {
962            r.send_done = true;
963            self.maybe_drop(stream);
964        } else if n < data.len() {
965            // The frame used all the room. QUIC accepted everything it was offered, so it does
966            // not know the stream wants more: tell it, or MAX_STREAM_DATA (MAX_DATA, an ack
967            // freeing the send buffer) would never wake the stream and a response larger than
968            // the peer's window would stall for good.
969            r.want_writable = true;
970            mark_blocked(conn, stream);
971        }
972        Ok(n)
973    }
974
975    /// Sends trailers and finishes the stream.
976    pub fn send_trailers(&mut self, conn: &mut Connection, stream: u64, fields: &[(&[u8], &[u8])]) -> Result<(), SendError> {
977        let r = self.reqs.get(&stream).ok_or(SendError::UnknownStream)?;
978        if !r.headers_sent || r.send_done {
979            return Err(SendError::InvalidState);
980        }
981        let mut b = Vec::new();
982        self.encode_headers(stream, fields, &mut b)?;
983        self.write_frame(conn, stream, b, true)?;
984        self.flush_qpack(conn);
985        self.maybe_drop(stream);
986        Ok(())
987    }
988
989    /// Abandons a request with a stream error (both directions).
990    pub fn reset_stream(&mut self, conn: &mut Connection, stream: u64, code_: u64) {
991        let _ = conn.stream_stop(stream, code_);
992        let _ = conn.stream_reset(stream, code_);
993        if let Some(r) = self.reqs.remove(&stream) {
994            if !r.headers_done || r.blocked {
995                self.decoder.cancel_stream(stream);
996            }
997        }
998        self.flush_qpack(conn);
999    }
1000
1001    /// Client: opens a request stream and sends its headers.
1002    pub fn send_request(&mut self, conn: &mut Connection, fields: &[(&[u8], &[u8])], fin: bool) -> Result<u64, SendError> {
1003        if self.goaway_recv.is_some() {
1004            return Err(SendError::GoingAway);
1005        }
1006        let id = conn.open_bi()?;
1007        self.reqs.insert(id, Req::default());
1008        self.send_headers(conn, id, fields, fin)?;
1009        Ok(id)
1010    }
1011
1012    /// Server: starts a graceful shutdown. Requests on streams below the returned ID are
1013    /// still served; later ones are rejected with H3_REQUEST_REJECTED.
1014    pub fn goaway(&mut self, conn: &mut Connection) -> u64 {
1015        let id = self.max_request.map_or(0, |m| m + 4);
1016        let id = self.goaway_sent.map_or(id, |g| g.min(id));
1017        self.goaway_sent = Some(id);
1018        let mut b = Vec::with_capacity(10);
1019        put_varint(&mut b, frame::GOAWAY);
1020        put_varint(&mut b, varint_len(id) as u64);
1021        put_varint(&mut b, id);
1022        let c = self.control_out;
1023        self.send_uni(conn, c, &b);
1024        id
1025    }
1026
1027    /// True once GOAWAY was sent and no request streams remain.
1028    pub fn is_idle_after_goaway(&self) -> bool {
1029        self.goaway_sent.is_some() && self.reqs.is_empty()
1030    }
1031}