Skip to main content

webtrans_proto/
frame.rs

1//! HTTP/3 frame type utilities used by WebTransport.
2
3use bytes::{Buf, BufMut};
4
5use crate::grease::is_grease_value;
6use crate::{VarInt, VarIntUnexpectedEnd};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9/// HTTP/3 frame type identifier.
10pub struct Frame(pub VarInt);
11
12impl Frame {
13    /// Decode a frame type varint from the buffer.
14    pub fn decode<B: Buf>(buf: &mut B) -> Result<Self, VarIntUnexpectedEnd> {
15        let typ = VarInt::decode(buf)?;
16        Ok(Frame(typ))
17    }
18
19    /// Encode this frame type varint to the buffer.
20    pub fn encode<B: BufMut>(&self, buf: &mut B) {
21        self.0.encode(buf)
22    }
23
24    /// Return `true` when this frame type uses RFC 9114 GREASE spacing.
25    pub fn is_grease(&self) -> bool {
26        is_grease_value(self.0.into_inner())
27    }
28
29    /// Read one full frame header and return its type plus a limited payload view.
30    pub fn read<B: Buf>(
31        buf: &mut B,
32    ) -> Result<(Frame, bytes::buf::Take<&mut B>), VarIntUnexpectedEnd> {
33        loop {
34            let typ = Frame::decode(buf)?;
35            let size = VarInt::decode(buf)?;
36            let size = usize::try_from(size.into_inner()).map_err(|_| VarIntUnexpectedEnd)?;
37
38            if buf.remaining() < size {
39                return Err(VarIntUnexpectedEnd);
40            }
41
42            // Ignore GREASE frames iteratively so an attacker cannot cause
43            // unbounded recursion with a long sequence of empty frames.
44            if typ.is_grease() {
45                buf.advance(size);
46                continue;
47            }
48
49            return Ok((typ, Buf::take(buf, size)));
50        }
51    }
52
53    /// Build a frame type from a known `u32` value.
54    pub const fn from_u32(value: u32) -> Self {
55        Self(VarInt::from_u32(value))
56    }
57
58    // Frames sent at the start of a bidirectional stream.
59    /// DATA frame type (`0x00`).
60    pub const DATA: Frame = Frame::from_u32(0x00);
61    /// HEADERS frame type (`0x01`).
62    pub const HEADERS: Frame = Frame::from_u32(0x01);
63    /// SETTINGS frame type (`0x04`).
64    pub const SETTINGS: Frame = Frame::from_u32(0x04);
65    /// WEBTRANSPORT stream frame type (`0x41`).
66    pub const WEBTRANSPORT: Frame = Frame::from_u32(0x41);
67}