Skip to main content

vibeio_http/h3/
frame.rs

1//! HTTP/3 frame codec (RFC 9114 Section 7).
2//!
3//! Frames on HTTP/3 streams have the layout `Type (i), Length (i), Frame
4//! Payload (..)` where `Type` and `Length` are QUIC variable-length
5//! integers (RFC 9000 Section 16). This module provides:
6//!
7//! - [`FrameDecoder`]: an incremental, buffer-owning decoder. The driver
8//!   feeds received bytes in and pulls completed [`Frame`]s out; `Ok(None)`
9//!   means more input is needed. Unknown and reserved (grease) frame types
10//!   are skipped without surfacing, per RFC 9114 Section 7.2.8.
11//! - [`Frame::encode`]: the corresponding serializer.
12//!
13//! Parse-level validation follows RFC 9114 Section 7.1: a frame payload
14//! must contain exactly the fields identified for its type — extra bytes
15//! and truncated fields are `H3_FRAME_ERROR`, as are redundant
16//! (non-minimal) variable-length integer encodings (Section 10.8, RFC 9000
17//! Section 16). HTTP/2 frame types without an HTTP/3 equivalent
18//! (PRIORITY, PING, WINDOW_UPDATE, CONTINUATION) are `H3_FRAME_UNEXPECTED`
19//! (Section 7.2.8).
20//!
21//! What this module deliberately does *not* enforce — it is the driver's
22//! (connection-state) job:
23//!
24//! - stream-type rules (which frame types are legal on which stream, and
25//!   that SETTINGS is first on the control stream),
26//! - `SETTINGS_MAX_FIELD_SECTION_SIZE` limits on HEADERS/PUSH_PROMISE
27//!   payloads,
28//! - push ID / stream ID semantics (`H3_ID_ERROR`).
29//!
30//! A clean end of stream (FIN) with `buffered() != 0` means the last frame
31//! was truncated; RFC 9114 Section 7.1 requires that be treated as
32//! `H3_FRAME_ERROR` by the driver.
33
34use bytes::{Buf, BufMut, Bytes, BytesMut};
35
36/// The largest value a QUIC variable-length integer can carry.
37pub const MAX_VARINT: u64 = (1 << 62) - 1;
38
39/// `DATA` frame type (RFC 9114 Section 7.2.1).
40pub const FRAME_DATA: u64 = 0x0;
41/// `HEADERS` frame type (RFC 9114 Section 7.2.2).
42pub const FRAME_HEADERS: u64 = 0x1;
43/// `CANCEL_PUSH` frame type (RFC 9114 Section 7.2.3).
44pub const FRAME_CANCEL_PUSH: u64 = 0x3;
45/// `SETTINGS` frame type (RFC 9114 Section 7.2.4).
46pub const FRAME_SETTINGS: u64 = 0x4;
47/// `PUSH_PROMISE` frame type (RFC 9114 Section 7.2.5).
48pub const FRAME_PUSH_PROMISE: u64 = 0x5;
49/// `GOAWAY` frame type (RFC 9114 Section 7.2.6).
50pub const FRAME_GOAWAY: u64 = 0x7;
51/// `MAX_PUSH_ID` frame type (RFC 9114 Section 7.2.7).
52pub const FRAME_MAX_PUSH_ID: u64 = 0xd;
53
54/// `SETTINGS_QPACK_MAX_TABLE_CAPACITY` (RFC 9204 Section 5).
55///
56/// Consumed by the control-stream driver when interpreting peer SETTINGS.
57#[allow(dead_code)]
58pub const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
59/// `SETTINGS_MAX_FIELD_SECTION_SIZE` (RFC 9114 Section 7.2.4.1).
60///
61/// Consumed by the control-stream driver when interpreting peer SETTINGS.
62#[allow(dead_code)]
63pub const SETTINGS_MAX_FIELD_SECTION_SIZE: u64 = 0x6;
64/// `SETTINGS_QPACK_BLOCKED_STREAMS` (RFC 9204 Section 5).
65///
66/// Consumed by the control-stream driver when interpreting peer SETTINGS.
67#[allow(dead_code)]
68pub const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
69/// `SETTINGS_ENABLE_CONNECT_PROTOCOL` (RFC 9114 Section 7.2.4.1).
70///
71/// Consumed by the control-stream driver when interpreting peer SETTINGS.
72#[allow(dead_code)]
73pub const SETTINGS_ENABLE_CONNECT_PROTOCOL: u64 = 0x8;
74/// `SETTINGS_H3_DATAGRAM` (RFC 9297 Section 3.1).
75///
76/// Consumed by the control-stream driver when interpreting peer SETTINGS.
77#[allow(dead_code)]
78pub const SETTINGS_H3_DATAGRAM: u64 = 0x33;
79
80/// A single SETTINGS parameter: `(identifier, value)`.
81pub type Setting = (u64, u64);
82
83/// A parsed `SETTINGS` frame payload (RFC 9114 Section 7.2.4).
84///
85/// Settings are kept in wire order. Reserved (grease) identifiers are
86/// dropped on decode and may be added for encoding; unknown identifiers
87/// are preserved and ignored by the driver.
88#[derive(Debug, Clone, Default, PartialEq, Eq)]
89pub struct Settings {
90    entries: Vec<Setting>,
91}
92
93impl Settings {
94    /// An empty SETTINGS payload.
95    #[inline]
96    pub fn new() -> Self {
97        Self::default()
98    }
99
100    /// Appends a `(identifier, value)` parameter in wire order.
101    #[inline]
102    pub fn insert(&mut self, id: u64, value: u64) {
103        self.entries.push((id, value));
104    }
105
106    /// The value of the first parameter with `id`, if any.
107    #[inline]
108    pub fn get(&self, id: u64) -> Option<u64> {
109        self.entries
110            .iter()
111            .find_map(|(i, v)| (*i == id).then_some(*v))
112    }
113
114    /// The parameters in wire order.
115    #[inline]
116    pub fn iter(&self) -> impl Iterator<Item = Setting> + '_ {
117        self.entries.iter().copied()
118    }
119}
120
121/// An HTTP/3 frame (RFC 9114 Section 7.2).
122///
123/// `Data` and `Headers` payloads are opaque byte ranges. `Data` is the
124/// streamed body chunk (the driver may hand it to the body reader
125/// without copying); `Headers` and `PushPromise` field sections are
126/// QPACK-encoded and decoded by the driver.
127#[derive(Debug, Clone, PartialEq, Eq)]
128pub enum Frame {
129    /// `DATA`: a chunk of the request or response body.
130    Data(Bytes),
131    /// `HEADERS`: the QPACK-encoded field section.
132    Headers(Bytes),
133    /// `SETTINGS`: connection parameters (first frame of a control
134    /// stream).
135    Settings(Settings),
136    /// `CANCEL_PUSH`: push ID whose push the peer should abandon.
137    CancelPush(u64),
138    /// `PUSH_PROMISE`: push ID plus the promised request's QPACK-encoded
139    /// field section.
140    PushPromise { push_id: u64, field_section: Bytes },
141    /// `GOAWAY`: the highest stream ID (server) or push ID (client) the
142    /// sender will process.
143    Goaway(u64),
144    /// `MAX_PUSH_ID`: the highest push ID the server may use.
145    MaxPushId(u64),
146}
147
148impl Frame {
149    /// Whether this is one of the known HTTP/3 frame types (RFC 9114
150    /// Section 7.2).
151    ///
152    /// The decoder never surfaces unknown or reserved (grease) frame types
153    /// — they are consumed and skipped (Section 7.2.8) — so every frame it
154    /// returns is by construction a known type. This method documents the
155    /// request-stream rule (Section 4.1) that after the trailers only
156    /// unknown frames may still appear.
157    #[inline]
158    pub fn is_known(&self) -> bool {
159        matches!(
160            self,
161            Frame::Data(_)
162                | Frame::Headers(_)
163                | Frame::Settings(_)
164                | Frame::CancelPush(_)
165                | Frame::PushPromise { .. }
166                | Frame::Goaway(_)
167                | Frame::MaxPushId(_)
168        )
169    }
170
171    /// Serializes this frame (type, length, payload) into `dst`.
172    #[inline]
173    pub fn encode(&self, dst: &mut BytesMut) {
174        match self {
175            Frame::Data(payload) => {
176                write_varint(FRAME_DATA, dst);
177                write_varint(payload.len() as u64, dst);
178                dst.extend_from_slice(payload);
179            }
180            Frame::Headers(payload) => {
181                write_varint(FRAME_HEADERS, dst);
182                write_varint(payload.len() as u64, dst);
183                dst.extend_from_slice(payload);
184            }
185            Frame::Settings(settings) => {
186                write_varint(FRAME_SETTINGS, dst);
187                let len: usize = settings
188                    .entries
189                    .iter()
190                    .map(|(id, value)| varint_size(*id) + varint_size(*value))
191                    .sum();
192                write_varint(len as u64, dst);
193                for (id, value) in &settings.entries {
194                    write_varint(*id, dst);
195                    write_varint(*value, dst);
196                }
197            }
198            Frame::CancelPush(push_id) => {
199                write_varint(FRAME_CANCEL_PUSH, dst);
200                write_varint(varint_size(*push_id) as u64, dst);
201                write_varint(*push_id, dst);
202            }
203            Frame::PushPromise {
204                push_id,
205                field_section,
206            } => {
207                write_varint(FRAME_PUSH_PROMISE, dst);
208                write_varint((varint_size(*push_id) + field_section.len()) as u64, dst);
209                write_varint(*push_id, dst);
210                dst.extend_from_slice(field_section);
211            }
212            Frame::Goaway(stream_id) => {
213                write_varint(FRAME_GOAWAY, dst);
214                write_varint(varint_size(*stream_id) as u64, dst);
215                write_varint(*stream_id, dst);
216            }
217            Frame::MaxPushId(push_id) => {
218                write_varint(FRAME_MAX_PUSH_ID, dst);
219                write_varint(varint_size(*push_id) as u64, dst);
220                write_varint(*push_id, dst);
221            }
222        }
223    }
224}
225
226/// Errors raised by [`FrameDecoder::next_frame`].
227///
228/// Each variant corresponds to a distinct RFC 9114 connection error code
229/// (see [`FrameError::h3_code`]).
230#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231pub enum FrameError {
232    /// `H3_FRAME_ERROR` (0x0106): the frame payload does not exactly match
233    /// the fields identified for its type, or a variable-length integer is
234    /// encoded non-minimally (RFC 9114 Sections 7.1 and 10.8).
235    Frame,
236    /// `H3_FRAME_UNEXPECTED` (0x0105): an HTTP/2 frame type with no
237    /// HTTP/3 equivalent (PRIORITY, PING, WINDOW_UPDATE, CONTINUATION;
238    /// RFC 9114 Section 7.2.8).
239    Unexpected(u64),
240    /// `H3_SETTINGS_ERROR` (0x0109): a reserved setting identifier
241    /// (0x02-0x05) or a duplicate identifier in one SETTINGS frame
242    /// (RFC 9114 Section 7.2.4).
243    Settings,
244}
245
246impl FrameError {
247    /// The RFC 9114 connection error code for this error.
248    pub const fn h3_code(self) -> u64 {
249        use crate::h3::H3Error;
250        match self {
251            FrameError::Frame => H3Error::FrameError.code(),
252            FrameError::Unexpected(_) => H3Error::FrameUnexpected.code(),
253            FrameError::Settings => H3Error::Settings.code(),
254        }
255    }
256}
257
258/// Incremental HTTP/3 frame decoder.
259///
260/// The decoder owns its buffer: the driver calls [`FrameDecoder::extend`]
261/// with every received chunk and [`FrameDecoder::next_frame`] once per
262/// event-loop turn, draining as many complete frames as are buffered.
263#[derive(Debug, Default)]
264pub struct FrameDecoder {
265    // Keep a transport chunk intact whenever it contains complete frames.
266    // `Bytes` slicing is reference-counted, so DATA and HEADERS can then be
267    // handed to the caller without a second copy. Fragmented frames still
268    // need coalescing when their next chunk arrives.
269    buf: Bytes,
270}
271
272impl FrameDecoder {
273    /// A decoder with an empty buffer.
274    #[inline]
275    pub fn new() -> Self {
276        Self::default()
277    }
278
279    /// Appends received bytes to the input buffer.
280    #[inline]
281    pub fn extend(&mut self, data: Bytes) {
282        if data.is_empty() {
283            return;
284        }
285        if self.buf.is_empty() {
286            self.buf = data;
287            return;
288        }
289
290        let mut joined = BytesMut::with_capacity(self.buf.len() + data.len());
291        joined.extend_from_slice(&self.buf);
292        joined.extend_from_slice(&data);
293        self.buf = joined.freeze();
294    }
295
296    /// Bytes buffered but not yet consumed by a frame.
297    #[inline]
298    pub fn buffered(&self) -> usize {
299        self.buf.len()
300    }
301
302    /// Pops the next complete frame, if any.
303    ///
304    /// Returns `Ok(None)` when the buffer does not yet hold a complete
305    /// frame; callers must extend the buffer and poll again. Unknown and
306    /// reserved frame types are consumed and skipped (RFC 9114 Section
307    /// 7.2.8) — a known frame behind them is still returned.
308    #[inline]
309    pub fn next_frame(&mut self) -> Result<Option<Frame>, FrameError> {
310        loop {
311            let Some((ty, type_len)) = parse_varint(&self.buf)? else {
312                return Ok(None);
313            };
314            if matches!(ty, 0x02 | 0x06 | 0x08 | 0x09) {
315                return Err(FrameError::Unexpected(ty));
316            }
317            let Some((len, len_len)) = parse_varint(&self.buf[type_len..])? else {
318                return Ok(None);
319            };
320            let header_len = type_len + len_len;
321            let Some(total) = header_len.checked_add(len as usize) else {
322                return Err(FrameError::Frame);
323            };
324            if total > self.buf.len() {
325                return Ok(None);
326            }
327            if !is_known_frame_type(ty) {
328                self.buf.advance(total);
329                continue;
330            }
331            let mut chunk = self.buf.split_to(total);
332            let payload = chunk.split_off(header_len);
333            let frame = match ty {
334                FRAME_DATA => Frame::Data(payload),
335                FRAME_HEADERS => Frame::Headers(payload),
336                FRAME_CANCEL_PUSH => Frame::CancelPush(take_varint(&payload)?),
337                FRAME_SETTINGS => Frame::Settings(parse_settings(&payload)?),
338                FRAME_PUSH_PROMISE => {
339                    let Some((push_id, id_len)) = parse_varint(&payload)? else {
340                        return Err(FrameError::Frame);
341                    };
342                    Frame::PushPromise {
343                        push_id,
344                        field_section: payload.slice(id_len..),
345                    }
346                }
347                FRAME_GOAWAY => Frame::Goaway(take_varint(&payload)?),
348                FRAME_MAX_PUSH_ID => Frame::MaxPushId(take_varint(&payload)?),
349                _ => unreachable!("unknown types are skipped above"),
350            };
351            return Ok(Some(frame));
352        }
353    }
354}
355
356impl FrameDecoder {
357    /// Returns the type of the next frame without consuming it, or `None`
358    /// when the type varint is incomplete. Used to pre-reject control-plane
359    /// frames on request streams (RFC 9114 Sections 7.2.3-7.2.7) before the
360    /// decoder parses (and would otherwise accept or mismatch) them.
361    #[inline]
362    pub fn peek_frame_type(&self) -> Option<u64> {
363        match parse_varint(&self.buf) {
364            Ok(Some((ty, _))) => Some(ty),
365            _ => None,
366        }
367    }
368}
369
370#[inline]
371fn is_known_frame_type(ty: u64) -> bool {
372    matches!(
373        ty,
374        FRAME_DATA
375            | FRAME_HEADERS
376            | FRAME_CANCEL_PUSH
377            | FRAME_SETTINGS
378            | FRAME_PUSH_PROMISE
379            | FRAME_GOAWAY
380            | FRAME_MAX_PUSH_ID
381    )
382}
383
384/// Reserved grease identifiers: `0x1f * N + 0x21` (RFC 9114 Sections 7.2.8
385/// and 7.2.4.1) — must be ignored, never interpreted.
386#[inline]
387fn is_grease(v: u64) -> bool {
388    v >= 0x21 && (v - 0x21).is_multiple_of(0x1f)
389}
390
391#[inline]
392fn is_reserved_setting(id: u64) -> bool {
393    (0x02..=0x05).contains(&id)
394}
395
396#[inline]
397fn parse_settings(payload: &[u8]) -> Result<Settings, FrameError> {
398    let mut settings = Settings::new();
399    let mut rest = payload;
400    while !rest.is_empty() {
401        let Some((id, id_len)) = parse_varint(rest)? else {
402            return Err(FrameError::Frame);
403        };
404        rest = &rest[id_len..];
405        let Some((value, value_len)) = parse_varint(rest)? else {
406            return Err(FrameError::Frame);
407        };
408        rest = &rest[value_len..];
409        if is_reserved_setting(id) {
410            return Err(FrameError::Settings);
411        }
412        if settings.get(id).is_some() {
413            return Err(FrameError::Settings);
414        }
415        if !is_grease(id) {
416            settings.insert(id, value);
417        }
418    }
419    Ok(settings)
420}
421
422/// Parses the single variable-length integer that must fill `buf` exactly
423/// (used for CANCEL_PUSH, GOAWAY, MAX_PUSH_ID, and the PUSH_PROMISE push
424/// ID). A truncated or non-minimal integer, or trailing bytes, is
425/// `H3_FRAME_ERROR` (RFC 9114 Sections 7.1 and 10.8).
426#[inline]
427fn take_varint(buf: &[u8]) -> Result<u64, FrameError> {
428    let Some((value, n)) = parse_varint(buf)? else {
429        return Err(FrameError::Frame);
430    };
431    if n != buf.len() {
432        return Err(FrameError::Frame);
433    }
434    Ok(value)
435}
436
437/// The minimum value each variable-length integer encoding width can carry
438/// (indexed by the prefix bits `first >> 6`). A value below it in that width
439/// is a non-minimal encoding (RFC 9000 Section 16).
440const MIN_VARINT: [u64; 4] = [0, 1 << 6, 1 << 14, 1 << 30];
441
442/// Parses a QUIC variable-length integer (RFC 9000 Section 16) from the
443/// front of `buf`.
444///
445/// Returns `Ok(None)` when `buf` is shorter than the encoding; `Err` when
446/// the encoding is non-minimal (a protocol violation, per RFC 9000 Section
447/// 16, surfaced as `H3_FRAME_ERROR`).
448///
449/// The control plane uses this to read uni stream type varints before a
450/// stream is assigned its role.
451#[inline]
452pub(crate) fn parse_varint(buf: &[u8]) -> Result<Option<(u64, usize)>, FrameError> {
453    let Some(&first) = buf.first() else {
454        return Ok(None);
455    };
456    let len = 1usize << (first >> 6);
457    if buf.len() < len {
458        return Ok(None);
459    }
460    let mut value = u64::from(first & 0x3f);
461    for &byte in &buf[1..len] {
462        value = (value << 8) | u64::from(byte);
463    }
464    // Minimal encoding: the value must not fit in the next-smaller
465    // encoding. 2-byte values must be >= 2^6, 4-byte >= 2^14, 8-byte >=
466    // 2^30 (RFC 9000 Section 16).
467    if len > 1 && value < MIN_VARINT[usize::from(first >> 6)] {
468        return Err(FrameError::Frame);
469    }
470    Ok(Some((value, len)))
471}
472
473/// The encoded length of `value` as a QUIC variable-length integer.
474#[inline]
475pub fn varint_size(value: u64) -> usize {
476    if value < (1 << 6) {
477        1
478    } else if value < (1 << 14) {
479        2
480    } else if value < (1 << 30) {
481        4
482    } else {
483        8
484    }
485}
486
487/// Encodes `value` as a QUIC variable-length integer (RFC 9000 Section
488/// 16). Panics in debug builds if `value` does not fit.
489#[inline]
490pub fn write_varint(value: u64, dst: &mut BytesMut) {
491    debug_assert!(value <= MAX_VARINT, "varint out of range: {value:#x}");
492    if value < (1 << 6) {
493        dst.put_u8(value as u8);
494    } else if value < (1 << 14) {
495        dst.put_u16((0b01 << 14) | value as u16);
496    } else if value < (1 << 30) {
497        dst.put_u32((0b10 << 30) | value as u32);
498    } else {
499        dst.put_u64((0b11 << 62) | value);
500    }
501}
502
503#[cfg(test)]
504mod tests {
505    use super::*;
506
507    #[inline]
508    fn decode_all(decoder: &mut FrameDecoder) -> Result<Vec<Frame>, FrameError> {
509        let mut frames = Vec::new();
510        while let Some(frame) = decoder.next_frame()? {
511            frames.push(frame);
512        }
513        Ok(frames)
514    }
515
516    #[inline]
517    fn encode_frames(frames: &[Frame]) -> Bytes {
518        let mut buf = BytesMut::new();
519        for frame in frames {
520            frame.encode(&mut buf);
521        }
522        buf.freeze()
523    }
524
525    #[test]
526    fn round_trip_all_frame_types() {
527        let mut settings = Settings::new();
528        settings.insert(SETTINGS_QPACK_MAX_TABLE_CAPACITY, 4096);
529        settings.insert(SETTINGS_MAX_FIELD_SECTION_SIZE, 100);
530        settings.insert(SETTINGS_QPACK_BLOCKED_STREAMS, 2);
531        settings.insert(0x21, 7); // grease: preserved on encode, ignored on decode
532
533        let frames = [
534            Frame::Data(Bytes::from_static(b"hello world")),
535            Frame::Headers(Bytes::from_static(b"\x3f\xbd\x01")),
536            Frame::Settings(settings),
537            Frame::CancelPush(7),
538            Frame::PushPromise {
539                push_id: 1,
540                field_section: Bytes::from_static(b"\x05\x00\x80"),
541            },
542            Frame::Goaway(2),
543            Frame::MaxPushId(0),
544            Frame::Data(Bytes::new()),
545            Frame::Headers(Bytes::new()),
546        ];
547        let mut decoder = FrameDecoder::new();
548        decoder.extend(encode_frames(&frames));
549        let got = decode_all(&mut decoder).expect("all frames parse");
550
551        // Reserved grease setting is dropped, everything else round-trips.
552        let mut expected = frames.to_vec();
553        expected[2] = Frame::Settings(settings_without_grease());
554        assert_eq!(got, expected);
555    }
556
557    #[inline]
558    fn settings_without_grease() -> Settings {
559        let mut s = Settings::new();
560        s.insert(SETTINGS_QPACK_MAX_TABLE_CAPACITY, 4096);
561        s.insert(SETTINGS_MAX_FIELD_SECTION_SIZE, 100);
562        s.insert(SETTINGS_QPACK_BLOCKED_STREAMS, 2);
563        s
564    }
565
566    #[test]
567    fn incremental_byte_at_a_time() {
568        let wire = encode_frames(&[
569            Frame::Headers(Bytes::from_static(b"abc")),
570            Frame::Data(Bytes::from_static(b"xy")),
571        ]);
572        let mut decoder = FrameDecoder::new();
573        let mut got = Vec::new();
574        for (i, &byte) in wire.iter().enumerate() {
575            decoder.extend(Bytes::copy_from_slice(&[byte]));
576            while let Some(frame) = decoder.next_frame().unwrap() {
577                got.push(frame);
578            }
579            if i < 4 {
580                assert!(got.is_empty(), "frame appeared early at byte {i}");
581            }
582            if i == 4 {
583                // The full HEADERS frame appears exactly when its last
584                // byte lands.
585                assert_eq!(got, vec![Frame::Headers(Bytes::from_static(b"abc"))]);
586            }
587        }
588        assert_eq!(
589            got,
590            vec![
591                Frame::Headers(Bytes::from_static(b"abc")),
592                Frame::Data(Bytes::from_static(b"xy")),
593            ]
594        );
595        assert_eq!(decoder.buffered(), 0);
596    }
597
598    #[test]
599    fn truncated_prefixes_are_incomplete() {
600        // Type byte only.
601        let mut decoder = FrameDecoder::new();
602        decoder.extend(Bytes::from_static(&[0x00]));
603        assert_eq!(decoder.next_frame().unwrap(), None);
604        // Type + length, no payload yet.
605        decoder.extend(Bytes::from_static(&[0x05]));
606        assert_eq!(decoder.next_frame().unwrap(), None);
607        // Partial payload.
608        decoder.extend(Bytes::from_static(b"he"));
609        assert_eq!(decoder.next_frame().unwrap(), None);
610        // Rest of the payload completes the frame.
611        decoder.extend(Bytes::from_static(b"llo"));
612        assert_eq!(
613            decode_all(&mut decoder).unwrap(),
614            vec![Frame::Data(Bytes::from_static(b"hello"))]
615        );
616
617        // A frame declaring the maximum varint length stays incomplete
618        // without erroring or allocating.
619        let mut decoder = FrameDecoder::new();
620        decoder.extend(Bytes::from_static(&[
621            0x01, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
622        ]));
623        assert_eq!(decoder.next_frame().unwrap(), None);
624        assert_eq!(decoder.buffered(), 10);
625    }
626
627    #[test]
628    fn forbidden_http2_frames() {
629        for ty in [0x02u8, 0x06, 0x08, 0x09] {
630            let mut decoder = FrameDecoder::new();
631            decoder.extend(Bytes::copy_from_slice(&[ty, 0x01, 0x00]));
632            let err = decoder.next_frame().unwrap_err();
633            assert_eq!(err, FrameError::Unexpected(u64::from(ty)));
634            assert_eq!(err.h3_code(), 0x0105);
635        }
636    }
637
638    #[test]
639    fn unknown_and_grease_frames_are_skipped() {
640        // Unknown type 0x42 with payload, grease 0x21/0x40/0x5f with
641        // arbitrary payload, between two known frames.
642        let mut wire = BytesMut::new();
643        Frame::Headers(Bytes::from_static(b"first")).encode(&mut wire);
644        write_varint(0x42, &mut wire);
645        write_varint(3, &mut wire);
646        wire.extend_from_slice(b"xyz");
647        write_varint(0x21, &mut wire);
648        write_varint(2, &mut wire);
649        wire.extend_from_slice(&[0xde, 0xad]);
650        Frame::Data(Bytes::from_static(b"last")).encode(&mut wire);
651
652        let mut decoder = FrameDecoder::new();
653        decoder.extend(wire.freeze());
654        let frames = decode_all(&mut decoder).unwrap();
655        assert_eq!(
656            frames,
657            vec![
658                Frame::Headers(Bytes::from_static(b"first")),
659                Frame::Data(Bytes::from_static(b"last")),
660            ]
661        );
662        assert_eq!(decoder.buffered(), 0);
663    }
664
665    #[test]
666    fn settings_payload_validation() {
667        // Empty SETTINGS is legal.
668        let mut decoder = FrameDecoder::new();
669        decoder.extend(Bytes::from_static(&[0x04, 0x00]));
670        assert_eq!(
671            decode_all(&mut decoder).unwrap(),
672            vec![Frame::Settings(Settings::new())]
673        );
674
675        // Duplicate identifier -> H3_SETTINGS_ERROR.
676        let mut wire = BytesMut::new();
677        Frame::Settings({
678            let mut s = Settings::new();
679            s.insert(0x06, 1);
680            s.insert(0x06, 2);
681            s
682        })
683        .encode(&mut wire);
684        let mut decoder = FrameDecoder::new();
685        decoder.extend(wire.freeze());
686        let err = decoder.next_frame().unwrap_err();
687        assert_eq!(err, FrameError::Settings);
688        assert_eq!(err.h3_code(), 0x0109);
689
690        // Reserved identifiers 0x02-0x05 -> H3_SETTINGS_ERROR.
691        for id in 0x02..=0x05 {
692            let mut wire = BytesMut::new();
693            Frame::Settings({
694                let mut s = Settings::new();
695                s.insert(id, 0);
696                s
697            })
698            .encode(&mut wire);
699            let mut decoder = FrameDecoder::new();
700            decoder.extend(wire.freeze());
701            assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Settings);
702        }
703
704        // Unknown identifier is preserved (the driver ignores it), known
705        // ones surface.
706        let mut s = Settings::new();
707        s.insert(0x0100, 5);
708        s.insert(SETTINGS_ENABLE_CONNECT_PROTOCOL, 1);
709        let mut wire = BytesMut::new();
710        Frame::Settings(s.clone()).encode(&mut wire);
711        let mut decoder = FrameDecoder::new();
712        decoder.extend(wire.freeze());
713        match decode_all(&mut decoder).unwrap()[0].clone() {
714            Frame::Settings(got) => {
715                assert_eq!(got.get(0x0100), Some(5));
716                assert_eq!(got.get(SETTINGS_ENABLE_CONNECT_PROTOCOL), Some(1));
717            }
718            other => panic!("expected Settings, got {other:?}"),
719        }
720
721        // Odd-length payload (a lone identifier) -> H3_FRAME_ERROR.
722        let mut decoder = FrameDecoder::new();
723        decoder.extend(Bytes::from_static(&[0x04, 0x01, 0x06]));
724        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
725    }
726
727    #[test]
728    fn fixed_value_frames_reject_bad_payloads() {
729        // CANCEL_PUSH with no payload -> H3_FRAME_ERROR.
730        let mut decoder = FrameDecoder::new();
731        decoder.extend(Bytes::from_static(&[0x03, 0x00]));
732        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
733
734        // CANCEL_PUSH with trailing bytes -> H3_FRAME_ERROR.
735        let mut decoder = FrameDecoder::new();
736        decoder.extend(Bytes::from_static(&[0x03, 0x02, 0x01, 0x00]));
737        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
738
739        // CANCEL_PUSH with a redundant 2-byte encoding of 5 -> H3_FRAME_ERROR.
740        let mut decoder = FrameDecoder::new();
741        decoder.extend(Bytes::from_static(&[0x03, 0x02, 0x40, 0x05]));
742        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
743
744        // GOAWAY with a minimal 1-byte value parses.
745        let mut decoder = FrameDecoder::new();
746        decoder.extend(Bytes::from_static(&[0x07, 0x01, 0x05]));
747        assert_eq!(decode_all(&mut decoder).unwrap(), vec![Frame::Goaway(5)]);
748
749        // Non-minimal type encoding (0 encoded in 2 bytes) -> error.
750        let mut decoder = FrameDecoder::new();
751        decoder.extend(Bytes::from_static(&[0x40, 0x00, 0x00]));
752        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
753
754        // Non-minimal length encoding -> error.
755        let mut decoder = FrameDecoder::new();
756        decoder.extend(Bytes::from_static(&[0x00, 0x40, 0x00]));
757        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
758    }
759
760    #[test]
761    fn push_promise_shapes() {
762        // push ID plus field section.
763        let mut decoder = FrameDecoder::new();
764        decoder.extend(Bytes::from_static(&[0x05, 0x04, 0x01, b'a', b'b', b'c']));
765        assert_eq!(
766            decode_all(&mut decoder).unwrap(),
767            vec![Frame::PushPromise {
768                push_id: 1,
769                field_section: Bytes::from_static(b"abc"),
770            }]
771        );
772
773        // Empty field section.
774        let mut decoder = FrameDecoder::new();
775        decoder.extend(Bytes::from_static(&[0x05, 0x01, 0x01]));
776        assert_eq!(
777            decode_all(&mut decoder).unwrap(),
778            vec![Frame::PushPromise {
779                push_id: 1,
780                field_section: Bytes::new(),
781            }]
782        );
783
784        // Missing push ID -> H3_FRAME_ERROR.
785        let mut decoder = FrameDecoder::new();
786        decoder.extend(Bytes::from_static(&[0x05, 0x00]));
787        assert_eq!(decoder.next_frame().unwrap_err(), FrameError::Frame);
788    }
789
790    #[test]
791    fn varint_edge_encodings() {
792        // Boundaries: 2^6-1 (1 byte), 2^6 (2 bytes), 2^14-1, 2^14, 2^30-1,
793        // 2^30, 2^62-1 (max).
794        for value in [
795            (1 << 6) - 1,
796            1 << 6,
797            (1 << 14) - 1,
798            1 << 14,
799            (1 << 30) - 1,
800            1 << 30,
801            MAX_VARINT,
802        ] {
803            let mut wire = BytesMut::new();
804            write_varint(value, &mut wire);
805            assert_eq!(wire.len(), varint_size(value));
806            let (got, n) = parse_varint(&wire).unwrap().unwrap();
807            assert_eq!(got, value);
808            assert_eq!(n, wire.len());
809        }
810
811        // Non-minimal encodings are rejected.
812        assert_eq!(parse_varint(&[0x40, 0x00]), Err(FrameError::Frame)); // 0 in 2 bytes
813        assert_eq!(
814            parse_varint(&[0x80, 0x00, 0x00, 0x40]),
815            Err(FrameError::Frame)
816        ); // 64 in 4 bytes
817        assert_eq!(parse_varint(&[0x40, 0x40]), Ok(Some((64, 2))));
818        // Truncated.
819        assert_eq!(parse_varint(&[0x40]), Ok(None));
820        assert_eq!(parse_varint(&[]), Ok(None));
821    }
822
823    #[test]
824    fn clean_eof_with_truncated_frame_is_detectable() {
825        let mut decoder = FrameDecoder::new();
826        decoder.extend(Bytes::from_static(&[0x01, 0x05, b'a', b'b']));
827        assert_eq!(decoder.next_frame().unwrap(), None);
828        // Driver's clean-FIN check: buffered() != 0 -> H3_FRAME_ERROR.
829        assert_eq!(decoder.buffered(), 4);
830    }
831}