Skip to main content

net_wire/qpack/field/
line.rs

1//! QPACK field line representations defined by RFC 9204.
2
3mod builder;
4
5use core::fmt;
6
7use crate::qpack::integer::{QpackInteger, QpackIntegerParseError};
8use crate::qpack::string::{QpackStringLiteral, QpackStringLiteralParseError};
9pub use builder::{
10    QpackFieldLineBuildError, QpackIndexedFieldLineBuilder, QpackIndexedPostBaseFieldLineBuilder,
11    QpackLiteralNameFieldLineBuilder, QpackLiteralNameReferenceFieldLineBuilder,
12    QpackLiteralPostBaseNameReferenceFieldLineBuilder,
13};
14pub(crate) use builder::{QpackFieldLinePlan, QpackFieldLinePlanInput, plan_field_line};
15
16/// A parsed QPACK field line.
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub enum QpackFieldLine<'a> {
19    /// An indexed field line.
20    Indexed(QpackIndexedFieldLine<'a>),
21    /// An indexed post-base field line.
22    IndexedPostBase(QpackIndexedPostBaseFieldLine<'a>),
23    /// A literal field line with a referenced name.
24    LiteralNameReference(QpackLiteralNameReferenceFieldLine<'a>),
25    /// A literal field line with a post-base referenced name.
26    LiteralPostBaseNameReference(QpackLiteralPostBaseNameReferenceFieldLine<'a>),
27    /// A literal field line containing a literal name.
28    LiteralName(QpackLiteralNameFieldLine<'a>),
29}
30
31impl<'a> QpackFieldLine<'a> {
32    /// Parses exactly one QPACK field line from the start of `bytes`.
33    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackFieldLineParseError> {
34        let first = *bytes
35            .first()
36            .ok_or(QpackFieldLineParseError::DispatchIncomplete)?;
37
38        if first & 0x80 != 0 {
39            let index =
40                QpackInteger::parse(bytes, 6).map_err(QpackFieldLineParseError::IndexedIndex)?;
41            return Ok(Self::Indexed(QpackIndexedFieldLine {
42                bytes: index.as_bytes(),
43                index,
44            }));
45        }
46
47        if first & 0xc0 == 0x40 {
48            let name_index = QpackInteger::parse(bytes, 4)
49                .map_err(QpackFieldLineParseError::LiteralNameReferenceNameIndex)?;
50            let name_length = name_index.as_bytes().len();
51            let value_bytes = bytes
52                .get(name_length..)
53                .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
54            let value = QpackStringLiteral::parse(value_bytes, 8)
55                .map_err(QpackFieldLineParseError::LiteralNameReferenceValue)?;
56            let length = name_length
57                .checked_add(value.as_bytes().len())
58                .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
59            let line_bytes = bytes
60                .get(..length)
61                .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
62            return Ok(Self::LiteralNameReference(
63                QpackLiteralNameReferenceFieldLine {
64                    bytes: line_bytes,
65                    name_index,
66                    value,
67                },
68            ));
69        }
70
71        if first & 0xe0 == 0x20 {
72            let name = QpackStringLiteral::parse(bytes, 4)
73                .map_err(QpackFieldLineParseError::LiteralNameName)?;
74            let name_length = name.as_bytes().len();
75            let value_bytes = bytes
76                .get(name_length..)
77                .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
78            let value = QpackStringLiteral::parse(value_bytes, 8)
79                .map_err(QpackFieldLineParseError::LiteralNameValue)?;
80            let length = name_length
81                .checked_add(value.as_bytes().len())
82                .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
83            let line_bytes = bytes
84                .get(..length)
85                .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
86            return Ok(Self::LiteralName(QpackLiteralNameFieldLine {
87                bytes: line_bytes,
88                name,
89                value,
90            }));
91        }
92
93        if first & 0xf0 == 0x10 {
94            let index = QpackInteger::parse(bytes, 4)
95                .map_err(QpackFieldLineParseError::IndexedPostBaseIndex)?;
96            return Ok(Self::IndexedPostBase(QpackIndexedPostBaseFieldLine {
97                bytes: index.as_bytes(),
98                index,
99            }));
100        }
101
102        let name_index = QpackInteger::parse(bytes, 3)
103            .map_err(QpackFieldLineParseError::LiteralPostBaseNameReferenceNameIndex)?;
104        let name_length = name_index.as_bytes().len();
105        let value_bytes = bytes
106            .get(name_length..)
107            .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
108        let value = QpackStringLiteral::parse(value_bytes, 8)
109            .map_err(QpackFieldLineParseError::LiteralPostBaseNameReferenceValue)?;
110        let length = name_length
111            .checked_add(value.as_bytes().len())
112            .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
113        let line_bytes = bytes
114            .get(..length)
115            .ok_or(QpackFieldLineParseError::RequiredLengthOverflow)?;
116        Ok(Self::LiteralPostBaseNameReference(
117            QpackLiteralPostBaseNameReferenceFieldLine {
118                bytes: line_bytes,
119                name_index,
120                value,
121            },
122        ))
123    }
124
125    /// Returns the exact encoded bytes occupied by this field line.
126    pub const fn as_bytes(self) -> &'a [u8] {
127        match self {
128            Self::Indexed(line) => line.as_bytes(),
129            Self::IndexedPostBase(line) => line.as_bytes(),
130            Self::LiteralNameReference(line) => line.as_bytes(),
131            Self::LiteralPostBaseNameReference(line) => line.as_bytes(),
132            Self::LiteralName(line) => line.as_bytes(),
133        }
134    }
135}
136
137/// An indexed QPACK field line.
138#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub struct QpackIndexedFieldLine<'a> {
140    bytes: &'a [u8],
141    index: QpackInteger<'a>,
142}
143
144impl<'a> QpackIndexedFieldLine<'a> {
145    /// Returns the exact encoded bytes occupied by this field line.
146    pub const fn as_bytes(self) -> &'a [u8] {
147        self.bytes
148    }
149    /// Returns whether the index refers to the static table.
150    pub const fn is_static(self) -> bool {
151        self.bytes[0] & 0x40 != 0
152    }
153    /// Returns the encoded field-line index.
154    pub const fn index(self) -> QpackInteger<'a> {
155        self.index
156    }
157}
158
159/// An indexed post-base QPACK field line.
160#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub struct QpackIndexedPostBaseFieldLine<'a> {
162    bytes: &'a [u8],
163    index: QpackInteger<'a>,
164}
165
166impl<'a> QpackIndexedPostBaseFieldLine<'a> {
167    /// Returns the exact encoded bytes occupied by this field line.
168    pub const fn as_bytes(self) -> &'a [u8] {
169        self.bytes
170    }
171    /// Returns the encoded post-base index.
172    pub const fn index(self) -> QpackInteger<'a> {
173        self.index
174    }
175}
176
177/// A literal QPACK field line with a referenced name.
178#[derive(Clone, Copy, Debug, Eq, PartialEq)]
179pub struct QpackLiteralNameReferenceFieldLine<'a> {
180    bytes: &'a [u8],
181    name_index: QpackInteger<'a>,
182    value: QpackStringLiteral<'a>,
183}
184
185impl<'a> QpackLiteralNameReferenceFieldLine<'a> {
186    /// Returns the exact encoded bytes occupied by this field line.
187    pub const fn as_bytes(self) -> &'a [u8] {
188        self.bytes
189    }
190    /// Returns whether intermediaries must not index this field line.
191    pub const fn is_never_indexed(self) -> bool {
192        self.bytes[0] & 0x20 != 0
193    }
194    /// Returns whether the name index refers to the static table.
195    pub const fn is_static(self) -> bool {
196        self.bytes[0] & 0x10 != 0
197    }
198    /// Returns the encoded referenced-name index.
199    pub const fn name_index(self) -> QpackInteger<'a> {
200        self.name_index
201    }
202    /// Returns the encoded field value.
203    pub const fn value(self) -> QpackStringLiteral<'a> {
204        self.value
205    }
206}
207
208/// A literal QPACK field line with a post-base referenced name.
209#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210pub struct QpackLiteralPostBaseNameReferenceFieldLine<'a> {
211    bytes: &'a [u8],
212    name_index: QpackInteger<'a>,
213    value: QpackStringLiteral<'a>,
214}
215
216impl<'a> QpackLiteralPostBaseNameReferenceFieldLine<'a> {
217    /// Returns the exact encoded bytes occupied by this field line.
218    pub const fn as_bytes(self) -> &'a [u8] {
219        self.bytes
220    }
221    /// Returns whether intermediaries must not index this field line.
222    pub const fn is_never_indexed(self) -> bool {
223        self.bytes[0] & 0x08 != 0
224    }
225    /// Returns the encoded post-base name index.
226    pub const fn name_index(self) -> QpackInteger<'a> {
227        self.name_index
228    }
229    /// Returns the encoded field value.
230    pub const fn value(self) -> QpackStringLiteral<'a> {
231        self.value
232    }
233}
234
235/// A literal QPACK field line containing a literal name.
236#[derive(Clone, Copy, Debug, Eq, PartialEq)]
237pub struct QpackLiteralNameFieldLine<'a> {
238    bytes: &'a [u8],
239    name: QpackStringLiteral<'a>,
240    value: QpackStringLiteral<'a>,
241}
242
243impl<'a> QpackLiteralNameFieldLine<'a> {
244    /// Returns the exact encoded bytes occupied by this field line.
245    pub const fn as_bytes(self) -> &'a [u8] {
246        self.bytes
247    }
248    /// Returns whether intermediaries must not index this field line.
249    pub const fn is_never_indexed(self) -> bool {
250        self.bytes[0] & 0x10 != 0
251    }
252    /// Returns the encoded literal name.
253    pub const fn name(self) -> QpackStringLiteral<'a> {
254        self.name
255    }
256    /// Returns the encoded field value.
257    pub const fn value(self) -> QpackStringLiteral<'a> {
258        self.value
259    }
260}
261
262/// An error returned while parsing a QPACK field line.
263#[derive(Clone, Copy, Debug, Eq, PartialEq)]
264pub enum QpackFieldLineParseError {
265    /// The input did not contain a field-line dispatch byte.
266    DispatchIncomplete,
267    /// The indexed field-line index could not be parsed.
268    IndexedIndex(#[doc = "The integer parsing error."] QpackIntegerParseError),
269    /// The indexed post-base field-line index could not be parsed.
270    IndexedPostBaseIndex(#[doc = "The integer parsing error."] QpackIntegerParseError),
271    /// The literal name-reference index could not be parsed.
272    LiteralNameReferenceNameIndex(#[doc = "The integer parsing error."] QpackIntegerParseError),
273    /// The literal name-reference value could not be parsed.
274    LiteralNameReferenceValue(
275        #[doc = "The string-literal parsing error."] QpackStringLiteralParseError,
276    ),
277    /// The literal post-base name-reference index could not be parsed.
278    LiteralPostBaseNameReferenceNameIndex(
279        #[doc = "The integer parsing error."] QpackIntegerParseError,
280    ),
281    /// The literal post-base name-reference value could not be parsed.
282    LiteralPostBaseNameReferenceValue(
283        #[doc = "The string-literal parsing error."] QpackStringLiteralParseError,
284    ),
285    /// The literal field name could not be parsed.
286    LiteralNameName(#[doc = "The string-literal parsing error."] QpackStringLiteralParseError),
287    /// The literal field value could not be parsed.
288    LiteralNameValue(#[doc = "The string-literal parsing error."] QpackStringLiteralParseError),
289    /// The combined component lengths overflowed `usize`.
290    RequiredLengthOverflow,
291}
292
293impl fmt::Display for QpackFieldLineParseError {
294    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
295        match self {
296            Self::DispatchIncomplete => formatter.write_str("QPACK field line is incomplete"),
297            Self::IndexedIndex(error) => {
298                write!(formatter, "invalid indexed field line index: {error}")
299            }
300            Self::IndexedPostBaseIndex(error) => write!(
301                formatter,
302                "invalid indexed post-base field line index: {error}"
303            ),
304            Self::LiteralNameReferenceNameIndex(error) => {
305                write!(formatter, "invalid literal name-reference index: {error}")
306            }
307            Self::LiteralNameReferenceValue(error) => {
308                write!(formatter, "invalid literal name-reference value: {error}")
309            }
310            Self::LiteralPostBaseNameReferenceNameIndex(error) => write!(
311                formatter,
312                "invalid literal post-base name-reference index: {error}"
313            ),
314            Self::LiteralPostBaseNameReferenceValue(error) => write!(
315                formatter,
316                "invalid literal post-base name-reference value: {error}"
317            ),
318            Self::LiteralNameName(error) => {
319                write!(formatter, "invalid literal field name: {error}")
320            }
321            Self::LiteralNameValue(error) => {
322                write!(formatter, "invalid literal field value: {error}")
323            }
324            Self::RequiredLengthOverflow => formatter.write_str("QPACK field line length overflow"),
325        }
326    }
327}
328
329/// A validated sequence of QPACK field lines backed by its encoded bytes.
330#[derive(Clone, Copy, Debug, Eq, PartialEq)]
331pub struct QpackFieldLines<'a> {
332    bytes: &'a [u8],
333}
334
335impl<'a> QpackFieldLines<'a> {
336    /// Validates and borrows every field line in `bytes`.
337    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackFieldLinesParseError> {
338        let mut remaining = bytes;
339        while !remaining.is_empty() {
340            let line = match QpackFieldLine::parse(remaining) {
341                Ok(line) => line,
342                Err(error) => {
343                    return Err(QpackFieldLinesParseError {
344                        offset: bytes.len() - remaining.len(),
345                        error,
346                    });
347                }
348            };
349            remaining = &remaining[line.as_bytes().len()..];
350        }
351        Ok(Self { bytes })
352    }
353
354    /// Returns the complete encoded field-line sequence.
355    pub const fn as_bytes(self) -> &'a [u8] {
356        self.bytes
357    }
358
359    /// Iterates over the field lines in this sequence.
360    pub const fn iter(self) -> QpackFieldLineIter<'a> {
361        QpackFieldLineIter::new(self.bytes)
362    }
363}
364
365/// An error encountered while validating a QPACK field-line sequence.
366#[derive(Clone, Copy, Debug, Eq, PartialEq)]
367pub struct QpackFieldLinesParseError {
368    offset: usize,
369    error: QpackFieldLineParseError,
370}
371
372impl QpackFieldLinesParseError {
373    /// Returns the byte offset at which the invalid field line begins.
374    pub const fn offset(self) -> usize {
375        self.offset
376    }
377
378    /// Returns the error reported for the field line at [`Self::offset`].
379    pub const fn error(self) -> QpackFieldLineParseError {
380        self.error
381    }
382}
383
384impl fmt::Display for QpackFieldLinesParseError {
385    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
386        write!(
387            formatter,
388            "QPACK field line at offset {}: {}",
389            self.offset, self.error
390        )
391    }
392}
393
394/// An iterator over encoded QPACK field lines.
395#[derive(Clone, Debug)]
396pub struct QpackFieldLineIter<'a> {
397    remaining: &'a [u8],
398    failed: bool,
399}
400
401impl<'a> QpackFieldLineIter<'a> {
402    /// Creates a fallible iterator over raw encoded field-line bytes.
403    pub const fn new(bytes: &'a [u8]) -> Self {
404        Self {
405            remaining: bytes,
406            failed: false,
407        }
408    }
409}
410
411impl<'a> Iterator for QpackFieldLineIter<'a> {
412    type Item = Result<QpackFieldLine<'a>, QpackFieldLineParseError>;
413
414    fn next(&mut self) -> Option<Self::Item> {
415        if self.failed || self.remaining.is_empty() {
416            return None;
417        }
418        match QpackFieldLine::parse(self.remaining) {
419            Ok(line) => {
420                self.remaining = &self.remaining[line.as_bytes().len()..];
421                Some(Ok(line))
422            }
423            Err(error) => {
424                self.remaining = &[];
425                self.failed = true;
426                Some(Err(error))
427            }
428        }
429    }
430}
431
432impl core::iter::FusedIterator for QpackFieldLineIter<'_> {}