Skip to main content

stackforge_core/layer/http2/
hpack.rs

1//! HPACK header compression for HTTP/2 (RFC 7541).
2//!
3//! Implements the HPACK header compression algorithm used in HTTP/2 HEADERS frames.
4//! Includes:
5//! - Static table (61 entries per RFC 7541 Appendix A)
6//! - Dynamic table with eviction
7//! - Integer encoding/decoding with N-bit prefix
8//! - String literal encoding/decoding
9//! - Huffman encoding/decoding (RFC 7541 Appendix B)
10//! - `HpackDecoder` and `HpackEncoder` types
11
12// ============================================================================
13// Static Table (RFC 7541 Appendix A)
14// ============================================================================
15
16/// Static HPACK table from RFC 7541 Appendix A.
17/// 61 entries, 1-indexed in the spec (index 0 here = spec index 1).
18pub const STATIC_TABLE: &[(&str, &str)] = &[
19    (":authority", ""),
20    (":method", "GET"),
21    (":method", "POST"),
22    (":path", "/"),
23    (":path", "/index.html"),
24    (":scheme", "http"),
25    (":scheme", "https"),
26    (":status", "200"),
27    (":status", "204"),
28    (":status", "206"),
29    (":status", "304"),
30    (":status", "400"),
31    (":status", "404"),
32    (":status", "500"),
33    ("accept-charset", ""),
34    ("accept-encoding", "gzip, deflate"),
35    ("accept-language", ""),
36    ("accept-ranges", ""),
37    ("accept", ""),
38    ("access-control-allow-origin", ""),
39    ("age", ""),
40    ("allow", ""),
41    ("authorization", ""),
42    ("cache-control", ""),
43    ("content-disposition", ""),
44    ("content-encoding", ""),
45    ("content-language", ""),
46    ("content-length", ""),
47    ("content-location", ""),
48    ("content-range", ""),
49    ("content-type", ""),
50    ("cookie", ""),
51    ("date", ""),
52    ("etag", ""),
53    ("expect", ""),
54    ("expires", ""),
55    ("from", ""),
56    ("host", ""),
57    ("if-match", ""),
58    ("if-modified-since", ""),
59    ("if-none-match", ""),
60    ("if-range", ""),
61    ("if-unmodified-since", ""),
62    ("last-modified", ""),
63    ("link", ""),
64    ("location", ""),
65    ("max-forwards", ""),
66    ("proxy-authenticate", ""),
67    ("proxy-authorization", ""),
68    ("range", ""),
69    ("referer", ""),
70    ("refresh", ""),
71    ("retry-after", ""),
72    ("server", ""),
73    ("set-cookie", ""),
74    ("strict-transport-security", ""),
75    ("transfer-encoding", ""),
76    ("user-agent", ""),
77    ("vary", ""),
78    ("via", ""),
79    ("www-authenticate", ""),
80];
81
82// ============================================================================
83// Huffman Table (RFC 7541 Appendix B)
84// All 256 byte values + EOS (symbol 256)
85// Each entry is (code: u32, nbits: u8)
86// ============================================================================
87
88/// Full Huffman code table from RFC 7541 Appendix B.
89/// Index is the symbol value (0-255 for bytes, 256 for EOS).
90/// Each entry is (code: u32, nbits: u8).
91#[rustfmt::skip]
92pub const HUFFMAN_TABLE: &[(u32, u8)] = &[
93    (0x1ff8, 13),      // 0
94    (0x7fffd8, 23),    // 1
95    (0xfffffe2, 28),   // 2
96    (0xfffffe3, 28),   // 3
97    (0xfffffe4, 28),   // 4
98    (0xfffffe5, 28),   // 5
99    (0xfffffe6, 28),   // 6
100    (0xfffffe7, 28),   // 7
101    (0xfffffe8, 28),   // 8
102    (0xffffea, 24),    // 9
103    (0x3ffffffc, 30),  // 10
104    (0xfffffe9, 28),   // 11
105    (0xfffffea, 28),   // 12
106    (0x3ffffffd, 30),  // 13
107    (0xfffffeb, 28),   // 14
108    (0xfffffec, 28),   // 15
109    (0xfffffed, 28),   // 16
110    (0xfffffee, 28),   // 17
111    (0xfffffef, 28),   // 18
112    (0xffffff0, 28),   // 19
113    (0xffffff1, 28),   // 20
114    (0xffffff2, 28),   // 21
115    (0x3ffffffe, 30),  // 22
116    (0xffffff3, 28),   // 23
117    (0xffffff4, 28),   // 24
118    (0xffffff5, 28),   // 25
119    (0xffffff6, 28),   // 26
120    (0xffffff7, 28),   // 27
121    (0xffffff8, 28),   // 28
122    (0xffffff9, 28),   // 29
123    (0xffffffa, 28),   // 30
124    (0xffffffb, 28),   // 31
125    (0x14, 6),         // 32 ' '
126    (0x3f8, 10),       // 33 '!'
127    (0x3f9, 10),       // 34 '"'
128    (0xffa, 12),       // 35 '#'
129    (0x1ff9, 13),      // 36 '$'
130    (0x15, 6),         // 37 '%'
131    (0xf8, 8),         // 38 '&'
132    (0x7fa, 11),       // 39 '\''
133    (0x3fa, 10),       // 40 '('
134    (0x3fb, 10),       // 41 ')'
135    (0xf9, 8),         // 42 '*'
136    (0x7fb, 11),       // 43 '+'
137    (0xfa, 8),         // 44 ','
138    (0x16, 6),         // 45 '-'
139    (0x17, 6),         // 46 '.'
140    (0x18, 6),         // 47 '/'
141    (0x00, 5),         // 48 '0'
142    (0x01, 5),         // 49 '1'
143    (0x02, 5),         // 50 '2'
144    (0x19, 6),         // 51 '3'
145    (0x1a, 6),         // 52 '4'
146    (0x1b, 6),         // 53 '5'
147    (0x1c, 6),         // 54 '6'
148    (0x1d, 6),         // 55 '7'
149    (0x1e, 6),         // 56 '8'
150    (0x1f, 6),         // 57 '9'
151    (0x5c, 7),         // 58 ':'
152    (0xfb, 8),         // 59 ';'
153    (0x7ffc, 15),      // 60 '<'
154    (0x20, 6),         // 61 '='
155    (0xffb, 12),       // 62 '>'
156    (0x3fc, 10),       // 63 '?'
157    (0x1ffa, 13),      // 64 '@'
158    (0x21, 6),         // 65 'A'
159    (0x5d, 7),         // 66 'B'
160    (0x5e, 7),         // 67 'C'
161    (0x5f, 7),         // 68 'D'
162    (0x60, 7),         // 69 'E'
163    (0x61, 7),         // 70 'F'
164    (0x62, 7),         // 71 'G'
165    (0x63, 7),         // 72 'H'
166    (0x64, 7),         // 73 'I'
167    (0x65, 7),         // 74 'J'
168    (0x66, 7),         // 75 'K'
169    (0x67, 7),         // 76 'L'
170    (0x68, 7),         // 77 'M'
171    (0x69, 7),         // 78 'N'
172    (0x6a, 7),         // 79 'O'
173    (0x6b, 7),         // 80 'P'
174    (0x6c, 7),         // 81 'Q'
175    (0x6d, 7),         // 82 'R'
176    (0x6e, 7),         // 83 'S'
177    (0x6f, 7),         // 84 'T'
178    (0x70, 7),         // 85 'U'
179    (0x71, 7),         // 86 'V'
180    (0x72, 7),         // 87 'W'
181    (0xfc, 8),         // 88 'X'
182    (0x73, 7),         // 89 'Y'
183    (0xfd, 8),         // 90 'Z'
184    (0x1ffb, 13),      // 91 '['
185    (0x7fff0, 19),     // 92 '\\'
186    (0x1ffc, 13),      // 93 ']'
187    (0x3ffc, 14),      // 94 '^'
188    (0x22, 6),         // 95 '_'
189    (0x7ffd, 15),      // 96 '`'
190    (0x03, 5),         // 97 'a'
191    (0x23, 6),         // 98 'b'
192    (0x04, 5),         // 99 'c'
193    (0x24, 6),         // 100 'd'
194    (0x05, 5),         // 101 'e'
195    (0x25, 6),         // 102 'f'
196    (0x26, 6),         // 103 'g'
197    (0x27, 6),         // 104 'h'
198    (0x06, 5),         // 105 'i'
199    (0x74, 7),         // 106 'j'
200    (0x75, 7),         // 107 'k'
201    (0x28, 6),         // 108 'l'
202    (0x29, 6),         // 109 'm'
203    (0x2a, 6),         // 110 'n'
204    (0x07, 5),         // 111 'o'
205    (0x2b, 6),         // 112 'p'
206    (0x76, 7),         // 113 'q'
207    (0x2c, 6),         // 114 'r'
208    (0x08, 5),         // 115 's'
209    (0x09, 5),         // 116 't'
210    (0x2d, 6),         // 117 'u'
211    (0x77, 7),         // 118 'v'
212    (0x78, 7),         // 119 'w'
213    (0x79, 7),         // 120 'x'
214    (0x7a, 7),         // 121 'y'
215    (0x7b, 7),         // 122 'z'
216    (0x7ffe, 15),      // 123 '{'
217    (0x7fc, 11),       // 124 '|'
218    (0x3ffd, 14),      // 125 '}'
219    (0x1ffd, 13),      // 126 '~'
220    (0xffffffc, 28),   // 127
221    (0xfffe6, 20),     // 128
222    (0x3fffd2, 22),    // 129
223    (0xfffe7, 20),     // 130
224    (0xfffe8, 20),     // 131
225    (0x3fffd3, 22),    // 132
226    (0x3fffd4, 22),    // 133
227    (0x3fffd5, 22),    // 134
228    (0x7fffd9, 23),    // 135
229    (0x3fffd6, 22),    // 136
230    (0x7fffda, 23),    // 137
231    (0x7fffdb, 23),    // 138
232    (0x7fffdc, 23),    // 139
233    (0x7fffdd, 23),    // 140
234    (0x7fffde, 23),    // 141
235    (0xffffeb, 24),    // 142
236    (0x7fffdf, 23),    // 143
237    (0xffffec, 24),    // 144
238    (0xffffed, 24),    // 145
239    (0x3fffd7, 22),    // 146
240    (0x7fffe0, 23),    // 147
241    (0xffffee, 24),    // 148
242    (0x7fffe1, 23),    // 149
243    (0x7fffe2, 23),    // 150
244    (0x7fffe3, 23),    // 151
245    (0x7fffe4, 23),    // 152
246    (0x1fffdc, 21),    // 153
247    (0x3fffd8, 22),    // 154
248    (0x7fffe5, 23),    // 155
249    (0x3fffd9, 22),    // 156
250    (0x7fffe6, 23),    // 157
251    (0x7fffe7, 23),    // 158
252    (0xffffef, 24),    // 159
253    (0x3fffda, 22),    // 160
254    (0x1fffdd, 21),    // 161
255    (0xfffe9, 20),     // 162
256    (0x3fffdb, 22),    // 163
257    (0x3fffdc, 22),    // 164
258    (0x7fffe8, 23),    // 165
259    (0x7fffe9, 23),    // 166
260    (0x1fffde, 21),    // 167
261    (0x7fffea, 23),    // 168
262    (0x3fffdd, 22),    // 169
263    (0x3fffde, 22),    // 170
264    (0xfffff0, 24),    // 171
265    (0x1fffdf, 21),    // 172
266    (0x3fffdf, 22),    // 173
267    (0x7fffeb, 23),    // 174
268    (0x7fffec, 23),    // 175
269    (0x1fffe0, 21),    // 176
270    (0x1fffe1, 21),    // 177
271    (0x3fffe0, 22),    // 178
272    (0x1fffe2, 21),    // 179
273    (0x7fffed, 23),    // 180
274    (0x3fffe1, 22),    // 181
275    (0x7fffee, 23),    // 182
276    (0x7fffef, 23),    // 183
277    (0xfffea, 20),     // 184
278    (0x3fffe2, 22),    // 185
279    (0x3fffe3, 22),    // 186
280    (0x3fffe4, 22),    // 187
281    (0x7ffff0, 23),    // 188
282    (0x3fffe5, 22),    // 189
283    (0x3fffe6, 22),    // 190
284    (0x7ffff1, 23),    // 191
285    (0x3ffffe0, 26),   // 192
286    (0x3ffffe1, 26),   // 193
287    (0xfffeb, 20),     // 194
288    (0x7fff1, 19),     // 195
289    (0x3fffe7, 22),    // 196
290    (0x7ffff2, 23),    // 197
291    (0x3fffe8, 22),    // 198
292    (0x1ffffec, 25),   // 199
293    (0x3ffffe2, 26),   // 200
294    (0x3ffffe3, 26),   // 201
295    (0x3ffffe4, 26),   // 202
296    (0x7ffffde, 27),   // 203
297    (0x7ffffdf, 27),   // 204
298    (0x3ffffe5, 26),   // 205
299    (0xfffff1, 24),    // 206
300    (0x1ffffed, 25),   // 207
301    (0x7fff2, 19),     // 208
302    (0x1fffe3, 21),    // 209
303    (0x3ffffe6, 26),   // 210
304    (0x7ffffe0, 27),   // 211
305    (0x7ffffe1, 27),   // 212
306    (0x3ffffe7, 26),   // 213
307    (0x7ffffe2, 27),   // 214
308    (0xfffff2, 24),    // 215
309    (0x1fffe4, 21),    // 216
310    (0x1fffe5, 21),    // 217
311    (0x3ffffe8, 26),   // 218
312    (0x3ffffe9, 26),   // 219
313    (0xffffffd, 28),   // 220
314    (0x7ffffe3, 27),   // 221
315    (0x7ffffe4, 27),   // 222
316    (0x7ffffe5, 27),   // 223
317    (0xfffec, 20),     // 224
318    (0xfffff3, 24),    // 225
319    (0xfffed, 20),     // 226
320    (0x1fffe6, 21),    // 227
321    (0x3fffe9, 22),    // 228
322    (0x1fffe7, 21),    // 229
323    (0x1fffe8, 21),    // 230
324    (0x7ffff3, 23),    // 231
325    (0x3fffea, 22),    // 232
326    (0x3fffeb, 22),    // 233
327    (0x1ffffee, 25),   // 234
328    (0x1ffffef, 25),   // 235
329    (0xfffff4, 24),    // 236
330    (0xfffff5, 24),    // 237
331    (0x3ffffea, 26),   // 238
332    (0x7ffff4, 23),    // 239
333    (0x3ffffeb, 26),   // 240
334    (0x7ffffe6, 27),   // 241
335    (0x3ffffec, 26),   // 242
336    (0x3ffffed, 26),   // 243
337    (0x7ffffe7, 27),   // 244
338    (0x7ffffe8, 27),   // 245
339    (0x7ffffe9, 27),   // 246
340    (0x7ffffea, 27),   // 247
341    (0x7ffffeb, 27),   // 248
342    (0xffffffe, 28),   // 249
343    (0x7ffffec, 27),   // 250
344    (0x7ffffed, 27),   // 251
345    (0x7ffffee, 27),   // 252
346    (0x7ffffef, 27),   // 253
347    (0x7fffff0, 27),   // 254
348    (0x3ffffee, 26),   // 255
349    (0x3fffffff, 30),  // 256 EOS
350];
351
352// ============================================================================
353// Integer Encoding/Decoding (RFC 7541 Section 5.1)
354// ============================================================================
355
356/// Decode an HPACK integer with N-bit prefix.
357///
358/// The first byte is the prefix byte, where the top (8 - `prefix_bits`) bits
359/// may contain a representation indicator.
360///
361/// # Arguments
362/// - `buf`: input bytes starting at the prefix byte
363/// - `prefix_bits`: number of bits in the integer prefix (1-8)
364///
365/// # Returns
366/// `Some((value, bytes_consumed))` or `None` if the buffer is too short or
367/// the integer is malformed.
368#[must_use]
369pub fn decode_integer(buf: &[u8], prefix_bits: u8) -> Option<(u64, usize)> {
370    if buf.is_empty() || prefix_bits == 0 || prefix_bits > 8 {
371        return None;
372    }
373
374    let prefix_max = (1u32 << prefix_bits) - 1;
375    let first = u64::from(buf[0] & (prefix_max as u8));
376
377    if first < u64::from(prefix_max) {
378        // Small value that fits in prefix
379        return Some((first, 1));
380    }
381
382    // Value is >= prefix_max, read continuation bytes
383    let mut value = u64::from(prefix_max);
384    let mut shift = 0u64;
385    let mut i = 1;
386
387    loop {
388        if i >= buf.len() {
389            return None;
390        }
391        let byte = buf[i];
392        i += 1;
393        let addend = u64::from(byte & 0x7F)
394            .checked_shl(shift as u32)
395            .and_then(|v| value.checked_add(v));
396        match addend {
397            Some(v) => value = v,
398            None => return None,
399        }
400        shift += 7;
401
402        if (byte & 0x80) == 0 {
403            break;
404        }
405
406        if shift > 63 {
407            // Overflow protection
408            return None;
409        }
410    }
411
412    Some((value, i))
413}
414
415/// Encode an HPACK integer with N-bit prefix.
416///
417/// # Arguments
418/// - `value`: the integer to encode
419/// - `prefix_bits`: number of prefix bits (1-8)
420/// - `prefix_byte`: the high bits to set in the first byte (the representation indicator bits)
421///
422/// # Returns
423/// A `Vec<u8>` containing the encoded integer.
424#[must_use]
425pub fn encode_integer(value: u64, prefix_bits: u8, prefix_byte: u8) -> Vec<u8> {
426    let prefix_max = (1u64 << prefix_bits) - 1;
427
428    if value < prefix_max {
429        // Fits in prefix
430        return vec![prefix_byte | (value as u8)];
431    }
432
433    // Does not fit in prefix
434    let mut out = vec![prefix_byte | (prefix_max as u8)];
435    let mut remaining = value - prefix_max;
436
437    loop {
438        if remaining < 128 {
439            out.push(remaining as u8);
440            break;
441        }
442        out.push((remaining & 0x7F) as u8 | 0x80);
443        remaining >>= 7;
444    }
445
446    out
447}
448
449// ============================================================================
450// Huffman Node for decoding
451// ============================================================================
452
453/// A node in the Huffman decoding tree.
454#[derive(Default)]
455struct HuffmanNode {
456    symbol: Option<u16>, // Some(sym) if this is a leaf
457    children: [Option<Box<HuffmanNode>>; 2],
458}
459
460impl HuffmanNode {
461    fn new() -> Self {
462        HuffmanNode {
463            symbol: None,
464            children: [None, None],
465        }
466    }
467}
468
469/// Build the Huffman decoding tree from the `HUFFMAN_TABLE`.
470fn build_huffman_tree() -> HuffmanNode {
471    let mut root = HuffmanNode::new();
472
473    for (sym, &(code, nbits)) in HUFFMAN_TABLE.iter().enumerate() {
474        if nbits == 0 || nbits > 32 {
475            continue;
476        }
477        // Skip impossible codes (EOS is sym 256, but we include it for EOS detection)
478        let mut node = &mut root;
479        for bit_idx in (0..nbits).rev() {
480            let bit = ((code >> bit_idx) & 1) as usize;
481            if node.children[bit].is_none() {
482                node.children[bit] = Some(Box::new(HuffmanNode::new()));
483            }
484            node = node.children[bit].as_mut().unwrap();
485        }
486        node.symbol = Some(sym as u16);
487    }
488
489    root
490}
491
492// ============================================================================
493// Huffman Decode / Encode (RFC 7541 Appendix B)
494// ============================================================================
495
496/// Decode Huffman-encoded data (RFC 7541 Appendix B).
497///
498/// Returns the decoded bytes on success, or `None` if the data is malformed.
499/// Padding bits (at most 7) must all be 1s as per the spec.
500#[must_use]
501pub fn huffman_decode(encoded: &[u8]) -> Option<Vec<u8>> {
502    let root = build_huffman_tree();
503    let mut output = Vec::new();
504    let mut node = &root;
505
506    for &byte in encoded {
507        for bit_idx in (0..8).rev() {
508            let bit = ((byte >> bit_idx) & 1) as usize;
509            node = node.children[bit].as_ref()?;
510
511            if let Some(sym) = node.symbol {
512                if sym == 256 {
513                    // EOS encountered
514                    return None;
515                }
516                output.push(sym as u8);
517                node = &root;
518            }
519        }
520    }
521
522    // After all bytes consumed, we should be at root or at a padding position.
523    // Padding must be high bits of a partial byte (all 1s). The tree traversal
524    // handles this: if we're not at root, the partial byte's remaining bits
525    // must form a path to a non-leaf node (valid padding).
526    // If we ended on a leaf that's EOS, that's an error (already handled).
527    // Being at a non-root position is acceptable as long as remaining bits were all 1.
528    // The loop above handles this correctly since we only follow actual bit paths.
529
530    Some(output)
531}
532
533/// Encode data using Huffman coding (RFC 7541 Appendix B).
534///
535/// Returns a byte vector. The output is padded with 1-bits to the next byte boundary.
536#[must_use]
537pub fn huffman_encode(data: &[u8]) -> Vec<u8> {
538    let mut bit_buf: u64 = 0;
539    let mut bit_count = 0u32;
540    let mut output = Vec::new();
541
542    for &byte in data {
543        let (code, nbits) = HUFFMAN_TABLE[byte as usize];
544        let nbits = u32::from(nbits);
545
546        bit_buf = (bit_buf << nbits) | u64::from(code);
547        bit_count += nbits;
548
549        while bit_count >= 8 {
550            bit_count -= 8;
551            output.push(((bit_buf >> bit_count) & 0xFF) as u8);
552        }
553    }
554
555    // Flush remaining bits with 1-bit padding
556    if bit_count > 0 {
557        let padding = 8 - bit_count;
558        bit_buf = (bit_buf << padding) | ((1u64 << padding) - 1);
559        output.push((bit_buf & 0xFF) as u8);
560    }
561
562    output
563}
564
565// ============================================================================
566// String Literal Decode/Encode (RFC 7541 Section 5.2)
567// ============================================================================
568
569/// Decode a string literal from an HPACK-encoded buffer.
570///
571/// Format:
572/// ```text
573/// +---+---+---+---+---+---+---+---+
574/// | H |    String Length (7+)      |
575/// +---+---------------------------+
576/// | String Data (Length octets)   |
577/// +-------------------------------+
578/// ```
579///
580/// # Returns
581/// `Some((string_bytes, bytes_consumed))` or `None` if the buffer is too short.
582#[must_use]
583pub fn decode_string(buf: &[u8]) -> Option<(Vec<u8>, usize)> {
584    if buf.is_empty() {
585        return None;
586    }
587
588    let huffman_flag = (buf[0] & 0x80) != 0;
589    let (length, consumed) = decode_integer(buf, 7)?;
590    let length = length as usize;
591
592    if consumed + length > buf.len() {
593        return None;
594    }
595
596    let string_data = &buf[consumed..consumed + length];
597
598    let result = if huffman_flag {
599        huffman_decode(string_data)?
600    } else {
601        string_data.to_vec()
602    };
603
604    Some((result, consumed + length))
605}
606
607/// Encode a string literal without Huffman coding.
608///
609/// Returns bytes in literal (non-Huffman) format.
610#[must_use]
611pub fn encode_string_literal(data: &[u8]) -> Vec<u8> {
612    let mut out = encode_integer(data.len() as u64, 7, 0x00); // H=0
613    out.extend_from_slice(data);
614    out
615}
616
617/// Encode a string literal with Huffman coding if beneficial.
618///
619/// Uses Huffman encoding and sets H=1. Always Huffman-encodes for simplicity.
620#[must_use]
621pub fn encode_string_huffman(data: &[u8]) -> Vec<u8> {
622    let encoded = huffman_encode(data);
623    let mut out = encode_integer(encoded.len() as u64, 7, 0x80); // H=1
624    out.extend_from_slice(&encoded);
625    out
626}
627
628// ============================================================================
629// HPACK Decoder (RFC 7541)
630// ============================================================================
631
632/// HPACK dynamic table entry size = name length + value length + 32 bytes overhead.
633fn entry_size(name: &str, value: &str) -> usize {
634    name.len() + value.len() + 32
635}
636
637/// HPACK decoder with dynamic table state.
638///
639/// This decoder maintains a dynamic table across multiple calls, allowing
640/// header compression state to be shared within an HTTP/2 connection.
641pub struct HpackDecoder {
642    /// Dynamic table entries (most recently added first).
643    dynamic_table: Vec<(String, String)>,
644    /// Maximum dynamic table size (bytes, per `SETTINGS_HEADER_TABLE_SIZE`).
645    max_table_size: usize,
646    /// Current dynamic table size (sum of entry sizes).
647    current_size: usize,
648}
649
650impl Default for HpackDecoder {
651    fn default() -> Self {
652        Self::new()
653    }
654}
655
656impl HpackDecoder {
657    /// Create a new HPACK decoder with default max table size of 4096 bytes.
658    #[must_use]
659    pub fn new() -> Self {
660        HpackDecoder {
661            dynamic_table: Vec::new(),
662            max_table_size: 4096,
663            current_size: 0,
664        }
665    }
666
667    /// Create a new HPACK decoder with a specific max table size.
668    #[must_use]
669    pub fn with_max_size(max_table_size: usize) -> Self {
670        HpackDecoder {
671            dynamic_table: Vec::new(),
672            max_table_size,
673            current_size: 0,
674        }
675    }
676
677    /// Update the maximum table size (e.g., from `SETTINGS_HEADER_TABLE_SIZE`).
678    pub fn set_max_table_size(&mut self, size: usize) {
679        self.max_table_size = size;
680        self.evict_to_fit(0);
681    }
682
683    /// Returns the number of entries currently in the dynamic table.
684    #[must_use]
685    pub fn dynamic_table_len(&self) -> usize {
686        self.dynamic_table.len()
687    }
688
689    /// Returns the current size (in bytes) of the dynamic table.
690    #[must_use]
691    pub fn dynamic_table_current_size(&self) -> usize {
692        self.current_size
693    }
694
695    /// Returns the entries in the dynamic table as (name, value) string slices.
696    /// Entries are ordered most-recently-added first (index 62 = first entry).
697    #[must_use]
698    pub fn dynamic_table_entries(&self) -> &[(String, String)] {
699        &self.dynamic_table
700    }
701
702    /// Decode a block of HPACK-encoded headers.
703    ///
704    /// Returns a list of `(name, value)` pairs in the order they were decoded.
705    pub fn decode(&mut self, buf: &[u8]) -> Result<Vec<(String, String)>, String> {
706        let mut headers = Vec::new();
707        let mut pos = 0;
708
709        while pos < buf.len() {
710            let first = buf[pos];
711
712            if (first & 0x80) != 0 {
713                // Indexed Header Field Representation (RFC 7541 Section 6.1)
714                // Pattern: 1xxxxxxx
715                let (idx, consumed) = decode_integer(&buf[pos..], 7)
716                    .ok_or_else(|| "Failed to decode indexed header index".to_string())?;
717                pos += consumed;
718
719                let (name, value) = self
720                    .table_entry(idx as usize)
721                    .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
722                headers.push((name, value));
723            } else if (first & 0x40) != 0 {
724                // Literal Header Field with Incremental Indexing (RFC 7541 Section 6.2.1)
725                // Pattern: 01xxxxxx
726                let (idx, consumed) = decode_integer(&buf[pos..], 6)
727                    .ok_or_else(|| "Failed to decode literal indexed name index".to_string())?;
728                pos += consumed;
729
730                let name = if idx == 0 {
731                    // Name is a literal string
732                    let (name_bytes, nc) = decode_string(&buf[pos..])
733                        .ok_or_else(|| "Failed to decode literal name string".to_string())?;
734                    pos += nc;
735                    String::from_utf8(name_bytes)
736                        .map_err(|e| format!("Invalid UTF-8 in header name: {e}"))?
737                } else {
738                    let (n, _) = self
739                        .table_entry(idx as usize)
740                        .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
741                    n
742                };
743
744                let (value_bytes, vc) = decode_string(&buf[pos..])
745                    .ok_or_else(|| "Failed to decode literal value string".to_string())?;
746                pos += vc;
747                let value = String::from_utf8(value_bytes)
748                    .map_err(|e| format!("Invalid UTF-8 in header value: {e}"))?;
749
750                self.add_to_dynamic_table(name.clone(), value.clone());
751                headers.push((name, value));
752            } else if (first & 0x20) != 0 {
753                // Dynamic Table Size Update (RFC 7541 Section 6.3)
754                // Pattern: 001xxxxx
755                let (new_size, consumed) = decode_integer(&buf[pos..], 5)
756                    .ok_or_else(|| "Failed to decode table size update".to_string())?;
757                pos += consumed;
758
759                if new_size as usize > self.max_table_size {
760                    return Err(format!(
761                        "Table size update {} exceeds max {}",
762                        new_size, self.max_table_size
763                    ));
764                }
765                self.evict_to_fit(0);
766                // Trim to new size
767                while self.current_size > new_size as usize {
768                    if let Some(entry) = self.dynamic_table.pop() {
769                        self.current_size = self
770                            .current_size
771                            .saturating_sub(entry_size(&entry.0, &entry.1));
772                    } else {
773                        break;
774                    }
775                }
776            } else if (first & 0x10) != 0 {
777                // Literal Header Field Never Indexed (RFC 7541 Section 6.2.3)
778                // Pattern: 0001xxxx
779                let (idx, consumed) = decode_integer(&buf[pos..], 4)
780                    .ok_or_else(|| "Failed to decode never-indexed name index".to_string())?;
781                pos += consumed;
782
783                let name = if idx == 0 {
784                    let (name_bytes, nc) = decode_string(&buf[pos..])
785                        .ok_or_else(|| "Failed to decode never-indexed name string".to_string())?;
786                    pos += nc;
787                    String::from_utf8(name_bytes)
788                        .map_err(|e| format!("Invalid UTF-8 in header name: {e}"))?
789                } else {
790                    let (n, _) = self
791                        .table_entry(idx as usize)
792                        .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
793                    n
794                };
795
796                let (value_bytes, vc) = decode_string(&buf[pos..])
797                    .ok_or_else(|| "Failed to decode never-indexed value string".to_string())?;
798                pos += vc;
799                let value = String::from_utf8(value_bytes)
800                    .map_err(|e| format!("Invalid UTF-8 in header value: {e}"))?;
801
802                // Never indexed — do not add to dynamic table
803                headers.push((name, value));
804            } else {
805                // Literal Header Field Without Indexing (RFC 7541 Section 6.2.2)
806                // Pattern: 0000xxxx
807                let (idx, consumed) = decode_integer(&buf[pos..], 4)
808                    .ok_or_else(|| "Failed to decode without-indexing name index".to_string())?;
809                pos += consumed;
810
811                let name = if idx == 0 {
812                    let (name_bytes, nc) = decode_string(&buf[pos..]).ok_or_else(|| {
813                        "Failed to decode without-indexing name string".to_string()
814                    })?;
815                    pos += nc;
816                    String::from_utf8(name_bytes)
817                        .map_err(|e| format!("Invalid UTF-8 in header name: {e}"))?
818                } else {
819                    let (n, _) = self
820                        .table_entry(idx as usize)
821                        .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
822                    n
823                };
824
825                let (value_bytes, vc) = decode_string(&buf[pos..])
826                    .ok_or_else(|| "Failed to decode without-indexing value string".to_string())?;
827                pos += vc;
828                let value = String::from_utf8(value_bytes)
829                    .map_err(|e| format!("Invalid UTF-8 in header value: {e}"))?;
830
831                // Without indexing — do not add to dynamic table
832                headers.push((name, value));
833            }
834        }
835
836        Ok(headers)
837    }
838
839    /// Look up a dynamic table entry (0-indexed from the front, most recent first).
840    fn dynamic_table_entry(&self, index: usize) -> Option<(&str, &str)> {
841        self.dynamic_table
842            .get(index)
843            .map(|(n, v)| (n.as_str(), v.as_str()))
844    }
845
846    /// Look up a static table entry by 1-based index.
847    fn static_table_entry(index: usize) -> Option<(&'static str, &'static str)> {
848        if index >= 1 && index <= STATIC_TABLE.len() {
849            Some(STATIC_TABLE[index - 1])
850        } else {
851            None
852        }
853    }
854
855    /// Look up a combined (static + dynamic) table entry.
856    ///
857    /// Indices 1..=61 are static table. Indices 62+ are dynamic table.
858    fn table_entry(&self, index: usize) -> Option<(String, String)> {
859        if index == 0 {
860            return None;
861        }
862
863        if index <= STATIC_TABLE.len() {
864            let (n, v) = Self::static_table_entry(index)?;
865            return Some((n.to_string(), v.to_string()));
866        }
867
868        let dynamic_idx = index - STATIC_TABLE.len() - 1;
869        let (n, v) = self.dynamic_table_entry(dynamic_idx)?;
870        Some((n.to_string(), v.to_string()))
871    }
872
873    /// Add a new entry to the dynamic table (prepended, i.e., most recent first).
874    fn add_to_dynamic_table(&mut self, name: String, value: String) {
875        let size = entry_size(&name, &value);
876        self.evict_to_fit(size);
877        if size <= self.max_table_size {
878            self.current_size += size;
879            self.dynamic_table.insert(0, (name, value));
880        }
881    }
882
883    /// Evict entries from the back of the dynamic table until the table can
884    /// accommodate `new_entry_size` more bytes.
885    fn evict_to_fit(&mut self, new_entry_size: usize) {
886        while !self.dynamic_table.is_empty()
887            && self.current_size + new_entry_size > self.max_table_size
888        {
889            if let Some(entry) = self.dynamic_table.pop() {
890                self.current_size = self
891                    .current_size
892                    .saturating_sub(entry_size(&entry.0, &entry.1));
893            }
894        }
895    }
896}
897
898// ============================================================================
899// HPACK Encoder
900// ============================================================================
901
902/// HPACK encoder.
903///
904/// Currently uses literal header fields without indexing for simplicity.
905/// An optimized version would use the static and dynamic tables to produce
906/// smaller output.
907pub struct HpackEncoder {
908    /// Dynamic table entries (most recently added first).
909    #[allow(dead_code)]
910    dynamic_table: Vec<(String, String)>,
911    /// Maximum table size.
912    #[allow(dead_code)]
913    max_table_size: usize,
914}
915
916impl Default for HpackEncoder {
917    fn default() -> Self {
918        Self::new()
919    }
920}
921
922impl HpackEncoder {
923    /// Create a new HPACK encoder with default max table size of 4096 bytes.
924    #[must_use]
925    pub fn new() -> Self {
926        HpackEncoder {
927            dynamic_table: Vec::new(),
928            max_table_size: 4096,
929        }
930    }
931
932    /// Encode a list of header name-value pairs using HPACK.
933    ///
934    /// This implementation uses literal header fields without indexing
935    /// (pattern 0000xxxx with index=0) for simplicity and correctness.
936    /// It checks the static table for name-only matches to save space.
937    #[must_use]
938    pub fn encode(&self, headers: &[(&str, &str)]) -> Vec<u8> {
939        let mut out = Vec::new();
940
941        for &(name, value) in headers {
942            // Check static table for exact match (both name and value)
943            if let Some(static_idx) = self.find_static_exact(name, value) {
944                // Indexed header field representation (Section 6.1)
945                // Pattern: 1xxxxxxx
946                out.extend_from_slice(&encode_integer(static_idx as u64, 7, 0x80));
947                continue;
948            }
949
950            // Check static table for name-only match
951            if let Some(name_idx) = self.find_static_name(name) {
952                // Literal header field with incremental indexing (Section 6.2.1)
953                // Pattern: 01xxxxxx with name index
954                out.extend_from_slice(&encode_integer(name_idx as u64, 6, 0x40));
955                out.extend_from_slice(&encode_string_literal(value.as_bytes()));
956                continue;
957            }
958
959            // Literal header field without indexing (Section 6.2.2)
960            // Pattern: 00000000 followed by name and value strings
961            out.push(0x00);
962            out.extend_from_slice(&encode_string_literal(name.as_bytes()));
963            out.extend_from_slice(&encode_string_literal(value.as_bytes()));
964        }
965
966        out
967    }
968
969    /// Encode headers using Huffman encoding for string values.
970    #[must_use]
971    pub fn encode_huffman(&self, headers: &[(&str, &str)]) -> Vec<u8> {
972        let mut out = Vec::new();
973
974        for &(name, value) in headers {
975            // Check static table for exact match
976            if let Some(static_idx) = self.find_static_exact(name, value) {
977                out.extend_from_slice(&encode_integer(static_idx as u64, 7, 0x80));
978                continue;
979            }
980
981            // Literal without indexing with Huffman-encoded strings
982            out.push(0x00);
983            out.extend_from_slice(&encode_string_huffman(name.as_bytes()));
984            out.extend_from_slice(&encode_string_huffman(value.as_bytes()));
985        }
986
987        out
988    }
989
990    /// Find a static table index for an exact (name, value) match.
991    /// Returns 1-based index or None.
992    fn find_static_exact(&self, name: &str, value: &str) -> Option<usize> {
993        for (i, &(n, v)) in STATIC_TABLE.iter().enumerate() {
994            if n == name && v == value {
995                return Some(i + 1);
996            }
997        }
998        None
999    }
1000
1001    /// Find a static table index for a name-only match.
1002    /// Returns 1-based index of first matching entry or None.
1003    fn find_static_name(&self, name: &str) -> Option<usize> {
1004        for (i, &(n, _)) in STATIC_TABLE.iter().enumerate() {
1005            if n == name {
1006                return Some(i + 1);
1007            }
1008        }
1009        None
1010    }
1011}
1012
1013// ============================================================================
1014// Tests
1015// ============================================================================
1016
1017#[cfg(test)]
1018mod tests {
1019    use super::*;
1020
1021    // -------------------------------------------------------------------------
1022    // Integer encode/decode
1023    // -------------------------------------------------------------------------
1024
1025    #[test]
1026    fn test_decode_integer_small() {
1027        // Value 5 with 5-bit prefix: 0b00000101 = 0x05
1028        let buf = [0x05u8];
1029        let (val, consumed) = decode_integer(&buf, 5).unwrap();
1030        assert_eq!(val, 5);
1031        assert_eq!(consumed, 1);
1032    }
1033
1034    #[test]
1035    fn test_decode_integer_multi_byte() {
1036        // RFC 7541 Section 5.1 example: value 1337 with 5-bit prefix
1037        // First byte: 0b00011111 = 31 (all prefix bits set)
1038        // Continuation: 0b10011010 = 154, 0b00001010 = 10
1039        // 1337 - 31 = 1306 = 0b10100011010
1040        // First continuation: 1306 & 0x7F = 26 (0b0011010), MSB=1 → 0b10011010 = 0x9A... wait
1041        // Let me use spec values: 1337 with 5-bit prefix
1042        // 1337 - 31 = 1306
1043        // 1306 % 128 = 26 | 0x80 = 0x9A, 1306 / 128 = 10 = 0x0A
1044        let buf = [0b00011111u8, 0x9A, 0x0A];
1045        let (val, consumed) = decode_integer(&buf, 5).unwrap();
1046        assert_eq!(val, 1337);
1047        assert_eq!(consumed, 3);
1048    }
1049
1050    #[test]
1051    fn test_encode_integer_small() {
1052        let encoded = encode_integer(5, 5, 0x00);
1053        assert_eq!(encoded, vec![0x05]);
1054    }
1055
1056    #[test]
1057    fn test_encode_integer_large() {
1058        // 1337 with 5-bit prefix
1059        let encoded = encode_integer(1337, 5, 0x00);
1060        assert_eq!(encoded, vec![0b00011111, 0x9A, 0x0A]);
1061    }
1062
1063    #[test]
1064    fn test_encode_decode_integer_roundtrip() {
1065        for val in [0u64, 1, 30, 31, 127, 128, 255, 1000, 65535, 1337] {
1066            for prefix in [4u8, 5, 6, 7] {
1067                let encoded = encode_integer(val, prefix, 0x00);
1068                let (decoded, _) = decode_integer(&encoded, prefix).unwrap();
1069                assert_eq!(
1070                    val, decoded,
1071                    "Roundtrip failed for val={} prefix={}",
1072                    val, prefix
1073                );
1074            }
1075        }
1076    }
1077
1078    // -------------------------------------------------------------------------
1079    // String literals
1080    // -------------------------------------------------------------------------
1081
1082    #[test]
1083    fn test_decode_string_literal() {
1084        // Non-Huffman string: length=5, "hello"
1085        let mut buf = vec![0x05u8]; // H=0, length=5
1086        buf.extend_from_slice(b"hello");
1087        let (s, consumed) = decode_string(&buf).unwrap();
1088        assert_eq!(s, b"hello");
1089        assert_eq!(consumed, 6);
1090    }
1091
1092    #[test]
1093    fn test_huffman_encode_decode_roundtrip() {
1094        let original = b"www.example.com";
1095        let encoded = huffman_encode(original);
1096        let decoded = huffman_decode(&encoded).unwrap();
1097        assert_eq!(decoded, original);
1098    }
1099
1100    #[test]
1101    fn test_huffman_encode_empty() {
1102        let encoded = huffman_encode(b"");
1103        assert!(encoded.is_empty());
1104    }
1105
1106    #[test]
1107    fn test_huffman_decode_rfc7541_example() {
1108        // RFC 7541 Appendix C.4.1 example: "www.example.com" Huffman-encoded
1109        // Known encoding from the RFC: f1e3 c2e5 f23a 6ba0 ab90 f4ff
1110        let encoded = [
1111            0xf1u8, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
1112        ];
1113        let decoded = huffman_decode(&encoded).unwrap();
1114        assert_eq!(decoded, b"www.example.com");
1115    }
1116
1117    #[test]
1118    fn test_decode_string_huffman() {
1119        let original = b"no-cache";
1120        let encoded = huffman_encode(original);
1121        // Build HPACK string format: H=1, length, data
1122        let mut buf = encode_integer(encoded.len() as u64, 7, 0x80);
1123        buf.extend_from_slice(&encoded);
1124        let (decoded, _) = decode_string(&buf).unwrap();
1125        assert_eq!(decoded, original);
1126    }
1127
1128    // -------------------------------------------------------------------------
1129    // Static table
1130    // -------------------------------------------------------------------------
1131
1132    #[test]
1133    fn test_static_table_size() {
1134        assert_eq!(STATIC_TABLE.len(), 61);
1135    }
1136
1137    #[test]
1138    fn test_static_table_first_entry() {
1139        assert_eq!(STATIC_TABLE[0], (":authority", ""));
1140    }
1141
1142    #[test]
1143    fn test_static_table_method_get() {
1144        assert_eq!(STATIC_TABLE[1], (":method", "GET"));
1145    }
1146
1147    // -------------------------------------------------------------------------
1148    // HPACK decoder
1149    // -------------------------------------------------------------------------
1150
1151    #[test]
1152    fn test_decoder_indexed_static() {
1153        // Index 2 = ":method: GET"
1154        // Indexed representation: 1xxxxxxx where x=2 → 0x82
1155        let buf = [0x82u8];
1156        let mut decoder = HpackDecoder::new();
1157        let headers = decoder.decode(&buf).unwrap();
1158        assert_eq!(headers.len(), 1);
1159        assert_eq!(headers[0], (":method".to_string(), "GET".to_string()));
1160    }
1161
1162    #[test]
1163    fn test_decoder_literal_with_indexing() {
1164        // Literal with incremental indexing: 01xxxxxx
1165        // name index = 0, name = "custom-key", value = "custom-header"
1166        let mut buf = vec![0x40u8]; // 01 000000 = new name, incremental indexing
1167        buf.extend_from_slice(&encode_string_literal(b"custom-key"));
1168        buf.extend_from_slice(&encode_string_literal(b"custom-header"));
1169
1170        let mut decoder = HpackDecoder::new();
1171        let headers = decoder.decode(&buf).unwrap();
1172        assert_eq!(headers.len(), 1);
1173        assert_eq!(
1174            headers[0],
1175            ("custom-key".to_string(), "custom-header".to_string())
1176        );
1177    }
1178
1179    #[test]
1180    fn test_decoder_literal_without_indexing() {
1181        // Literal without indexing: 0000xxxx with idx=0 (new name)
1182        let mut buf = vec![0x00u8];
1183        buf.extend_from_slice(&encode_string_literal(b"x-custom"));
1184        buf.extend_from_slice(&encode_string_literal(b"value"));
1185
1186        let mut decoder = HpackDecoder::new();
1187        let headers = decoder.decode(&buf).unwrap();
1188        assert_eq!(headers.len(), 1);
1189        assert_eq!(headers[0], ("x-custom".to_string(), "value".to_string()));
1190    }
1191
1192    #[test]
1193    fn test_decoder_dynamic_table_eviction() {
1194        let mut decoder = HpackDecoder::with_max_size(64);
1195        // Add an entry that barely fits
1196        let mut buf = vec![0x40u8];
1197        buf.extend_from_slice(&encode_string_literal(b"x-a"));
1198        buf.extend_from_slice(&encode_string_literal(b"b"));
1199        decoder.decode(&buf).unwrap();
1200        assert_eq!(decoder.dynamic_table.len(), 1);
1201
1202        // Add another entry that should evict the first
1203        let mut buf2 = vec![0x40u8];
1204        buf2.extend_from_slice(&encode_string_literal(b"x-c"));
1205        buf2.extend_from_slice(&encode_string_literal(b"d"));
1206        decoder.decode(&buf2).unwrap();
1207        // First entry should be evicted since total would exceed 64
1208        assert!(decoder.dynamic_table.len() <= 2);
1209    }
1210
1211    // -------------------------------------------------------------------------
1212    // HPACK encoder
1213    // -------------------------------------------------------------------------
1214
1215    #[test]
1216    fn test_encoder_static_exact_match() {
1217        let encoder = HpackEncoder::new();
1218        // ":method: GET" is static index 2 → should encode as 0x82
1219        let encoded = encoder.encode(&[(":method", "GET")]);
1220        assert_eq!(encoded, vec![0x82]);
1221    }
1222
1223    #[test]
1224    fn test_encoder_static_name_match() {
1225        let encoder = HpackEncoder::new();
1226        // ":method" matches static index 2 with value "GET", but "DELETE" won't match exact
1227        let encoded = encoder.encode(&[(":method", "DELETE")]);
1228        // Should use name-indexed literal: 0x40 | idx(2) = 0x42, then value
1229        assert_eq!(encoded[0], 0x42); // 01 000010 = name-indexed literal with static index 2
1230    }
1231
1232    #[test]
1233    fn test_encoder_roundtrip() {
1234        let encoder = HpackEncoder::new();
1235        let mut decoder = HpackDecoder::new();
1236
1237        let headers = vec![
1238            (":method", "GET"),
1239            (":path", "/"),
1240            (":scheme", "https"),
1241            ("user-agent", "test/1.0"),
1242            ("x-custom", "value"),
1243        ];
1244        let encoded = encoder.encode(&headers);
1245        let decoded = decoder.decode(&encoded).unwrap();
1246
1247        assert_eq!(decoded.len(), headers.len());
1248        for (i, (name, value)) in headers.iter().enumerate() {
1249            assert_eq!(decoded[i].0, *name);
1250            assert_eq!(decoded[i].1, *value);
1251        }
1252    }
1253
1254    #[test]
1255    fn test_encoder_huffman_roundtrip() {
1256        let encoder = HpackEncoder::new();
1257        let mut decoder = HpackDecoder::new();
1258
1259        let headers = vec![
1260            ("x-request-id", "abc-123"),
1261            ("content-type", "application/json"),
1262        ];
1263        let encoded = encoder.encode_huffman(&headers);
1264        let decoded = decoder.decode(&encoded).unwrap();
1265
1266        assert_eq!(decoded.len(), headers.len());
1267        for (i, (name, value)) in headers.iter().enumerate() {
1268            assert_eq!(decoded[i].0, *name);
1269            assert_eq!(decoded[i].1, *value);
1270        }
1271    }
1272}