Skip to main content

net_wire/http3/frame/
envelope.rs

1//! Exact HTTP/3 frame envelope views.
2//!
3//! Atomic caller-buffer construction of raw HTTP/3 frames.
4
5use core::fmt;
6
7use crate::quic::varint::{
8    QuicVarInt, QuicVarIntBuildError, QuicVarIntBuilder, QuicVarIntLen, QuicVarIntParseError,
9};
10
11use super::super::codepoints::Http3FrameType;
12
13/// Failure to parse a structurally bounded HTTP/3 frame.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub enum Http3FrameParseError {
16    /// The frame type variable-length integer is incomplete.
17    TypeVarInt(QuicVarIntParseError),
18    /// The payload length variable-length integer is incomplete.
19    LengthVarInt(QuicVarIntParseError),
20    /// The encoded payload length cannot be represented as `usize`.
21    PayloadLengthNotRepresentable {
22        /// Encoded payload length.
23        value: u64,
24    },
25    /// Adding the encoded header and payload lengths overflows `usize`.
26    FrameLengthOverflow {
27        /// Encoded type length in bytes.
28        type_length: usize,
29        /// Encoded payload-length length in bytes.
30        length_length: usize,
31        /// Decoded payload length in bytes.
32        payload_length: usize,
33    },
34    /// The declared payload exceeds the caller-provided maximum.
35    PayloadTooLarge {
36        /// Caller-provided maximum payload length.
37        maximum: usize,
38        /// Declared payload length.
39        actual: usize,
40    },
41    /// The complete frame is not available.
42    IncompletePayload {
43        /// Bytes required for the complete frame.
44        required: usize,
45        /// Bytes available in the input.
46        available: usize,
47    },
48}
49
50impl fmt::Display for Http3FrameParseError {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match self {
53            Self::TypeVarInt(error) => {
54                write!(f, "HTTP/3 frame type variable-length integer: {error}")
55            }
56            Self::LengthVarInt(error) => {
57                write!(
58                    f,
59                    "HTTP/3 frame payload length variable-length integer: {error}"
60                )
61            }
62            Self::PayloadLengthNotRepresentable { value } => write!(
63                f,
64                "HTTP/3 frame payload length {value} cannot be represented as usize"
65            ),
66            Self::FrameLengthOverflow {
67                type_length,
68                length_length,
69                payload_length,
70            } => write!(
71                f,
72                "HTTP/3 frame length overflows: type {type_length} bytes, length {length_length} bytes, payload {payload_length} bytes"
73            ),
74            Self::PayloadTooLarge { maximum, actual } => write!(
75                f,
76                "HTTP/3 frame payload is too large: maximum {maximum}, got {actual}"
77            ),
78            Self::IncompletePayload {
79                required,
80                available,
81            } => write!(
82                f,
83                "HTTP/3 frame payload is incomplete: need {required} bytes, have {available}"
84            ),
85        }
86    }
87}
88
89impl core::error::Error for Http3FrameParseError {}
90
91/// A structurally bounded HTTP/3 frame preserving exact variable-integer encodings.
92#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub struct Http3Frame<'a> {
94    bytes: &'a [u8],
95    frame_type: QuicVarInt<'a>,
96    payload_length: QuicVarInt<'a>,
97}
98
99impl<'a> Http3Frame<'a> {
100    pub(super) fn from_validated(
101        bytes: &'a [u8],
102        frame_type_value: u64,
103        frame_type_length: QuicVarIntLen,
104        payload_length_value: u64,
105        payload_length_length: QuicVarIntLen,
106    ) -> Self {
107        let type_end = frame_type_length.byte_len();
108        let length_end = type_end + payload_length_length.byte_len();
109        Self {
110            bytes,
111            frame_type: QuicVarInt::from_validated(
112                &bytes[..type_end],
113                frame_type_value,
114                frame_type_length,
115            ),
116            payload_length: QuicVarInt::from_validated(
117                &bytes[type_end..length_end],
118                payload_length_value,
119                payload_length_length,
120            ),
121        }
122    }
123
124    /// Parses the first complete frame, enforcing the caller-provided payload maximum.
125    pub fn parse(bytes: &'a [u8], maximum_payload: usize) -> Result<Self, Http3FrameParseError> {
126        let frame_type = QuicVarInt::parse(bytes).map_err(Http3FrameParseError::TypeVarInt)?;
127        let type_length = frame_type.byte_len();
128        let payload_length =
129            QuicVarInt::parse(&bytes[type_length..]).map_err(Http3FrameParseError::LengthVarInt)?;
130        let total_length = frame_length(
131            type_length,
132            payload_length.byte_len(),
133            payload_length.value(),
134            maximum_payload,
135            bytes.len(),
136        )?;
137        Ok(Self {
138            bytes: &bytes[..total_length],
139            frame_type,
140            payload_length,
141        })
142    }
143
144    /// Returns the raw frame type.
145    pub const fn frame_type(&self) -> Http3FrameType {
146        Http3FrameType::new(self.frame_type.value())
147    }
148
149    /// Returns the exact frame type variable-length integer.
150    pub const fn frame_type_varint(&self) -> QuicVarInt<'a> {
151        self.frame_type
152    }
153
154    /// Returns the exact payload length variable-length integer.
155    pub const fn payload_length_varint(&self) -> QuicVarInt<'a> {
156        self.payload_length
157    }
158
159    /// Returns the declared payload length.
160    pub fn payload_length(&self) -> usize {
161        self.payload().len()
162    }
163
164    /// Returns the exact payload bytes.
165    pub fn payload(&self) -> &'a [u8] {
166        &self.bytes[self.frame_type.byte_len() + self.payload_length.byte_len()..]
167    }
168
169    /// Returns the exact represented frame bytes.
170    pub const fn as_bytes(&self) -> &'a [u8] {
171        self.bytes
172    }
173}
174
175/// A mutable structurally bounded HTTP/3 frame.
176#[derive(Debug, Eq, PartialEq)]
177pub struct Http3FrameMut<'a> {
178    bytes: &'a mut [u8],
179    frame_type: Http3FrameType,
180    payload_offset: usize,
181}
182
183impl<'a> Http3FrameMut<'a> {
184    /// Parses the first complete frame, enforcing the caller-provided payload maximum.
185    pub fn parse(
186        bytes: &'a mut [u8],
187        maximum_payload: usize,
188    ) -> Result<Self, Http3FrameParseError> {
189        let (frame_type, payload_offset, total_length) = frame_layout(bytes, maximum_payload)?;
190        Ok(Self {
191            bytes: &mut bytes[..total_length],
192            frame_type,
193            payload_offset,
194        })
195    }
196
197    /// Returns the raw frame type.
198    pub const fn frame_type(&self) -> Http3FrameType {
199        self.frame_type
200    }
201
202    /// Returns the declared payload length.
203    pub fn payload_length(&self) -> usize {
204        self.bytes.len() - self.payload_offset
205    }
206
207    /// Returns the exact payload bytes.
208    pub fn payload(&self) -> &[u8] {
209        &self.bytes[self.payload_offset..]
210    }
211
212    /// Returns the exact payload bytes mutably without changing their encoded length.
213    pub fn payload_mut(&mut self) -> &mut [u8] {
214        &mut self.bytes[self.payload_offset..]
215    }
216
217    /// Returns the exact represented frame bytes.
218    pub fn as_bytes(&self) -> &[u8] {
219        self.bytes
220    }
221}
222
223fn frame_layout(
224    bytes: &[u8],
225    maximum_payload: usize,
226) -> Result<(Http3FrameType, usize, usize), Http3FrameParseError> {
227    let frame_type = QuicVarInt::parse(bytes).map_err(Http3FrameParseError::TypeVarInt)?;
228    let type_length = frame_type.byte_len();
229    let payload_length =
230        QuicVarInt::parse(&bytes[type_length..]).map_err(Http3FrameParseError::LengthVarInt)?;
231    let payload_offset = type_length + payload_length.byte_len();
232    let total_length = frame_length(
233        type_length,
234        payload_length.byte_len(),
235        payload_length.value(),
236        maximum_payload,
237        bytes.len(),
238    )?;
239    Ok((
240        Http3FrameType::new(frame_type.value()),
241        payload_offset,
242        total_length,
243    ))
244}
245
246fn frame_length(
247    type_length: usize,
248    length_length: usize,
249    payload_length_value: u64,
250    maximum_payload: usize,
251    available: usize,
252) -> Result<usize, Http3FrameParseError> {
253    let payload_length = usize::try_from(payload_length_value).map_err(|_| {
254        Http3FrameParseError::PayloadLengthNotRepresentable {
255            value: payload_length_value,
256        }
257    })?;
258    if payload_length > maximum_payload {
259        return Err(Http3FrameParseError::PayloadTooLarge {
260            maximum: maximum_payload,
261            actual: payload_length,
262        });
263    }
264    let header_length = type_length.checked_add(length_length).ok_or(
265        Http3FrameParseError::FrameLengthOverflow {
266            type_length,
267            length_length,
268            payload_length,
269        },
270    )?;
271    let required = header_length.checked_add(payload_length).ok_or(
272        Http3FrameParseError::FrameLengthOverflow {
273            type_length,
274            length_length,
275            payload_length,
276        },
277    )?;
278    if available < required {
279        return Err(Http3FrameParseError::IncompletePayload {
280            required,
281            available,
282        });
283    }
284    Ok(required)
285}
286
287/// Failure to build an HTTP/3 frame in caller-provided storage.
288#[derive(Clone, Copy, Debug, Eq, PartialEq)]
289pub enum Http3FrameBuildError {
290    /// The frame type cannot be encoded as a QUIC variable-length integer.
291    TypeVarInt(QuicVarIntBuildError),
292    /// The payload length cannot be encoded as a QUIC variable-length integer.
293    LengthVarInt(QuicVarIntBuildError),
294    /// The payload length cannot be represented as `u64`.
295    PayloadLengthNotRepresentable {
296        /// Supplied payload length.
297        length: usize,
298    },
299    /// Adding the encoded header and payload lengths overflows `usize`.
300    FrameLengthOverflow {
301        /// Encoded type length in bytes.
302        type_length: usize,
303        /// Encoded payload-length length in bytes.
304        length_length: usize,
305        /// Supplied payload length in bytes.
306        payload_length: usize,
307    },
308    /// The caller buffer cannot contain the complete frame.
309    BufferTooShort {
310        /// Bytes required for the complete frame.
311        required: usize,
312        /// Bytes available in the destination.
313        available: usize,
314    },
315}
316
317impl fmt::Display for Http3FrameBuildError {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        match self {
320            Self::TypeVarInt(error) => {
321                write!(f, "HTTP/3 frame type variable-length integer: {error}")
322            }
323            Self::LengthVarInt(error) => {
324                write!(
325                    f,
326                    "HTTP/3 frame payload length variable-length integer: {error}"
327                )
328            }
329            Self::PayloadLengthNotRepresentable { length } => write!(
330                f,
331                "HTTP/3 frame payload length {length} cannot be represented as u64"
332            ),
333            Self::FrameLengthOverflow {
334                type_length,
335                length_length,
336                payload_length,
337            } => write!(
338                f,
339                "HTTP/3 frame length overflows: type {type_length} bytes, length {length_length} bytes, payload {payload_length} bytes"
340            ),
341            Self::BufferTooShort {
342                required,
343                available,
344            } => write!(
345                f,
346                "HTTP/3 frame destination is too short: need {required} bytes, have {available}"
347            ),
348        }
349    }
350}
351
352impl core::error::Error for Http3FrameBuildError {}
353
354/// A fully validated HTTP/3 frame envelope ready to be written.
355pub(crate) struct Http3FrameEnvelopePlan {
356    frame_type: Http3FrameType,
357    payload_length: u64,
358    type_bytes: [u8; 8],
359    type_length: QuicVarIntLen,
360    length_bytes: [u8; 8],
361    length_length: QuicVarIntLen,
362    header_length: usize,
363    required: usize,
364}
365
366impl Http3FrameEnvelopePlan {
367    /// Encodes an envelope and verifies that `available` can hold it and its payload.
368    pub(crate) fn new(
369        frame_type: Http3FrameType,
370        payload_length: usize,
371        available: usize,
372    ) -> Result<Self, Http3FrameBuildError> {
373        let payload_length_value = u64::try_from(payload_length).map_err(|_| {
374            Http3FrameBuildError::PayloadLengthNotRepresentable {
375                length: payload_length,
376            }
377        })?;
378        let mut type_bytes = [0; 8];
379        let type_varint = QuicVarIntBuilder::new(&mut type_bytes, frame_type.value())
380            .build()
381            .map_err(Http3FrameBuildError::TypeVarInt)?;
382        let mut length_bytes = [0; 8];
383        let length_varint = QuicVarIntBuilder::new(&mut length_bytes, payload_length_value)
384            .build()
385            .map_err(Http3FrameBuildError::LengthVarInt)?;
386        let type_length = type_varint.encoded_len();
387        let length_length = length_varint.encoded_len();
388        let header_length = type_length
389            .byte_len()
390            .checked_add(length_length.byte_len())
391            .ok_or(Http3FrameBuildError::FrameLengthOverflow {
392                type_length: type_length.byte_len(),
393                length_length: length_length.byte_len(),
394                payload_length,
395            })?;
396        let required = header_length.checked_add(payload_length).ok_or(
397            Http3FrameBuildError::FrameLengthOverflow {
398                type_length: type_length.byte_len(),
399                length_length: length_length.byte_len(),
400                payload_length,
401            },
402        )?;
403        if available < required {
404            return Err(Http3FrameBuildError::BufferTooShort {
405                required,
406                available,
407            });
408        }
409        Ok(Self {
410            frame_type,
411            payload_length: payload_length_value,
412            type_bytes,
413            type_length,
414            length_bytes,
415            length_length,
416            header_length,
417            required,
418        })
419    }
420
421    /// Returns the offset immediately following the envelope.
422    pub(crate) const fn header_length(&self) -> usize {
423        self.header_length
424    }
425
426    /// Returns the complete frame length established during planning.
427    pub(crate) const fn required(&self) -> usize {
428        self.required
429    }
430
431    /// Writes the already validated envelope.
432    pub(crate) fn write_header(&self, destination: &mut [u8]) {
433        let type_length = self.type_length.byte_len();
434        destination[..type_length].copy_from_slice(&self.type_bytes[..type_length]);
435        destination[type_length..self.header_length]
436            .copy_from_slice(&self.length_bytes[..self.length_length.byte_len()]);
437    }
438
439    /// Associates an already written frame with this validated envelope.
440    pub(crate) fn finish<'output>(self, bytes: &'output mut [u8]) -> Http3Frame<'output> {
441        Http3Frame::from_validated(
442            bytes,
443            self.frame_type.value(),
444            self.type_length,
445            self.payload_length,
446            self.length_length,
447        )
448    }
449}
450
451/// Builds a raw HTTP/3 frame in caller-owned storage.
452pub struct Http3FrameBuilder<'output, 'payload> {
453    destination: &'output mut [u8],
454    frame_type: Http3FrameType,
455    payload: &'payload [u8],
456}
457
458impl<'output, 'payload> Http3FrameBuilder<'output, 'payload> {
459    /// Creates a builder that canonically encodes the type and payload length.
460    pub fn new(
461        destination: &'output mut [u8],
462        frame_type: Http3FrameType,
463        payload: &'payload [u8],
464    ) -> Self {
465        Self {
466            destination,
467            frame_type,
468            payload,
469        }
470    }
471
472    /// Validates all components and capacity before atomically writing the complete frame.
473    pub fn build(self) -> Result<Http3Frame<'output>, Http3FrameBuildError> {
474        let plan = Http3FrameEnvelopePlan::new(
475            self.frame_type,
476            self.payload.len(),
477            self.destination.len(),
478        )?;
479        let required = plan.required();
480        let header_length = plan.header_length();
481        let bytes = &mut self.destination[..required];
482        plan.write_header(bytes);
483        bytes[header_length..].copy_from_slice(self.payload);
484        Ok(plan.finish(bytes))
485    }
486}