Skip to main content

vibeio_http/h2/hpack/
decode.rs

1//! HPACK decoder (RFC 7541 Section 6).
2//!
3//! Decodes a complete header block (HEADERS plus any CONTINUATION frames,
4//! already assembled by the caller) into a header list, maintaining the
5//! dynamic table across blocks.
6
7use super::{
8    integer, string,
9    table::{Header, Table},
10    HpackError,
11};
12use bytes::Bytes;
13
14/// The representation prefixes defined in RFC 7541 Section 6.
15const INDEXED: u8 = 0b1000_0000;
16const LITERAL_WITH_INDEXING: u8 = 0b0100_0000;
17const LITERAL_WITHOUT_INDEXING: u8 = 0b1111_0000;
18const LITERAL_NEVER_INDEXED: u8 = 0b0001_0000;
19const SIZE_UPDATE_MASK: u8 = 0b1110_0000;
20const SIZE_UPDATE: u8 = 0b0010_0000;
21
22/// Decodes HPACK header blocks.
23#[derive(Debug)]
24pub struct Decoder {
25    /// Static + dynamic header table.
26    table: Table,
27    /// The maximum table size allowed by the protocol
28    /// (`SETTINGS_HEADER_TABLE_SIZE`). Size updates above this are an
29    /// error.
30    max_table_size: usize,
31    /// A size update queued by the protocol layer (SETTINGS) and applied
32    /// at the start of the next decode.
33    queued_size_update: Option<usize>,
34    /// Cap on the total size of a decoded header list (sum of name and
35    /// value octets). Exceeding it is an error.
36    max_header_list_size: usize,
37}
38
39impl Decoder {
40    /// Creates a decoder with the given protocol maximum table size
41    /// (RFC 7541 Section 4.2: 4096 by default).
42    #[inline]
43    pub fn new(max_table_size: usize) -> Self {
44        Decoder {
45            table: Table::with_max_size(max_table_size),
46            max_table_size,
47            queued_size_update: None,
48            max_header_list_size: usize::MAX,
49        }
50    }
51
52    /// Queues a protocol-level table size update (from SETTINGS) to be
53    /// applied at the start of the next header block.
54    #[inline]
55    pub fn queue_size_update(&mut self, size: usize) {
56        self.queued_size_update = Some(match self.queued_size_update {
57            Some(current) => current.max(size),
58            None => size,
59        });
60    }
61
62    /// Sets the maximum size of a decoded header list. Headers blocks
63    /// whose cumulative name+value octets exceed this are rejected.
64    #[inline]
65    pub fn set_max_header_list_size(&mut self, size: usize) {
66        self.max_header_list_size = size;
67    }
68
69    #[cfg(test)]
70    #[inline]
71    pub(crate) fn table(&self) -> &Table {
72        &self.table
73    }
74
75    /// Decodes a complete header block. The dynamic table is updated
76    /// across calls.
77    #[inline]
78    pub fn decode(&mut self, buf: &[u8], list_size: &mut usize) -> Result<Vec<Header>, HpackError> {
79        if let Some(size) = self.queued_size_update.take() {
80            self.max_table_size = size;
81            // The table capacity itself is driven by size-update
82            // representations in the wire; the queued value only raises
83            // the protocol cap.
84        }
85
86        let mut off = 0usize;
87        let mut headers = Vec::new();
88        // Size updates must precede any header field representation
89        // (RFC 7541 Section 6.3).
90        let mut can_resize = true;
91
92        while off < buf.len() {
93            let byte = buf[off];
94            // The first octet carries the representation prefix and part
95            // of the index/size; `integer::decode` reads on from here.
96            off += 1;
97            let rep = Representation::load(byte)?;
98            match rep {
99                Representation::Indexed => {
100                    can_resize = false;
101                    let index = integer::decode(buf, &mut off, 7, byte)? as usize;
102                    let entry = self.table.get(index).ok_or(HpackError::InvalidIndex)?;
103                    *list_size += entry.name().len() + entry.value().len();
104                    if *list_size > self.max_header_list_size {
105                        return Err(HpackError::HeaderListTooLarge);
106                    }
107                    headers.push(entry);
108                }
109                Representation::LiteralWithIndexing
110                | Representation::LiteralWithoutIndexing
111                | Representation::LiteralNeverIndexed => {
112                    can_resize = false;
113                    let index = integer::decode(
114                        buf,
115                        &mut off,
116                        if rep == Representation::LiteralWithIndexing {
117                            6
118                        } else {
119                            4
120                        },
121                        byte,
122                    )? as usize;
123
124                    // Name: from the table when index > 0, else literal.
125                    let name = if index == 0 {
126                        let (_, name) = string::decode(buf, &mut off, self.max_header_list_size)?;
127                        Bytes::from(name)
128                    } else {
129                        let entry = self.table.get(index).ok_or(HpackError::InvalidIndex)?;
130                        Bytes::copy_from_slice(entry.name())
131                    };
132
133                    let (_, value) = string::decode(buf, &mut off, self.max_header_list_size)?;
134
135                    *list_size += name.len() + value.len();
136                    if *list_size > self.max_header_list_size {
137                        return Err(HpackError::HeaderListTooLarge);
138                    }
139
140                    let header = Header::new(name, value);
141                    if rep == Representation::LiteralWithIndexing {
142                        self.table.add(header.clone());
143                    }
144                    headers.push(header);
145                }
146                Representation::SizeUpdate => {
147                    if !can_resize {
148                        return Err(HpackError::InvalidMaxSize);
149                    }
150                    let size = integer::decode(buf, &mut off, 5, byte)? as usize;
151                    if size > self.max_table_size {
152                        return Err(HpackError::InvalidMaxSize);
153                    }
154                    self.table.set_max_size(size);
155                }
156            }
157        }
158
159        Ok(headers)
160    }
161}
162
163impl Default for Decoder {
164    #[inline]
165    fn default() -> Self {
166        Decoder::new(4096)
167    }
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171enum Representation {
172    Indexed,
173    LiteralWithIndexing,
174    LiteralWithoutIndexing,
175    LiteralNeverIndexed,
176    SizeUpdate,
177}
178
179impl Representation {
180    #[inline]
181    fn load(byte: u8) -> Result<Representation, HpackError> {
182        if byte & INDEXED == INDEXED {
183            Ok(Representation::Indexed)
184        } else if byte & LITERAL_WITH_INDEXING == LITERAL_WITH_INDEXING {
185            Ok(Representation::LiteralWithIndexing)
186        } else if byte & LITERAL_WITHOUT_INDEXING == 0 {
187            Ok(Representation::LiteralWithoutIndexing)
188        } else if byte & LITERAL_WITHOUT_INDEXING == LITERAL_NEVER_INDEXED {
189            Ok(Representation::LiteralNeverIndexed)
190        } else if byte & SIZE_UPDATE_MASK == SIZE_UPDATE {
191            Ok(Representation::SizeUpdate)
192        } else {
193            Err(HpackError::InvalidRepresentation)
194        }
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[inline]
203    fn decode_one(wire: &[u8]) -> Vec<(String, String)> {
204        let mut decoder = Decoder::new(4096);
205        decoder
206            .decode(wire, &mut 0)
207            .unwrap()
208            .into_iter()
209            .map(|h| {
210                (
211                    String::from_utf8(h.name().to_vec()).unwrap(),
212                    String::from_utf8(h.value().to_vec()).unwrap(),
213                )
214            })
215            .collect()
216    }
217
218    /// RFC 7541 C.2.1: literal with incremental indexing, new name.
219    #[test]
220    fn literal_new_name_indexed() {
221        let wire = [
222            0x40, 0x0a, b'c', b'u', b's', b't', b'o', b'm', b'-', b'k', b'e', b'y', 0x0d, b'c',
223            b'u', b's', b't', b'o', b'm', b'-', b'h', b'e', b'a', b'd', b'e', b'r',
224        ];
225        assert_eq!(
226            decode_one(&wire),
227            vec![("custom-key".into(), "custom-header".into())]
228        );
229    }
230
231    /// RFC 7541 C.2.2: literal without indexing, indexed name.
232    #[test]
233    fn literal_without_indexing_indexed_name() {
234        let wire = [
235            0x04, 0x0c, b'/', b's', b'a', b'm', b'p', b'l', b'e', b'/', b'p', b'a', b't', b'h',
236        ];
237        assert_eq!(
238            decode_one(&wire),
239            vec![(":path".into(), "/sample/path".into())]
240        );
241    }
242
243    /// RFC 7541 C.2.3: literal never indexed, new name.
244    #[test]
245    fn literal_never_indexed() {
246        let wire = [
247            0x10, 0x08, b'p', b'a', b's', b's', b'w', b'o', b'r', b'd', 0x06, b's', b'e', b'c',
248            b'r', b'e', b't',
249        ];
250        assert_eq!(
251            decode_one(&wire),
252            vec![("password".into(), "secret".into())]
253        );
254    }
255
256    /// RFC 7541 C.2.4: indexed header field.
257    #[test]
258    fn indexed() {
259        assert_eq!(decode_one(&[0x82]), vec![(":method".into(), "GET".into())]);
260    }
261
262    /// RFC 7541 C.2.4: table state after indexed + literal.
263    #[test]
264    fn table_state_after_representations() {
265        let mut decoder = Decoder::new(4096);
266        let _ = decoder.decode(&[0x82], &mut 0).unwrap();
267        assert_eq!(decoder.table().dynamic_len(), 0);
268        // 0x40 + custom-key + custom-header adds an entry.
269        let wire = [
270            0x40, 0x0a, b'c', b'u', b's', b't', b'o', b'm', b'-', b'k', b'e', b'y', 0x0d, b'c',
271            b'u', b's', b't', b'o', b'm', b'-', b'h', b'e', b'a', b'd', b'e', b'r',
272        ];
273        let _ = decoder.decode(&wire, &mut 0).unwrap();
274        assert_eq!(decoder.table().dynamic_len(), 1);
275        assert_eq!(decoder.table().get(62).unwrap().name(), b"custom-key");
276    }
277
278    /// RFC 7541 C.3.1: full request block without Huffman.
279    #[test]
280    fn c3_1_request_without_huffman() {
281        // 8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d
282        let wire = hex_to_bytes("828684410f7777772e6578616d706c652e636f6d");
283        assert_eq!(
284            decode_one(&wire),
285            vec![
286                (":method".into(), "GET".into()),
287                (":scheme".into(), "http".into()),
288                (":path".into(), "/".into()),
289                (":authority".into(), "www.example.com".into()),
290            ]
291        );
292    }
293
294    /// RFC 7541 C.4.1: full request block with Huffman.
295    #[test]
296    fn c4_1_request_with_huffman() {
297        let wire = hex_to_bytes("828684418cf1e3c2e5f23a6ba0ab90f4ff");
298        assert_eq!(
299            decode_one(&wire),
300            vec![
301                (":method".into(), "GET".into()),
302                (":scheme".into(), "http".into()),
303                (":path".into(), "/".into()),
304                (":authority".into(), "www.example.com".into()),
305            ]
306        );
307    }
308
309    /// RFC 7541 C.3.2/C.3.3: second/third request of the C.3 connection
310    /// (no Huffman); C.4.2/C.4.3: the same for the C.4 connection
311    /// (Huffman). Each connection starts with its C.x.1 block.
312    #[test]
313    fn c3_c4_sequential_requests() {
314        let block = |d: &mut Decoder, wire: &str| {
315            d.decode(&hex_to_bytes(wire), &mut 0)
316                .unwrap()
317                .into_iter()
318                .map(|h| {
319                    (
320                        String::from_utf8(h.name().to_vec()).unwrap(),
321                        String::from_utf8(h.value().to_vec()).unwrap(),
322                    )
323                })
324                .collect::<Vec<_>>()
325        };
326
327        let mut decoder = Decoder::new(4096);
328        let _ = block(&mut decoder, "828684410f7777772e6578616d706c652e636f6d");
329
330        // C.3.2: references :authority: www.example.com at dynamic 62.
331        assert_eq!(
332            block(&mut decoder, "828684be58086e6f2d6361636865"),
333            vec![
334                (":method".into(), "GET".into()),
335                (":scheme".into(), "http".into()),
336                (":path".into(), "/".into()),
337                (":authority".into(), "www.example.com".into()),
338                ("cache-control".into(), "no-cache".into()),
339            ]
340        );
341
342        // C.3.3: references cache-control: no-cache at 62, :authority at
343        // 63, then a literal new name.
344        assert_eq!(
345            block(
346                &mut decoder,
347                "828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565",
348            ),
349            vec![
350                (":method".into(), "GET".into()),
351                (":scheme".into(), "https".into()),
352                (":path".into(), "/index.html".into()),
353                (":authority".into(), "www.example.com".into()),
354                ("custom-key".into(), "custom-value".into()),
355            ]
356        );
357
358        let mut decoder = Decoder::new(4096);
359        let _ = block(&mut decoder, "828684418cf1e3c2e5f23a6ba0ab90f4ff");
360
361        // C.4.2: references :authority: www.example.com at dynamic 62.
362        assert_eq!(
363            block(&mut decoder, "828684be5886a8eb10649cbf"),
364            vec![
365                (":method".into(), "GET".into()),
366                (":scheme".into(), "http".into()),
367                (":path".into(), "/".into()),
368                (":authority".into(), "www.example.com".into()),
369                ("cache-control".into(), "no-cache".into()),
370            ]
371        );
372
373        // C.4.3: references :authority at 63, then a literal new name.
374        assert_eq!(
375            block(
376                &mut decoder,
377                "828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf"
378            ),
379            vec![
380                (":method".into(), "GET".into()),
381                (":scheme".into(), "https".into()),
382                (":path".into(), "/index.html".into()),
383                (":authority".into(), "www.example.com".into()),
384                ("custom-key".into(), "custom-value".into()),
385            ]
386        );
387    }
388
389    /// RFC 7541 C.5: response walkthrough without Huffman, dynamic
390    /// table capped at 256 octets so evictions occur.
391    #[test]
392    fn c5_response_walkthrough() {
393        let mut decoder = Decoder::new(256);
394        let block = |d: &mut Decoder, wire: &str| {
395            d.decode(&hex_to_bytes(wire), &mut 0)
396                .unwrap()
397                .into_iter()
398                .map(|h| {
399                    (
400                        String::from_utf8(h.name().to_vec()).unwrap(),
401                        String::from_utf8(h.value().to_vec()).unwrap(),
402                    )
403                })
404                .collect::<Vec<_>>()
405        };
406
407        // C.5.1: first response.
408        let first = vec![
409            (":status".into(), "302".into()),
410            ("cache-control".into(), "private".into()),
411            ("date".into(), "Mon, 21 Oct 2013 20:13:21 GMT".into()),
412            ("location".into(), "https://www.example.com".into()),
413        ];
414        assert_eq!(
415            block(&mut decoder, "4803333032580770726976617465611d4d6f6e2c203231204f637420323031332032303a31333a323120474d546e1768747470733a2f2f7777772e6578616d706c652e636f6d"),
416            first
417        );
418        assert_eq!(decoder.table().dynamic_len(), 4);
419
420        // C.5.2: second response; :status: 302 evicted to make room.
421        let second = vec![
422            (":status".into(), "307".into()),
423            ("cache-control".into(), "private".into()),
424            ("date".into(), "Mon, 21 Oct 2013 20:13:21 GMT".into()),
425            ("location".into(), "https://www.example.com".into()),
426        ];
427        assert_eq!(block(&mut decoder, "4803333037c1c0bf"), second);
428        assert_eq!(decoder.table().dynamic_len(), 4);
429
430        // C.5.3: third response; several entries evicted.
431        let third = vec![
432            (":status".into(), "200".into()),
433            ("cache-control".into(), "private".into()),
434            ("date".into(), "Mon, 21 Oct 2013 20:13:22 GMT".into()),
435            ("location".into(), "https://www.example.com".into()),
436            ("content-encoding".into(), "gzip".into()),
437            (
438                "set-cookie".into(),
439                "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1".into(),
440            ),
441        ];
442        assert_eq!(
443            block(
444                &mut decoder,
445                "88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31",
446            ),
447            third
448        );
449        assert_eq!(decoder.table().dynamic_len(), 3);
450    }
451
452    /// RFC 7541 C.6: response walkthrough with Huffman, same table
453    /// dynamics as C.5.
454    #[test]
455    fn c6_response_walkthrough() {
456        let mut decoder = Decoder::new(256);
457        let block = |d: &mut Decoder, wire: &str| {
458            d.decode(&hex_to_bytes(wire), &mut 0)
459                .unwrap()
460                .into_iter()
461                .map(|h| {
462                    (
463                        String::from_utf8(h.name().to_vec()).unwrap(),
464                        String::from_utf8(h.value().to_vec()).unwrap(),
465                    )
466                })
467                .collect::<Vec<_>>()
468        };
469
470        let first = vec![
471            (":status".into(), "302".into()),
472            ("cache-control".into(), "private".into()),
473            ("date".into(), "Mon, 21 Oct 2013 20:13:21 GMT".into()),
474            ("location".into(), "https://www.example.com".into()),
475        ];
476        assert_eq!(
477            block(&mut decoder, "488264025885aec3771a4b6196d07abe941054d444a8200595040b8166e082a62d1bff6e919d29ad171863c78f0b97c8e9ae82ae43d3"),
478            first
479        );
480        assert_eq!(decoder.table().dynamic_len(), 4);
481
482        let second = vec![
483            (":status".into(), "307".into()),
484            ("cache-control".into(), "private".into()),
485            ("date".into(), "Mon, 21 Oct 2013 20:13:21 GMT".into()),
486            ("location".into(), "https://www.example.com".into()),
487        ];
488        assert_eq!(block(&mut decoder, "4883640effc1c0bf"), second);
489        assert_eq!(decoder.table().dynamic_len(), 4);
490
491        let third = vec![
492            (":status".into(), "200".into()),
493            ("cache-control".into(), "private".into()),
494            ("date".into(), "Mon, 21 Oct 2013 20:13:22 GMT".into()),
495            ("location".into(), "https://www.example.com".into()),
496            ("content-encoding".into(), "gzip".into()),
497            (
498                "set-cookie".into(),
499                "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1".into(),
500            ),
501        ];
502        assert_eq!(
503            block(
504                &mut decoder,
505                "88c16196d07abe941054d444a8200595040b8166e084a62d1bffc05a839bd9ab77ad94e7821dd7f2e6c7b335dfdfcd5b3960d5af27087f3672c1ab270fb5291f9587316065c003ed4ee5b1063d5007",
506            ),
507            third
508        );
509        assert_eq!(decoder.table().dynamic_len(), 3);
510    }
511
512    #[test]
513    fn size_update_at_start() {
514        let mut decoder = Decoder::new(4096);
515        // 0x3f + continuation: size update to 4096.
516        let wire = [0x3f, 0xe1, 0x1f, 0x82];
517        let out = decoder.decode(&wire, &mut 0).unwrap();
518        assert_eq!(out.len(), 1);
519        assert_eq!(decoder.table().max_size(), 4096);
520    }
521
522    #[test]
523    fn size_update_after_field_rejected() {
524        let mut decoder = Decoder::new(4096);
525        // Indexed (0x82) then size update (0x20).
526        assert!(matches!(
527            decoder.decode(&[0x82, 0x20], &mut 0),
528            Err(HpackError::InvalidMaxSize)
529        ));
530    }
531
532    #[test]
533    fn size_update_over_protocol_max_rejected() {
534        let mut decoder = Decoder::new(128);
535        // Size update to 4096 while the protocol allows only 128.
536        assert!(matches!(
537            decoder.decode(&[0x3f, 0xe1, 0x1f], &mut 0),
538            Err(HpackError::InvalidMaxSize)
539        ));
540    }
541
542    #[test]
543    fn invalid_table_index_rejected() {
544        // Indexed field with index 0.
545        assert!(matches!(decode(&[0x80]), Err(HpackError::InvalidIndex)));
546    }
547
548    #[test]
549    fn index_out_of_range_rejected() {
550        let mut decoder = Decoder::new(4096);
551        // Index 200 (0x7f is only 127; use 0xc8 + continuation for 200).
552        assert!(matches!(
553            decoder.decode(&[0x7f, 0x49], &mut 0),
554            Err(HpackError::InvalidIndex)
555        ));
556    }
557
558    #[test]
559    fn header_list_size_capped() {
560        let mut decoder = Decoder::new(4096);
561        decoder.set_max_header_list_size(10);
562        // name "x" (1) + value of 10 octets: each string is within the
563        // cap, but the list totals 11.
564        let wire = [
565            0x40, 0x01, b'x', 0x0a, b'y', b'y', b'y', b'y', b'y', b'y', b'y', b'y', b'y', b'y',
566        ];
567        assert!(matches!(
568            decoder.decode(&wire, &mut 0),
569            Err(HpackError::HeaderListTooLarge)
570        ));
571    }
572
573    #[test]
574    fn small_size_update_ok() {
575        // 0x30 = size update (001xxxxx) with value 16; every octet value
576        // maps to exactly one representation, so there is no invalid
577        // byte to test against.
578        let mut decoder = Decoder::new(4096);
579        decoder.decode(&[0x30], &mut 0).unwrap();
580        assert_eq!(decoder.table().max_size(), 16);
581    }
582
583    fn decode(wire: &[u8]) -> Result<Vec<Header>, HpackError> {
584        let mut decoder = Decoder::new(4096);
585        decoder.decode(wire, &mut 0)
586    }
587
588    fn hex_to_bytes(hex: &str) -> Vec<u8> {
589        hex.as_bytes()
590            .chunks_exact(2)
591            .map(|pair| {
592                let hi = (pair[0] as char).to_digit(16).unwrap() as u8;
593                let lo = (pair[1] as char).to_digit(16).unwrap() as u8;
594                (hi << 4) | lo
595            })
596            .collect()
597    }
598}