Skip to main content

net_wire/http2/hpack/
representation.rs

1//! RFC 7541 section 6 header-field representation parsing.
2
3use core::fmt;
4
5use super::integer::{HpackInteger, HpackIntegerParseError};
6use super::string::{HpackStringLiteral, HpackStringLiteralParseError};
7
8/// One exact borrowed RFC 7541 section 6 header-field representation.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum HpackRepresentation<'a> {
11    /// An indexed header field.
12    Indexed(HpackIndexedField<'a>),
13    /// A literal header field.
14    Literal(HpackLiteralField<'a>),
15    /// A dynamic table size update.
16    DynamicTableSizeUpdate(HpackDynamicTableSizeUpdate<'a>),
17}
18
19impl<'a> HpackRepresentation<'a> {
20    /// Parses one representation and excludes any following bytes.
21    pub fn parse(bytes: &'a [u8]) -> Result<Self, HpackRepresentationParseError> {
22        let first = *bytes
23            .first()
24            .ok_or(HpackRepresentationParseError::IndexedIndex(
25                HpackIntegerParseError::Incomplete {
26                    required: 1,
27                    available: 0,
28                },
29            ))?;
30
31        if first & 0x80 != 0 {
32            HpackIndexedField::parse(bytes).map(Self::Indexed)
33        } else if first & 0x40 != 0 {
34            HpackLiteralField::parse(bytes, HpackLiteralMode::IncrementalIndexing)
35                .map(Self::Literal)
36        } else if first & 0x20 != 0 {
37            HpackDynamicTableSizeUpdate::parse(bytes).map(Self::DynamicTableSizeUpdate)
38        } else if first & 0x10 != 0 {
39            HpackLiteralField::parse(bytes, HpackLiteralMode::NeverIndexed).map(Self::Literal)
40        } else {
41            HpackLiteralField::parse(bytes, HpackLiteralMode::WithoutIndexing).map(Self::Literal)
42        }
43    }
44}
45
46/// An exact borrowed RFC 7541 section 6.1 indexed header field.
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
48pub struct HpackIndexedField<'a> {
49    bytes: &'a [u8],
50    index: HpackInteger<'a>,
51}
52
53impl<'a> HpackIndexedField<'a> {
54    fn parse(bytes: &'a [u8]) -> Result<Self, HpackRepresentationParseError> {
55        let index =
56            HpackInteger::parse(bytes, 7).map_err(HpackRepresentationParseError::IndexedIndex)?;
57        if index.value() == 0 {
58            return Err(HpackRepresentationParseError::IndexedFieldZero);
59        }
60        Ok(Self {
61            bytes: index.as_bytes(),
62            index,
63        })
64    }
65
66    /// Returns the exact indexed-field representation.
67    pub const fn as_bytes(self) -> &'a [u8] {
68        self.bytes
69    }
70
71    /// Returns the exact raw-preserving index representation.
72    pub const fn index(self) -> HpackInteger<'a> {
73        self.index
74    }
75
76    /// Returns the decoded index value.
77    pub const fn index_value(self) -> u64 {
78        self.index.value()
79    }
80}
81
82/// The indexing behavior requested by an HPACK literal header field.
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum HpackLiteralMode {
85    /// Insert the field into the dynamic table.
86    IncrementalIndexing,
87    /// Do not insert the field into the dynamic table.
88    WithoutIndexing,
89    /// Do not insert the field into the dynamic table and mark it sensitive.
90    NeverIndexed,
91}
92
93/// An exact borrowed RFC 7541 literal header field.
94#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub struct HpackLiteralField<'a> {
96    bytes: &'a [u8],
97    mode: HpackLiteralMode,
98    name_index: HpackInteger<'a>,
99    name: Option<HpackStringLiteral<'a>>,
100    value: HpackStringLiteral<'a>,
101}
102
103impl<'a> HpackLiteralField<'a> {
104    fn parse(
105        bytes: &'a [u8],
106        mode: HpackLiteralMode,
107    ) -> Result<Self, HpackRepresentationParseError> {
108        let prefix_bits = match mode {
109            HpackLiteralMode::IncrementalIndexing => 6,
110            HpackLiteralMode::WithoutIndexing | HpackLiteralMode::NeverIndexed => 4,
111        };
112        let name_index = HpackInteger::parse(bytes, prefix_bits)
113            .map_err(HpackRepresentationParseError::LiteralNameIndex)?;
114        let after_name_index = bytes
115            .get(name_index.as_bytes().len()..)
116            .ok_or(HpackRepresentationParseError::LengthOverflow)?;
117        let name = if name_index.value() == 0 {
118            let name = HpackStringLiteral::parse(after_name_index)
119                .map_err(HpackRepresentationParseError::LiteralName)?;
120            Some(name)
121        } else {
122            None
123        };
124        let after_name = match name {
125            Some(name) => after_name_index
126                .get(name.as_bytes().len()..)
127                .ok_or(HpackRepresentationParseError::LengthOverflow)?,
128            None => after_name_index,
129        };
130        let value = HpackStringLiteral::parse(after_name)
131            .map_err(HpackRepresentationParseError::LiteralValue)?;
132        let total = name_index
133            .as_bytes()
134            .len()
135            .checked_add(name.map_or(0, |literal| literal.as_bytes().len()))
136            .and_then(|length| length.checked_add(value.as_bytes().len()))
137            .ok_or(HpackRepresentationParseError::LengthOverflow)?;
138        let representation = bytes
139            .get(..total)
140            .ok_or(HpackRepresentationParseError::LengthOverflow)?;
141
142        Ok(Self {
143            bytes: representation,
144            mode,
145            name_index,
146            name,
147            value,
148        })
149    }
150
151    /// Returns the exact literal-field representation.
152    pub const fn as_bytes(self) -> &'a [u8] {
153        self.bytes
154    }
155
156    /// Returns the literal field's indexing mode.
157    pub const fn mode(self) -> HpackLiteralMode {
158        self.mode
159    }
160
161    /// Returns the exact raw-preserving name index representation.
162    pub const fn name_index(self) -> HpackInteger<'a> {
163        self.name_index
164    }
165
166    /// Returns the decoded name index value; zero means `name` is present.
167    pub const fn name_index_value(self) -> u64 {
168        self.name_index.value()
169    }
170
171    /// Returns the literal name when the name index is zero.
172    pub const fn name(self) -> Option<HpackStringLiteral<'a>> {
173        self.name
174    }
175
176    /// Returns the required opaque literal value.
177    pub const fn value(self) -> HpackStringLiteral<'a> {
178        self.value
179    }
180}
181
182/// An exact borrowed RFC 7541 section 6.3 dynamic table size update.
183#[derive(Clone, Copy, Debug, Eq, PartialEq)]
184pub struct HpackDynamicTableSizeUpdate<'a> {
185    bytes: &'a [u8],
186    size: HpackInteger<'a>,
187}
188
189impl<'a> HpackDynamicTableSizeUpdate<'a> {
190    fn parse(bytes: &'a [u8]) -> Result<Self, HpackRepresentationParseError> {
191        let size = HpackInteger::parse(bytes, 5)
192            .map_err(HpackRepresentationParseError::DynamicTableSize)?;
193        Ok(Self {
194            bytes: size.as_bytes(),
195            size,
196        })
197    }
198
199    /// Returns the exact dynamic-table-size-update representation.
200    pub const fn as_bytes(self) -> &'a [u8] {
201        self.bytes
202    }
203
204    /// Returns the exact raw-preserving size representation.
205    pub const fn size(self) -> HpackInteger<'a> {
206        self.size
207    }
208
209    /// Returns the decoded dynamic table size.
210    pub const fn size_value(self) -> u64 {
211        self.size.value()
212    }
213}
214
215/// Failure to parse an HPACK header-field representation.
216#[derive(Clone, Copy, Debug, Eq, PartialEq)]
217pub enum HpackRepresentationParseError {
218    /// The indexed-field integer is malformed, incomplete, or overflows `u64`.
219    IndexedIndex(HpackIntegerParseError),
220    /// An indexed header field used forbidden index zero.
221    IndexedFieldZero,
222    /// The literal field's name index is malformed, incomplete, or overflows `u64`.
223    LiteralNameIndex(HpackIntegerParseError),
224    /// The literal field's name string is malformed or incomplete.
225    LiteralName(HpackStringLiteralParseError),
226    /// The literal field's value string is malformed or incomplete.
227    LiteralValue(HpackStringLiteralParseError),
228    /// The dynamic table size integer is malformed, incomplete, or overflows `u64`.
229    DynamicTableSize(HpackIntegerParseError),
230    /// Component lengths cannot be represented while framing the representation.
231    LengthOverflow,
232}
233
234impl fmt::Display for HpackRepresentationParseError {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        match self {
237            Self::IndexedIndex(error) => write!(f, "invalid HPACK indexed field index: {error}"),
238            Self::IndexedFieldZero => f.write_str("HPACK indexed field index must not be zero"),
239            Self::LiteralNameIndex(error) => {
240                write!(f, "invalid HPACK literal field name index: {error}")
241            }
242            Self::LiteralName(error) => write!(f, "invalid HPACK literal field name: {error}"),
243            Self::LiteralValue(error) => write!(f, "invalid HPACK literal field value: {error}"),
244            Self::DynamicTableSize(error) => write!(f, "invalid HPACK dynamic table size: {error}"),
245            Self::LengthOverflow => f.write_str("HPACK representation length overflows usize"),
246        }
247    }
248}