Skip to main content

net_wire/http3/stream/
uni.rs

1//! Exact borrowed and caller-buffer-built HTTP/3 unidirectional stream headers.
2
3use core::fmt;
4
5use crate::quic::varint::{
6    QuicVarInt, QuicVarIntBuildError, QuicVarIntBuilder, QuicVarIntLen, QuicVarIntParseError,
7};
8
9use super::super::codepoints::Http3StreamType;
10use super::super::enums::stream::Http3UniStreamKind;
11use super::super::ids::Http3PushId;
12
13/// Identifies a variable-length integer field in an HTTP/3 unidirectional stream header.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub enum Http3UniStreamField {
16    /// The unidirectional stream type.
17    StreamType,
18    /// The Push ID following a push stream type.
19    PushId,
20}
21
22impl fmt::Display for Http3UniStreamField {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            Self::StreamType => f.write_str("stream type"),
26            Self::PushId => f.write_str("Push ID"),
27        }
28    }
29}
30
31/// Failure to parse an HTTP/3 unidirectional stream header.
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub enum Http3UniStreamParseError {
34    /// A required header variable-length integer is incomplete.
35    VarInt {
36        /// Field whose variable-length integer could not be parsed.
37        field: Http3UniStreamField,
38        /// Byte offset from the beginning of the stream header.
39        offset: usize,
40        /// Underlying QUIC variable-length integer failure.
41        error: QuicVarIntParseError,
42    },
43}
44
45impl fmt::Display for Http3UniStreamParseError {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        match self {
48            Self::VarInt {
49                field,
50                offset,
51                error,
52            } => write!(
53                f,
54                "HTTP/3 unidirectional stream {field} variable-length integer at offset {offset}: {error}"
55            ),
56        }
57    }
58}
59
60impl core::error::Error for Http3UniStreamParseError {}
61
62/// An exact borrowed HTTP/3 unidirectional stream header.
63///
64/// The view contains the stream-type variable-length integer and, for a push stream, its Push ID.
65/// Bytes after this header are intentionally excluded and remain owned by the caller.
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
67pub struct Http3UniStreamHeader<'a> {
68    bytes: &'a [u8],
69    stream_type: QuicVarInt<'a>,
70    push_id: Option<QuicVarInt<'a>>,
71}
72
73impl<'a> Http3UniStreamHeader<'a> {
74    /// Parses one exact unidirectional stream header from the beginning of `bytes`.
75    pub fn parse(bytes: &'a [u8]) -> Result<Self, Http3UniStreamParseError> {
76        let stream_type =
77            QuicVarInt::parse(bytes).map_err(|error| Http3UniStreamParseError::VarInt {
78                field: Http3UniStreamField::StreamType,
79                offset: 0,
80                error,
81            })?;
82        let stream_type_length = stream_type.byte_len();
83        let push_id = if stream_type.value() == Http3StreamType::PUSH.value() {
84            Some(
85                QuicVarInt::parse(&bytes[stream_type_length..]).map_err(|error| {
86                    Http3UniStreamParseError::VarInt {
87                        field: Http3UniStreamField::PushId,
88                        offset: stream_type_length,
89                        error,
90                    }
91                })?,
92            )
93        } else {
94            None
95        };
96        let header_length = match push_id {
97            Some(push_id) => stream_type_length + push_id.byte_len(),
98            None => stream_type_length,
99        };
100        Ok(Self::from_validated(
101            &bytes[..header_length],
102            stream_type.value(),
103            stream_type.encoded_len(),
104            push_id.map(|value| (value.value(), value.encoded_len())),
105        ))
106    }
107
108    /// Returns the semantic stream kind.
109    pub const fn kind(self) -> Http3UniStreamKind {
110        match self.stream_type.value() {
111            0 => Http3UniStreamKind::Control,
112            1 => match self.push_id {
113                Some(push_id) => Http3UniStreamKind::Push(Http3PushId::new(push_id.value())),
114                None => Http3UniStreamKind::Unknown(Http3StreamType::PUSH),
115            },
116            2 => Http3UniStreamKind::QpackEncoder,
117            3 => Http3UniStreamKind::QpackDecoder,
118            value => Http3UniStreamKind::Unknown(Http3StreamType::new(value)),
119        }
120    }
121
122    /// Returns the raw unidirectional stream type.
123    pub const fn stream_type(self) -> Http3StreamType {
124        Http3StreamType::new(self.stream_type.value())
125    }
126
127    /// Returns the exact encoded stream-type variable-length integer.
128    pub const fn stream_type_varint(self) -> QuicVarInt<'a> {
129        self.stream_type
130    }
131
132    /// Returns the Push ID for a push stream.
133    pub const fn push_id(self) -> Option<Http3PushId> {
134        match self.push_id {
135            Some(push_id) => Some(Http3PushId::new(push_id.value())),
136            None => None,
137        }
138    }
139
140    /// Returns the exact encoded Push ID variable-length integer for a push stream.
141    pub const fn push_id_varint(self) -> Option<QuicVarInt<'a>> {
142        self.push_id
143    }
144
145    /// Returns exactly the consumed stream header bytes.
146    pub const fn as_bytes(self) -> &'a [u8] {
147        self.bytes
148    }
149
150    /// Assembles a header from builder- or parser-validated wire components.
151    pub(super) fn from_validated(
152        bytes: &'a [u8],
153        stream_type_value: u64,
154        stream_type_length: QuicVarIntLen,
155        push_id: Option<(u64, QuicVarIntLen)>,
156    ) -> Self {
157        let stream_type_length_bytes = stream_type_length.byte_len();
158        let stream_type = QuicVarInt::from_validated(
159            &bytes[..stream_type_length_bytes],
160            stream_type_value,
161            stream_type_length,
162        );
163        let push_id = push_id.map(|(value, length)| {
164            let start = stream_type_length_bytes;
165            QuicVarInt::from_validated(&bytes[start..start + length.byte_len()], value, length)
166        });
167        Self {
168            bytes,
169            stream_type,
170            push_id,
171        }
172    }
173}
174
175/// Failure to build an HTTP/3 unidirectional stream header in caller-provided storage.
176#[derive(Clone, Copy, Debug, Eq, PartialEq)]
177pub enum Http3UniStreamBuildError {
178    /// A header variable-length integer cannot be canonically encoded.
179    VarInt {
180        /// Field whose value could not be encoded.
181        field: Http3UniStreamField,
182        /// Underlying QUIC variable-length integer failure.
183        error: QuicVarIntBuildError,
184    },
185    /// Adding encoded header field lengths overflowed `usize`.
186    LengthOverflow {
187        /// First encoded field length in bytes.
188        first: usize,
189        /// Second encoded field length in bytes.
190        second: usize,
191    },
192    /// The caller buffer cannot contain the complete header.
193    BufferTooShort {
194        /// Bytes required for the complete header.
195        required: usize,
196        /// Bytes available in the destination.
197        available: usize,
198    },
199    /// A known stream type was incorrectly supplied as `Unknown`.
200    KnownTypeMarkedUnknown {
201        /// Known raw stream type value supplied as `Unknown`.
202        stream_type: Http3StreamType,
203    },
204}
205
206impl fmt::Display for Http3UniStreamBuildError {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        match self {
209            Self::VarInt { field, error } => {
210                write!(
211                    f,
212                    "HTTP/3 unidirectional stream {field} variable-length integer: {error}"
213                )
214            }
215            Self::LengthOverflow { first, second } => write!(
216                f,
217                "HTTP/3 unidirectional stream header length overflows: {first} plus {second} bytes"
218            ),
219            Self::BufferTooShort {
220                required,
221                available,
222            } => write!(
223                f,
224                "HTTP/3 unidirectional stream destination is too short: need {required} bytes, have {available}"
225            ),
226            Self::KnownTypeMarkedUnknown { stream_type } => write!(
227                f,
228                "HTTP/3 known unidirectional stream type {} cannot be marked unknown",
229                stream_type.value()
230            ),
231        }
232    }
233}
234
235impl core::error::Error for Http3UniStreamBuildError {}
236
237/// Builds a canonical HTTP/3 unidirectional stream header in caller-owned storage.
238pub struct Http3UniStreamHeaderBuilder<'output> {
239    destination: &'output mut [u8],
240    kind: Http3UniStreamKind,
241}
242
243impl<'output> Http3UniStreamHeaderBuilder<'output> {
244    /// Creates a builder for an HTTP/3 unidirectional stream kind.
245    pub fn new(destination: &'output mut [u8], kind: Http3UniStreamKind) -> Self {
246        Self { destination, kind }
247    }
248
249    /// Canonically encodes and atomically writes the exact stream header.
250    pub fn build(self) -> Result<Http3UniStreamHeader<'output>, Http3UniStreamBuildError> {
251        let (stream_type, push_id) = stream_fields(self.kind)?;
252        let stream_type = encode_varint(stream_type.value(), Http3UniStreamField::StreamType)?;
253        let push_id = match push_id {
254            Some(push_id) => Some(encode_varint(push_id, Http3UniStreamField::PushId)?),
255            None => None,
256        };
257        let required = match push_id {
258            Some(push_id) => stream_type
259                .length
260                .byte_len()
261                .checked_add(push_id.length.byte_len())
262                .ok_or(Http3UniStreamBuildError::LengthOverflow {
263                    first: stream_type.length.byte_len(),
264                    second: push_id.length.byte_len(),
265                })?,
266            None => stream_type.length.byte_len(),
267        };
268        if self.destination.len() < required {
269            return Err(Http3UniStreamBuildError::BufferTooShort {
270                required,
271                available: self.destination.len(),
272            });
273        }
274
275        let bytes = &mut self.destination[..required];
276        let stream_type_length = stream_type.length.byte_len();
277        bytes[..stream_type_length].copy_from_slice(stream_type.bytes());
278        if let Some(push_id) = push_id {
279            bytes[stream_type_length..].copy_from_slice(push_id.bytes());
280        }
281        Ok(Http3UniStreamHeader::from_validated(
282            bytes,
283            stream_type.value,
284            stream_type.length,
285            push_id.map(|push_id| (push_id.value, push_id.length)),
286        ))
287    }
288}
289
290#[derive(Clone, Copy)]
291struct EncodedVarInt {
292    bytes: [u8; 8],
293    value: u64,
294    length: QuicVarIntLen,
295}
296
297impl EncodedVarInt {
298    fn bytes(&self) -> &[u8] {
299        &self.bytes[..self.length.byte_len()]
300    }
301}
302
303fn stream_fields(
304    kind: Http3UniStreamKind,
305) -> Result<(Http3StreamType, Option<u64>), Http3UniStreamBuildError> {
306    match kind {
307        Http3UniStreamKind::Control => Ok((Http3StreamType::CONTROL, None)),
308        Http3UniStreamKind::Push(push_id) => Ok((Http3StreamType::PUSH, Some(push_id.value()))),
309        Http3UniStreamKind::QpackEncoder => Ok((Http3StreamType::QPACK_ENCODER, None)),
310        Http3UniStreamKind::QpackDecoder => Ok((Http3StreamType::QPACK_DECODER, None)),
311        Http3UniStreamKind::Unknown(stream_type) => {
312            if matches!(stream_type.value(), 0..=3) {
313                Err(Http3UniStreamBuildError::KnownTypeMarkedUnknown { stream_type })
314            } else {
315                Ok((stream_type, None))
316            }
317        }
318    }
319}
320
321fn encode_varint(
322    value: u64,
323    field: Http3UniStreamField,
324) -> Result<EncodedVarInt, Http3UniStreamBuildError> {
325    let mut bytes = [0; 8];
326    let varint = QuicVarIntBuilder::new(&mut bytes, value)
327        .build()
328        .map_err(|error| Http3UniStreamBuildError::VarInt { field, error })?;
329    let length = varint.encoded_len();
330    Ok(EncodedVarInt {
331        bytes,
332        value,
333        length,
334    })
335}