Skip to main content

micro_h2/hpack/
decode.rs

1//! Decoding a header block.
2//!
3//! The five representations of RFC 7541 section 6, distinguished by the top
4//! bits of the first byte:
5//!
6//! ```text
7//! 1xxxxxxx   indexed header field                    (7-bit prefix)
8//! 01xxxxxx   literal, incremental indexing           (6-bit prefix)
9//! 001xxxxx   dynamic table size update               (5-bit prefix)
10//! 0001xxxx   literal, never indexed                  (4-bit prefix)
11//! 0000xxxx   literal, without indexing               (4-bit prefix)
12//! ```
13//!
14//! The order matters: `0001xxxx` must be tested before `0000xxxx`, and the size
15//! update before both. Getting the prefix lengths wrong misreads the integer
16//! that follows, which then consumes the wrong number of bytes and desynchronises
17//! the rest of the block.
18
19use crate::Error;
20use crate::hpack::dynamic::{DynamicTable, lookup};
21use crate::hpack::huffman;
22use crate::hpack::static_table;
23
24/// The longest header name or value this decoder will materialise.
25///
26/// Bounded because there is no allocator. Exceeding it is an error rather than a
27/// truncation: a silently shortened header value is worse than a failed request.
28pub const MAX_STRING: usize = 512;
29
30/// One decoded header.
31pub struct Header {
32    pub name: heapless::String<MAX_STRING>,
33    pub value: heapless::String<MAX_STRING>,
34}
35
36/// Decodes header blocks, carrying the dynamic table between them.
37///
38/// One decoder per connection, never one per message: the table is connection
39/// state, and a fresh decoder for each response would resolve every index the
40/// server sent against an empty table.
41pub struct Decoder {
42    table: DynamicTable,
43}
44
45impl Decoder {
46    pub fn new(table_size: usize) -> Self {
47        Self {
48            table: DynamicTable::new(table_size),
49        }
50    }
51
52    pub fn table(&self) -> &DynamicTable {
53        &self.table
54    }
55
56    /// Decode a complete header block, calling `on_header` for each field.
57    ///
58    /// A callback rather than a returned collection: a response's headers do not
59    /// all need to be held at once, and the caller usually wants two of them.
60    pub fn decode(
61        &mut self,
62        mut input: &[u8],
63        mut on_header: impl FnMut(&str, &str),
64    ) -> Result<(), Error> {
65        while !input.is_empty() {
66            let first = input[0];
67
68            if first & 0x80 != 0 {
69                // Indexed: name and value both come from a table.
70                let (index, rest) = decode_integer(input, 7)?;
71                input = rest;
72                let (name, value) = lookup(&self.table, index as usize)?;
73                on_header(name, value);
74                continue;
75            }
76
77            if first & 0xe0 == 0x20 {
78                // Dynamic table size update. Not a header, and legal only at the
79                // start of a block, but accepting it anywhere costs nothing and
80                // rejecting a legal stream costs the connection.
81                let (size, rest) = decode_integer(input, 5)?;
82                input = rest;
83                self.table.set_capacity(size as usize);
84                continue;
85            }
86
87            // The three literal forms differ only in what they do to the table.
88            let (prefix, indexing) = if first & 0xc0 == 0x40 {
89                (6, true)
90            } else {
91                // Both `0000xxxx` and `0001xxxx` have a 4-bit prefix and neither
92                // indexes. "Never indexed" additionally means intermediaries must
93                // not index it, which as an endpoint we honour by doing nothing.
94                (4, false)
95            };
96
97            let (index, rest) = decode_integer(input, prefix)?;
98            input = rest;
99
100            let mut name = heapless::String::<MAX_STRING>::new();
101            if index == 0 {
102                input = decode_string(input, &mut name)?;
103            } else {
104                let (existing, _) = lookup(&self.table, index as usize)?;
105                name.push_str(existing).map_err(|_| Error::BufferTooSmall)?;
106            }
107
108            let mut value = heapless::String::<MAX_STRING>::new();
109            input = decode_string(input, &mut value)?;
110
111            on_header(&name, &value);
112            if indexing {
113                self.table.insert(&name, &value);
114            }
115        }
116        Ok(())
117    }
118}
119
120/// RFC 7541 section 5.1: an integer with an `n`-bit prefix, continued in
121/// seven-bit groups when the prefix is all ones.
122///
123/// Returns the value and the remaining input.
124pub fn decode_integer(input: &[u8], prefix_bits: u32) -> Result<(u64, &[u8]), Error> {
125    let mask = (1u64 << prefix_bits) - 1;
126    let first = *input.first().ok_or(Error::Incomplete)? as u64;
127    let value = first & mask;
128    if value < mask {
129        return Ok((value, &input[1..]));
130    }
131
132    let mut value = mask;
133    let mut shift = 0;
134    let mut rest = &input[1..];
135    loop {
136        let byte = *rest.first().ok_or(Error::Incomplete)?;
137        rest = &rest[1..];
138        // Bounded so a hostile encoder cannot spin here, and so the shift cannot
139        // overflow — 64 bits is ten seven-bit groups.
140        if shift > 63 {
141            return Err(Error::Hpack);
142        }
143        value = value
144            .checked_add(((byte & 0x7f) as u64) << shift)
145            .ok_or(Error::Hpack)?;
146        if byte & 0x80 == 0 {
147            return Ok((value, rest));
148        }
149        shift += 7;
150    }
151}
152
153/// RFC 7541 section 5.2: a length-prefixed string, optionally Huffman-coded.
154fn decode_string<'a>(
155    input: &'a [u8],
156    out: &mut heapless::String<MAX_STRING>,
157) -> Result<&'a [u8], Error> {
158    let huffman_coded = input.first().ok_or(Error::Incomplete)? & 0x80 != 0;
159    let (len, rest) = decode_integer(input, 7)?;
160    let len = len as usize;
161    let bytes = rest.get(..len).ok_or(Error::Incomplete)?;
162
163    let mut buffer = [0u8; MAX_STRING];
164    let decoded: &[u8] = if huffman_coded {
165        let n = huffman::decode(bytes, &mut buffer)?;
166        &buffer[..n]
167    } else {
168        bytes
169    };
170
171    let text = core::str::from_utf8(decoded).map_err(|_| Error::Hpack)?;
172    out.push_str(text).map_err(|_| Error::BufferTooSmall)?;
173    Ok(&rest[len..])
174}
175
176/// The static table, re-exported so callers can name indices without reaching
177/// into a sibling module.
178pub use static_table::{DYNAMIC_BASE, ENTRIES as STATIC_ENTRIES};
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    fn hex(text: &str) -> heapless::Vec<u8, 256> {
185        let mut bytes = heapless::Vec::new();
186        for pair in text.as_bytes().chunks(2) {
187            bytes
188                .push(u8::from_str_radix(core::str::from_utf8(pair).unwrap(), 16).unwrap())
189                .unwrap();
190        }
191        bytes
192    }
193
194    /// Collect a block's headers as `name: value` strings.
195    fn decode_all(decoder: &mut Decoder, block: &str) -> heapless::Vec<heapless::String<256>, 16> {
196        let mut headers = heapless::Vec::new();
197        decoder
198            .decode(&hex(block), |name, value| {
199                let mut entry = heapless::String::<256>::new();
200                entry.push_str(name).unwrap();
201                entry.push_str(": ").unwrap();
202                entry.push_str(value).unwrap();
203                headers.push(entry).unwrap();
204            })
205            .unwrap();
206        headers
207    }
208
209    #[test]
210    fn decodes_integers_as_the_rfc_section_5_1_examples_do() {
211        // 10 in a 5-bit prefix fits inline.
212        assert_eq!(decode_integer(&[0x0a], 5).unwrap().0, 10);
213        // 1337 in a 5-bit prefix needs continuation bytes.
214        assert_eq!(decode_integer(&[0x1f, 0x9a, 0x0a], 5).unwrap().0, 1337);
215        // 42 in an 8-bit prefix.
216        assert_eq!(decode_integer(&[0x2a], 8).unwrap().0, 42);
217        // A prefix that is all ones with nothing following is incomplete, not 31.
218        assert_eq!(decode_integer(&[0x1f], 5), Err(Error::Incomplete));
219    }
220
221    /// RFC 7541 Appendix C.3 — three requests on one connection, with the
222    /// dynamic table carried between them. This is the case a per-message
223    /// decoder gets wrong.
224    #[test]
225    fn decodes_the_rfc_appendix_c_3_request_sequence() {
226        let mut decoder = Decoder::new(4096);
227
228        let first = decode_all(&mut decoder, "828684410f7777772e6578616d706c652e636f6d");
229        assert_eq!(first[0].as_str(), ":method: GET");
230        assert_eq!(first[1].as_str(), ":scheme: http");
231        assert_eq!(first[2].as_str(), ":path: /");
232        assert_eq!(first[3].as_str(), ":authority: www.example.com");
233        assert_eq!(decoder.table().len(), 1);
234
235        // The second request references the entry the first one created.
236        let second = decode_all(&mut decoder, "828684be58086e6f2d6361636865");
237        assert_eq!(second[3].as_str(), ":authority: www.example.com");
238        assert_eq!(second[4].as_str(), "cache-control: no-cache");
239        assert_eq!(decoder.table().len(), 2);
240
241        let third = decode_all(
242            &mut decoder,
243            "828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565",
244        );
245        assert_eq!(third[3].as_str(), ":authority: www.example.com");
246        assert_eq!(third[4].as_str(), "custom-key: custom-value");
247        assert_eq!(decoder.table().len(), 3);
248    }
249
250    /// RFC 7541 Appendix C.4 — the same three requests, Huffman-coded.
251    #[test]
252    fn decodes_the_rfc_appendix_c_4_huffman_request_sequence() {
253        let mut decoder = Decoder::new(4096);
254
255        let first = decode_all(&mut decoder, "828684418cf1e3c2e5f23a6ba0ab90f4ff");
256        assert_eq!(first[3].as_str(), ":authority: www.example.com");
257
258        let second = decode_all(&mut decoder, "828684be5886a8eb10649cbf");
259        assert_eq!(second[4].as_str(), "cache-control: no-cache");
260
261        let third = decode_all(
262            &mut decoder,
263            "828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf",
264        );
265        assert_eq!(third[4].as_str(), "custom-key: custom-value");
266    }
267
268    /// RFC 7541 Appendix C.5 — responses with a table small enough to evict,
269    /// which is where an eviction bug turns into wrong headers rather than
270    /// missing ones.
271    #[test]
272    fn decodes_the_rfc_appendix_c_5_response_sequence_with_eviction() {
273        let mut decoder = Decoder::new(256);
274
275        let first = decode_all(
276            &mut decoder,
277            "4803333032580770726976617465611d4d6f6e2c203231204f637420323031332032303a31333a323120474d546e1768747470733a2f2f7777772e6578616d706c652e636f6d",
278        );
279        assert_eq!(first[0].as_str(), ":status: 302");
280        assert_eq!(first[3].as_str(), "location: https://www.example.com");
281
282        let second = decode_all(&mut decoder, "4803333037c1c0bf");
283        assert_eq!(second[0].as_str(), ":status: 307");
284        assert_eq!(second[1].as_str(), "cache-control: private");
285        assert_eq!(second[3].as_str(), "location: https://www.example.com");
286
287        let third = decode_all(
288            &mut decoder,
289            "88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31",
290        );
291        assert_eq!(third[0].as_str(), ":status: 200");
292        assert_eq!(third[4].as_str(), "content-encoding: gzip");
293        assert!(
294            third[5]
295                .as_str()
296                .starts_with("set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU")
297        );
298    }
299
300    #[test]
301    fn a_dynamic_table_size_update_is_applied_and_is_not_a_header() {
302        let mut decoder = Decoder::new(4096);
303        // 0x20 | 0 => set capacity to 0, then an indexed :method GET.
304        let headers = decode_all(&mut decoder, "2082");
305        assert_eq!(headers.len(), 1);
306        assert_eq!(headers[0].as_str(), ":method: GET");
307        assert_eq!(decoder.table().capacity(), 0);
308    }
309
310    #[test]
311    fn an_index_the_peer_never_defined_is_an_error() {
312        // The failure this prevents is subtle: carrying on would shift every
313        // later index, so headers would decode as *other headers* for the rest
314        // of the connection.
315        let mut decoder = Decoder::new(4096);
316        assert_eq!(decoder.decode(&hex("be"), |_, _| {}), Err(Error::Hpack));
317        // ...and a static index past the end of the table, likewise.
318        assert_eq!(decoder.decode(&hex("ff00"), |_, _| {}), Err(Error::Hpack));
319    }
320
321    #[test]
322    fn a_truncated_block_is_incomplete_rather_than_a_short_header() {
323        let mut decoder = Decoder::new(4096);
324        // A literal whose declared length runs past the end of the block.
325        assert_eq!(
326            decoder.decode(&hex("400a637573746f6d"), |_, _| {}),
327            Err(Error::Incomplete)
328        );
329    }
330
331    #[test]
332    fn never_indexed_headers_do_not_enter_the_table() {
333        let mut decoder = Decoder::new(4096);
334        // 0x10 => literal never indexed, new name.
335        let headers = decode_all(&mut decoder, "10012d0131");
336        assert_eq!(headers[0].as_str(), "-: 1");
337        assert!(
338            decoder.table().is_empty(),
339            "a never-indexed header must not be remembered"
340        );
341    }
342}