Skip to main content

net_wire/http3/frame/
payload.rs

1//! Shared typed HTTP/3 frame payload diagnostics and helpers.
2
3use core::fmt;
4
5use crate::quic::varint::{
6    QuicVarInt, QuicVarIntBuildError, QuicVarIntBuilder, QuicVarIntLen, QuicVarIntParseError,
7};
8
9use super::envelope::{
10    Http3Frame, Http3FrameBuildError, Http3FrameEnvelopePlan, Http3FrameParseError,
11};
12use crate::http3::codepoints::Http3FrameType;
13
14/// Identifies an HTTP/3 frame payload field whose variable-length integer is malformed.
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Http3FramePayloadField {
17    /// A Push ID field.
18    PushId,
19    /// The role-neutral identifier carried by a GOAWAY frame.
20    GoawayIdentifier,
21    /// A SETTINGS parameter identifier.
22    SettingIdentifier,
23    /// A SETTINGS parameter value.
24    SettingValue,
25}
26
27impl fmt::Display for Http3FramePayloadField {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Self::PushId => f.write_str("Push ID"),
31            Self::GoawayIdentifier => f.write_str("GOAWAY identifier"),
32            Self::SettingIdentifier => f.write_str("SETTINGS identifier"),
33            Self::SettingValue => f.write_str("SETTINGS value"),
34        }
35    }
36}
37
38/// Failure to parse the intrinsic payload layout of a standard HTTP/3 frame.
39#[derive(Clone, Copy, Debug, Eq, PartialEq)]
40pub enum Http3FramePayloadParseError {
41    /// Parsing the raw HTTP/3 frame envelope failed.
42    Frame(Http3FrameParseError),
43    /// The raw frame type does not match the typed view.
44    WrongFrameType {
45        /// Frame type required by the typed view.
46        expected: Http3FrameType,
47        /// Frame type carried by the raw frame.
48        actual: Http3FrameType,
49    },
50    /// A payload variable-length integer is malformed.
51    PayloadVarInt {
52        /// Field whose variable-length integer could not be parsed.
53        field: Http3FramePayloadField,
54        /// Byte offset within the frame payload.
55        offset: usize,
56        /// Underlying QUIC variable-length integer failure.
57        error: QuicVarIntParseError,
58    },
59    /// An exact-layout payload has bytes after its required field.
60    TrailingPayload {
61        /// Bytes consumed by the required payload fields.
62        consumed: usize,
63        /// Total payload length in bytes.
64        actual: usize,
65    },
66}
67
68impl fmt::Display for Http3FramePayloadParseError {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match self {
71            Self::Frame(error) => write!(f, "HTTP/3 frame: {error}"),
72            Self::WrongFrameType { expected, actual } => write!(
73                f,
74                "wrong HTTP/3 frame type: expected {}, got {}",
75                expected.value(),
76                actual.value()
77            ),
78            Self::PayloadVarInt {
79                field,
80                offset,
81                error,
82            } => write!(
83                f,
84                "HTTP/3 {field} variable-length integer at payload offset {offset}: {error}"
85            ),
86            Self::TrailingPayload { consumed, actual } => write!(
87                f,
88                "HTTP/3 frame payload has trailing bytes: consumed {consumed}, got {actual}"
89            ),
90        }
91    }
92}
93
94impl core::error::Error for Http3FramePayloadParseError {
95    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
96        match self {
97            Self::Frame(error) => Some(error),
98            Self::WrongFrameType { .. }
99            | Self::PayloadVarInt { .. }
100            | Self::TrailingPayload { .. } => None,
101        }
102    }
103}
104
105/// Failure to build an HTTP/3 frame whose payload contains structured fields.
106#[derive(Clone, Copy, Debug, Eq, PartialEq)]
107pub enum Http3FramePayloadBuildError {
108    /// Building the raw HTTP/3 frame envelope failed.
109    Frame(Http3FrameBuildError),
110    /// A payload field cannot be canonically encoded as a QUIC variable-length integer.
111    PayloadVarInt {
112        /// Field whose value could not be encoded.
113        field: Http3FramePayloadField,
114        /// Underlying QUIC variable-length integer failure.
115        error: QuicVarIntBuildError,
116    },
117    /// Adding payload components overflowed `usize`.
118    PayloadLengthOverflow {
119        /// Accumulated payload length before the addition.
120        current: usize,
121        /// Component length being added.
122        addition: usize,
123    },
124}
125
126impl fmt::Display for Http3FramePayloadBuildError {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        match self {
129            Self::Frame(error) => write!(f, "HTTP/3 frame: {error}"),
130            Self::PayloadVarInt { field, error } => {
131                write!(f, "HTTP/3 {field} variable-length integer: {error}")
132            }
133            Self::PayloadLengthOverflow { current, addition } => write!(
134                f,
135                "HTTP/3 frame payload length overflows: {current} plus {addition} bytes"
136            ),
137        }
138    }
139}
140
141impl core::error::Error for Http3FramePayloadBuildError {
142    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
143        match self {
144            Self::Frame(error) => Some(error),
145            Self::PayloadVarInt { .. } | Self::PayloadLengthOverflow { .. } => None,
146        }
147    }
148}
149
150pub(in crate::http3) struct EncodedVarInt {
151    bytes: [u8; 8],
152    pub(super) len: QuicVarIntLen,
153}
154
155impl EncodedVarInt {
156    pub(in crate::http3) fn bytes(&self) -> &[u8] {
157        &self.bytes[..self.len.byte_len()]
158    }
159
160    pub(in crate::http3) const fn byte_len(&self) -> usize {
161        self.len.byte_len()
162    }
163}
164
165pub(in crate::http3) fn encode_varint(value: u64) -> Result<EncodedVarInt, QuicVarIntBuildError> {
166    let mut bytes = [0; 8];
167    let varint = QuicVarIntBuilder::new(&mut bytes, value).build()?;
168    let len = varint.encoded_len();
169    Ok(EncodedVarInt { bytes, len })
170}
171
172pub(super) fn envelope(
173    frame_type: Http3FrameType,
174    payload_length: usize,
175    destination: &mut [u8],
176) -> Result<Http3FrameEnvelopePlan, Http3FramePayloadBuildError> {
177    Http3FrameEnvelopePlan::new(frame_type, payload_length, destination.len())
178        .map_err(Http3FramePayloadBuildError::Frame)
179}
180
181pub(super) fn add_payload(current: usize, addition: usize) -> Option<usize> {
182    current.checked_add(addition)
183}
184
185pub(in crate::http3) fn validate_type(
186    frame: Http3Frame<'_>,
187    expected: Http3FrameType,
188) -> Result<(), Http3FramePayloadParseError> {
189    let actual = frame.frame_type();
190    if actual != expected {
191        return Err(Http3FramePayloadParseError::WrongFrameType { expected, actual });
192    }
193    Ok(())
194}
195
196pub(in crate::http3) fn parse_payload_varint<'a>(
197    bytes: &'a [u8],
198    field: Http3FramePayloadField,
199    offset: usize,
200) -> Result<QuicVarInt<'a>, Http3FramePayloadParseError> {
201    QuicVarInt::parse(bytes).map_err(|error| Http3FramePayloadParseError::PayloadVarInt {
202        field,
203        offset,
204        error,
205    })
206}
207
208pub(super) fn parse_exact_varint<'a>(
209    payload: &'a [u8],
210    field: Http3FramePayloadField,
211) -> Result<QuicVarInt<'a>, Http3FramePayloadParseError> {
212    let varint = parse_payload_varint(payload, field, 0)?;
213    if varint.byte_len() != payload.len() {
214        return Err(Http3FramePayloadParseError::TrailingPayload {
215            consumed: varint.byte_len(),
216            actual: payload.len(),
217        });
218    }
219    Ok(varint)
220}