Skip to main content

net_wire/http2/hpack/
representation_builder.rs

1//! Atomic caller-buffer construction for RFC 7541 section 6 representations.
2
3use core::fmt;
4
5use super::integer::{canonical_encoded_len, write_canonical_for_valid_prefix};
6use super::representation::HpackLiteralMode;
7use super::string::HpackStringLiteralBuildError;
8
9/// Builds an RFC 7541 section 6.1 indexed header field in caller-owned storage.
10pub struct HpackIndexedFieldBuilder<'a> {
11    destination: &'a mut [u8],
12    index: u64,
13}
14
15impl<'a> HpackIndexedFieldBuilder<'a> {
16    /// Creates a builder for a nonzero static or dynamic table index.
17    pub fn new(destination: &'a mut [u8], index: u64) -> Self {
18        Self { destination, index }
19    }
20
21    /// Validates capacity before atomically writing a canonical indexed field.
22    pub fn build(self) -> Result<&'a [u8], HpackRepresentationBuildError> {
23        if self.index == 0 {
24            return Err(HpackRepresentationBuildError::IndexedFieldZero);
25        }
26        let required = canonical_encoded_len(self.index, 0x7f);
27        check_capacity(self.destination.len(), required)?;
28        let representation = &mut self.destination[..required];
29        write_integer(representation, 7, 0x7f, 0x80, self.index);
30        Ok(&*representation)
31    }
32}
33
34/// A literal header-field name, either a table index or opaque already encoded bytes.
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum HpackLiteralName<'a> {
37    /// Reuses a nonzero static or dynamic table name index.
38    Indexed(u64),
39    /// Emits a literal name framed as an HPACK string literal.
40    ///
41    /// When `huffman` is true, `encoded_bytes` are already Huffman-encoded opaque bytes.
42    Literal {
43        /// Opaque bytes to frame as the literal name.
44        encoded_bytes: &'a [u8],
45        /// Whether `encoded_bytes` are already Huffman-encoded.
46        huffman: bool,
47    },
48}
49
50/// Opaque already encoded bytes for a literal header-field value.
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub struct HpackLiteralValue<'a> {
53    encoded_bytes: &'a [u8],
54    huffman: bool,
55}
56
57impl<'a> HpackLiteralValue<'a> {
58    /// Creates a value input around opaque bytes already Huffman-encoded when `huffman` is true.
59    ///
60    /// This type only preserves the supplied bytes and flag. It does not encode Huffman data;
61    /// callers needing Huffman coding should first use [`super::HpackHuffmanEncoder`].
62    pub const fn new_encoded(encoded_bytes: &'a [u8], huffman: bool) -> Self {
63        Self {
64            encoded_bytes,
65            huffman,
66        }
67    }
68
69    /// Returns the opaque bytes that will be framed as the value string literal.
70    pub const fn encoded_bytes(self) -> &'a [u8] {
71        self.encoded_bytes
72    }
73
74    /// Returns whether the supplied opaque bytes are already Huffman-encoded.
75    pub const fn is_huffman(self) -> bool {
76        self.huffman
77    }
78}
79
80/// Builds RFC 7541 section 6.2 literal header fields in caller-owned storage.
81pub struct HpackLiteralFieldBuilder<'a, 'b> {
82    destination: &'a mut [u8],
83    mode: HpackLiteralMode,
84    name: HpackLiteralName<'b>,
85    value: HpackLiteralValue<'b>,
86}
87
88impl<'a, 'b> HpackLiteralFieldBuilder<'a, 'b> {
89    /// Creates a builder for a literal field with an explicit indexing mode and byte-oriented inputs.
90    pub fn new(
91        destination: &'a mut [u8],
92        mode: HpackLiteralMode,
93        name: HpackLiteralName<'b>,
94        value: HpackLiteralValue<'b>,
95    ) -> Self {
96        Self {
97            destination,
98            mode,
99            name,
100            value,
101        }
102    }
103
104    /// Validates every component and total capacity before atomically writing a literal field.
105    pub fn build(self) -> Result<&'a [u8], HpackRepresentationBuildError> {
106        let (prefix_bits, high_bits) = match self.mode {
107            HpackLiteralMode::IncrementalIndexing => (6, 0x40),
108            HpackLiteralMode::WithoutIndexing => (4, 0),
109            HpackLiteralMode::NeverIndexed => (4, 0x10),
110        };
111        let name_index = self.name_index();
112        if matches!(self.name, HpackLiteralName::Indexed(0)) {
113            return Err(HpackRepresentationBuildError::LiteralNameIndexZero);
114        }
115        let literal_name = self
116            .literal_name()
117            .map(|name| {
118                string_info(name)
119                    .map(|info| (name, info))
120                    .map_err(HpackRepresentationBuildError::LiteralName)
121            })
122            .transpose()?;
123        let name_index_len = canonical_encoded_len(name_index, prefix_mask(prefix_bits));
124        let literal_name_len = literal_name.map_or(0, |(_, (length, _))| length);
125        let (value_len, value_encoded_length) =
126            string_info(self.value).map_err(HpackRepresentationBuildError::LiteralValue)?;
127        let required = name_index_len
128            .checked_add(literal_name_len)
129            .and_then(|length| length.checked_add(value_len))
130            .ok_or(HpackRepresentationBuildError::TotalLengthOverflow)?;
131        check_capacity(self.destination.len(), required)?;
132
133        let representation = &mut self.destination[..required];
134        let (name_index_destination, after_name_index) =
135            representation.split_at_mut(name_index_len);
136        write_integer(
137            name_index_destination,
138            prefix_bits,
139            prefix_mask(prefix_bits),
140            high_bits,
141            name_index,
142        );
143        let after_name = match literal_name {
144            Some((name, (_, encoded_length))) => {
145                let (name_destination, remainder) = after_name_index.split_at_mut(literal_name_len);
146                write_string(name_destination, name, encoded_length);
147                remainder
148            }
149            None => after_name_index,
150        };
151        write_string(after_name, self.value, value_encoded_length);
152        Ok(&*representation)
153    }
154
155    fn name_index(&self) -> u64 {
156        match self.name {
157            HpackLiteralName::Indexed(index) => index,
158            HpackLiteralName::Literal { .. } => 0,
159        }
160    }
161
162    fn literal_name(&self) -> Option<HpackLiteralValue<'b>> {
163        match self.name {
164            HpackLiteralName::Indexed(_) => None,
165            HpackLiteralName::Literal {
166                encoded_bytes,
167                huffman,
168            } => Some(HpackLiteralValue::new_encoded(encoded_bytes, huffman)),
169        }
170    }
171}
172
173/// Builds an RFC 7541 section 6.3 dynamic table size update in caller-owned storage.
174pub struct HpackDynamicTableSizeUpdateBuilder<'a> {
175    destination: &'a mut [u8],
176    size: u64,
177}
178
179impl<'a> HpackDynamicTableSizeUpdateBuilder<'a> {
180    /// Creates a builder for a dynamic table size value.
181    pub fn new(destination: &'a mut [u8], size: u64) -> Self {
182        Self { destination, size }
183    }
184
185    /// Validates capacity before atomically writing a canonical size update.
186    pub fn build(self) -> Result<&'a [u8], HpackRepresentationBuildError> {
187        let required = canonical_encoded_len(self.size, 0x1f);
188        check_capacity(self.destination.len(), required)?;
189        let representation = &mut self.destination[..required];
190        write_integer(representation, 5, 0x1f, 0x20, self.size);
191        Ok(&*representation)
192    }
193}
194
195/// Failure to build an HPACK header-field representation.
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum HpackRepresentationBuildError {
198    /// An indexed header field used forbidden index zero.
199    IndexedFieldZero,
200
201    /// A literal header field selected indexed-name form with forbidden index zero.
202    LiteralNameIndexZero,
203
204    /// The literal name string could not be built.
205    LiteralName(HpackStringLiteralBuildError),
206    /// The literal value string could not be built.
207    LiteralValue(HpackStringLiteralBuildError),
208    /// The sum of representation component lengths overflows `usize`.
209    TotalLengthOverflow,
210    /// The caller buffer cannot contain the complete representation.
211    BufferTooShort {
212        /// Required bytes.
213        required: usize,
214        /// Available bytes.
215        available: usize,
216    },
217}
218
219impl fmt::Display for HpackRepresentationBuildError {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        match self {
222            Self::IndexedFieldZero => f.write_str("HPACK indexed field index must not be zero"),
223            Self::LiteralNameIndexZero => {
224                f.write_str("HPACK literal indexed name must not use index zero")
225            }
226
227            Self::LiteralName(error) => write!(f, "could not build HPACK literal name: {error}"),
228            Self::LiteralValue(error) => write!(f, "could not build HPACK literal value: {error}"),
229            Self::TotalLengthOverflow => f.write_str("HPACK representation length overflows usize"),
230            Self::BufferTooShort {
231                required,
232                available,
233            } => write!(
234                f,
235                "HPACK representation buffer is too short: need {required} bytes, have {available}"
236            ),
237        }
238    }
239}
240
241fn check_capacity(available: usize, required: usize) -> Result<(), HpackRepresentationBuildError> {
242    if available < required {
243        return Err(HpackRepresentationBuildError::BufferTooShort {
244            required,
245            available,
246        });
247    }
248    Ok(())
249}
250
251fn string_info(value: HpackLiteralValue<'_>) -> Result<(usize, u64), HpackStringLiteralBuildError> {
252    let encoded_length = u64::try_from(value.encoded_bytes.len()).map_err(|_| {
253        HpackStringLiteralBuildError::EncodedLengthNotRepresentable {
254            encoded_length: value.encoded_bytes.len(),
255        }
256    })?;
257    let length_length = canonical_encoded_len(encoded_length, 0x7f);
258    let total = length_length.checked_add(value.encoded_bytes.len()).ok_or(
259        HpackStringLiteralBuildError::TotalLengthOverflow {
260            length_length,
261            encoded_length: value.encoded_bytes.len(),
262        },
263    )?;
264    Ok((total, encoded_length))
265}
266
267fn write_string(destination: &mut [u8], value: HpackLiteralValue<'_>, encoded_length: u64) {
268    let length_length = canonical_encoded_len(encoded_length, 0x7f);
269    let (length_destination, payload_destination) = destination.split_at_mut(length_length);
270    write_integer(
271        length_destination,
272        7,
273        0x7f,
274        if value.huffman { 0x80 } else { 0 },
275        encoded_length,
276    );
277    payload_destination.copy_from_slice(value.encoded_bytes);
278}
279
280fn write_integer(
281    destination: &mut [u8],
282    prefix_bits: u8,
283    prefix_mask: u8,
284    high_bits: u8,
285    value: u64,
286) {
287    write_canonical_for_valid_prefix(destination, prefix_bits, prefix_mask, high_bits, value);
288}
289
290const fn prefix_mask(prefix_bits: u8) -> u8 {
291    (1 << prefix_bits) - 1
292}