1pub 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#[rustfmt::skip]
92pub const HUFFMAN_TABLE: &[(u32, u8)] = &[
93 (0x1ff8, 13), (0x7fffd8, 23), (0xfffffe2, 28), (0xfffffe3, 28), (0xfffffe4, 28), (0xfffffe5, 28), (0xfffffe6, 28), (0xfffffe7, 28), (0xfffffe8, 28), (0xffffea, 24), (0x3ffffffc, 30), (0xfffffe9, 28), (0xfffffea, 28), (0x3ffffffd, 30), (0xfffffeb, 28), (0xfffffec, 28), (0xfffffed, 28), (0xfffffee, 28), (0xfffffef, 28), (0xffffff0, 28), (0xffffff1, 28), (0xffffff2, 28), (0x3ffffffe, 30), (0xffffff3, 28), (0xffffff4, 28), (0xffffff5, 28), (0xffffff6, 28), (0xffffff7, 28), (0xffffff8, 28), (0xffffff9, 28), (0xffffffa, 28), (0xffffffb, 28), (0x14, 6), (0x3f8, 10), (0x3f9, 10), (0xffa, 12), (0x1ff9, 13), (0x15, 6), (0xf8, 8), (0x7fa, 11), (0x3fa, 10), (0x3fb, 10), (0xf9, 8), (0x7fb, 11), (0xfa, 8), (0x16, 6), (0x17, 6), (0x18, 6), (0x00, 5), (0x01, 5), (0x02, 5), (0x19, 6), (0x1a, 6), (0x1b, 6), (0x1c, 6), (0x1d, 6), (0x1e, 6), (0x1f, 6), (0x5c, 7), (0xfb, 8), (0x7ffc, 15), (0x20, 6), (0xffb, 12), (0x3fc, 10), (0x1ffa, 13), (0x21, 6), (0x5d, 7), (0x5e, 7), (0x5f, 7), (0x60, 7), (0x61, 7), (0x62, 7), (0x63, 7), (0x64, 7), (0x65, 7), (0x66, 7), (0x67, 7), (0x68, 7), (0x69, 7), (0x6a, 7), (0x6b, 7), (0x6c, 7), (0x6d, 7), (0x6e, 7), (0x6f, 7), (0x70, 7), (0x71, 7), (0x72, 7), (0xfc, 8), (0x73, 7), (0xfd, 8), (0x1ffb, 13), (0x7fff0, 19), (0x1ffc, 13), (0x3ffc, 14), (0x22, 6), (0x7ffd, 15), (0x03, 5), (0x23, 6), (0x04, 5), (0x24, 6), (0x05, 5), (0x25, 6), (0x26, 6), (0x27, 6), (0x06, 5), (0x74, 7), (0x75, 7), (0x28, 6), (0x29, 6), (0x2a, 6), (0x07, 5), (0x2b, 6), (0x76, 7), (0x2c, 6), (0x08, 5), (0x09, 5), (0x2d, 6), (0x77, 7), (0x78, 7), (0x79, 7), (0x7a, 7), (0x7b, 7), (0x7ffe, 15), (0x7fc, 11), (0x3ffd, 14), (0x1ffd, 13), (0xffffffc, 28), (0xfffe6, 20), (0x3fffd2, 22), (0xfffe7, 20), (0xfffe8, 20), (0x3fffd3, 22), (0x3fffd4, 22), (0x3fffd5, 22), (0x7fffd9, 23), (0x3fffd6, 22), (0x7fffda, 23), (0x7fffdb, 23), (0x7fffdc, 23), (0x7fffdd, 23), (0x7fffde, 23), (0xffffeb, 24), (0x7fffdf, 23), (0xffffec, 24), (0xffffed, 24), (0x3fffd7, 22), (0x7fffe0, 23), (0xffffee, 24), (0x7fffe1, 23), (0x7fffe2, 23), (0x7fffe3, 23), (0x7fffe4, 23), (0x1fffdc, 21), (0x3fffd8, 22), (0x7fffe5, 23), (0x3fffd9, 22), (0x7fffe6, 23), (0x7fffe7, 23), (0xffffef, 24), (0x3fffda, 22), (0x1fffdd, 21), (0xfffe9, 20), (0x3fffdb, 22), (0x3fffdc, 22), (0x7fffe8, 23), (0x7fffe9, 23), (0x1fffde, 21), (0x7fffea, 23), (0x3fffdd, 22), (0x3fffde, 22), (0xfffff0, 24), (0x1fffdf, 21), (0x3fffdf, 22), (0x7fffeb, 23), (0x7fffec, 23), (0x1fffe0, 21), (0x1fffe1, 21), (0x3fffe0, 22), (0x1fffe2, 21), (0x7fffed, 23), (0x3fffe1, 22), (0x7fffee, 23), (0x7fffef, 23), (0xfffea, 20), (0x3fffe2, 22), (0x3fffe3, 22), (0x3fffe4, 22), (0x7ffff0, 23), (0x3fffe5, 22), (0x3fffe6, 22), (0x7ffff1, 23), (0x3ffffe0, 26), (0x3ffffe1, 26), (0xfffeb, 20), (0x7fff1, 19), (0x3fffe7, 22), (0x7ffff2, 23), (0x3fffe8, 22), (0x1ffffec, 25), (0x3ffffe2, 26), (0x3ffffe3, 26), (0x3ffffe4, 26), (0x7ffffde, 27), (0x7ffffdf, 27), (0x3ffffe5, 26), (0xfffff1, 24), (0x1ffffed, 25), (0x7fff2, 19), (0x1fffe3, 21), (0x3ffffe6, 26), (0x7ffffe0, 27), (0x7ffffe1, 27), (0x3ffffe7, 26), (0x7ffffe2, 27), (0xfffff2, 24), (0x1fffe4, 21), (0x1fffe5, 21), (0x3ffffe8, 26), (0x3ffffe9, 26), (0xffffffd, 28), (0x7ffffe3, 27), (0x7ffffe4, 27), (0x7ffffe5, 27), (0xfffec, 20), (0xfffff3, 24), (0xfffed, 20), (0x1fffe6, 21), (0x3fffe9, 22), (0x1fffe7, 21), (0x1fffe8, 21), (0x7ffff3, 23), (0x3fffea, 22), (0x3fffeb, 22), (0x1ffffee, 25), (0x1ffffef, 25), (0xfffff4, 24), (0xfffff5, 24), (0x3ffffea, 26), (0x7ffff4, 23), (0x3ffffeb, 26), (0x7ffffe6, 27), (0x3ffffec, 26), (0x3ffffed, 26), (0x7ffffe7, 27), (0x7ffffe8, 27), (0x7ffffe9, 27), (0x7ffffea, 27), (0x7ffffeb, 27), (0xffffffe, 28), (0x7ffffec, 27), (0x7ffffed, 27), (0x7ffffee, 27), (0x7ffffef, 27), (0x7fffff0, 27), (0x3ffffee, 26), (0x3fffffff, 30), ];
351
352#[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 return Some((first, 1));
380 }
381
382 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 value += u64::from(byte & 0x7F) << shift;
394 shift += 7;
395
396 if (byte & 0x80) == 0 {
397 break;
398 }
399
400 if shift > 63 {
401 return None;
403 }
404 }
405
406 Some((value, i))
407}
408
409#[must_use]
419pub fn encode_integer(value: u64, prefix_bits: u8, prefix_byte: u8) -> Vec<u8> {
420 let prefix_max = (1u64 << prefix_bits) - 1;
421
422 if value < prefix_max {
423 return vec![prefix_byte | (value as u8)];
425 }
426
427 let mut out = vec![prefix_byte | (prefix_max as u8)];
429 let mut remaining = value - prefix_max;
430
431 loop {
432 if remaining < 128 {
433 out.push(remaining as u8);
434 break;
435 }
436 out.push((remaining & 0x7F) as u8 | 0x80);
437 remaining >>= 7;
438 }
439
440 out
441}
442
443#[derive(Default)]
449struct HuffmanNode {
450 symbol: Option<u16>, children: [Option<Box<HuffmanNode>>; 2],
452}
453
454impl HuffmanNode {
455 fn new() -> Self {
456 HuffmanNode {
457 symbol: None,
458 children: [None, None],
459 }
460 }
461}
462
463fn build_huffman_tree() -> HuffmanNode {
465 let mut root = HuffmanNode::new();
466
467 for (sym, &(code, nbits)) in HUFFMAN_TABLE.iter().enumerate() {
468 if nbits == 0 || nbits > 32 {
469 continue;
470 }
471 let mut node = &mut root;
473 for bit_idx in (0..nbits).rev() {
474 let bit = ((code >> bit_idx) & 1) as usize;
475 if node.children[bit].is_none() {
476 node.children[bit] = Some(Box::new(HuffmanNode::new()));
477 }
478 node = node.children[bit].as_mut().unwrap();
479 }
480 node.symbol = Some(sym as u16);
481 }
482
483 root
484}
485
486#[must_use]
495pub fn huffman_decode(encoded: &[u8]) -> Option<Vec<u8>> {
496 let root = build_huffman_tree();
497 let mut output = Vec::new();
498 let mut node = &root;
499
500 for &byte in encoded {
501 for bit_idx in (0..8).rev() {
502 let bit = ((byte >> bit_idx) & 1) as usize;
503 node = node.children[bit].as_ref()?;
504
505 if let Some(sym) = node.symbol {
506 if sym == 256 {
507 return None;
509 }
510 output.push(sym as u8);
511 node = &root;
512 }
513 }
514 }
515
516 Some(output)
525}
526
527#[must_use]
531pub fn huffman_encode(data: &[u8]) -> Vec<u8> {
532 let mut bit_buf: u64 = 0;
533 let mut bit_count = 0u32;
534 let mut output = Vec::new();
535
536 for &byte in data {
537 let (code, nbits) = HUFFMAN_TABLE[byte as usize];
538 let nbits = u32::from(nbits);
539
540 bit_buf = (bit_buf << nbits) | u64::from(code);
541 bit_count += nbits;
542
543 while bit_count >= 8 {
544 bit_count -= 8;
545 output.push(((bit_buf >> bit_count) & 0xFF) as u8);
546 }
547 }
548
549 if bit_count > 0 {
551 let padding = 8 - bit_count;
552 bit_buf = (bit_buf << padding) | ((1u64 << padding) - 1);
553 output.push((bit_buf & 0xFF) as u8);
554 }
555
556 output
557}
558
559#[must_use]
577pub fn decode_string(buf: &[u8]) -> Option<(Vec<u8>, usize)> {
578 if buf.is_empty() {
579 return None;
580 }
581
582 let huffman_flag = (buf[0] & 0x80) != 0;
583 let (length, consumed) = decode_integer(buf, 7)?;
584 let length = length as usize;
585
586 if consumed + length > buf.len() {
587 return None;
588 }
589
590 let string_data = &buf[consumed..consumed + length];
591
592 let result = if huffman_flag {
593 huffman_decode(string_data)?
594 } else {
595 string_data.to_vec()
596 };
597
598 Some((result, consumed + length))
599}
600
601#[must_use]
605pub fn encode_string_literal(data: &[u8]) -> Vec<u8> {
606 let mut out = encode_integer(data.len() as u64, 7, 0x00); out.extend_from_slice(data);
608 out
609}
610
611#[must_use]
615pub fn encode_string_huffman(data: &[u8]) -> Vec<u8> {
616 let encoded = huffman_encode(data);
617 let mut out = encode_integer(encoded.len() as u64, 7, 0x80); out.extend_from_slice(&encoded);
619 out
620}
621
622fn entry_size(name: &str, value: &str) -> usize {
628 name.len() + value.len() + 32
629}
630
631pub struct HpackDecoder {
636 dynamic_table: Vec<(String, String)>,
638 max_table_size: usize,
640 current_size: usize,
642}
643
644impl Default for HpackDecoder {
645 fn default() -> Self {
646 Self::new()
647 }
648}
649
650impl HpackDecoder {
651 #[must_use]
653 pub fn new() -> Self {
654 HpackDecoder {
655 dynamic_table: Vec::new(),
656 max_table_size: 4096,
657 current_size: 0,
658 }
659 }
660
661 #[must_use]
663 pub fn with_max_size(max_table_size: usize) -> Self {
664 HpackDecoder {
665 dynamic_table: Vec::new(),
666 max_table_size,
667 current_size: 0,
668 }
669 }
670
671 pub fn set_max_table_size(&mut self, size: usize) {
673 self.max_table_size = size;
674 self.evict_to_fit(0);
675 }
676
677 #[must_use]
679 pub fn dynamic_table_len(&self) -> usize {
680 self.dynamic_table.len()
681 }
682
683 #[must_use]
685 pub fn dynamic_table_current_size(&self) -> usize {
686 self.current_size
687 }
688
689 #[must_use]
692 pub fn dynamic_table_entries(&self) -> &[(String, String)] {
693 &self.dynamic_table
694 }
695
696 pub fn decode(&mut self, buf: &[u8]) -> Result<Vec<(String, String)>, String> {
700 let mut headers = Vec::new();
701 let mut pos = 0;
702
703 while pos < buf.len() {
704 let first = buf[pos];
705
706 if (first & 0x80) != 0 {
707 let (idx, consumed) = decode_integer(&buf[pos..], 7)
710 .ok_or_else(|| "Failed to decode indexed header index".to_string())?;
711 pos += consumed;
712
713 let (name, value) = self
714 .table_entry(idx as usize)
715 .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
716 headers.push((name, value));
717 } else if (first & 0x40) != 0 {
718 let (idx, consumed) = decode_integer(&buf[pos..], 6)
721 .ok_or_else(|| "Failed to decode literal indexed name index".to_string())?;
722 pos += consumed;
723
724 let name = if idx == 0 {
725 let (name_bytes, nc) = decode_string(&buf[pos..])
727 .ok_or_else(|| "Failed to decode literal name string".to_string())?;
728 pos += nc;
729 String::from_utf8(name_bytes)
730 .map_err(|e| format!("Invalid UTF-8 in header name: {e}"))?
731 } else {
732 let (n, _) = self
733 .table_entry(idx as usize)
734 .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
735 n
736 };
737
738 let (value_bytes, vc) = decode_string(&buf[pos..])
739 .ok_or_else(|| "Failed to decode literal value string".to_string())?;
740 pos += vc;
741 let value = String::from_utf8(value_bytes)
742 .map_err(|e| format!("Invalid UTF-8 in header value: {e}"))?;
743
744 self.add_to_dynamic_table(name.clone(), value.clone());
745 headers.push((name, value));
746 } else if (first & 0x20) != 0 {
747 let (new_size, consumed) = decode_integer(&buf[pos..], 5)
750 .ok_or_else(|| "Failed to decode table size update".to_string())?;
751 pos += consumed;
752
753 if new_size as usize > self.max_table_size {
754 return Err(format!(
755 "Table size update {} exceeds max {}",
756 new_size, self.max_table_size
757 ));
758 }
759 self.evict_to_fit(0);
760 while self.current_size > new_size as usize {
762 if let Some(entry) = self.dynamic_table.pop() {
763 self.current_size = self
764 .current_size
765 .saturating_sub(entry_size(&entry.0, &entry.1));
766 } else {
767 break;
768 }
769 }
770 } else if (first & 0x10) != 0 {
771 let (idx, consumed) = decode_integer(&buf[pos..], 4)
774 .ok_or_else(|| "Failed to decode never-indexed name index".to_string())?;
775 pos += consumed;
776
777 let name = if idx == 0 {
778 let (name_bytes, nc) = decode_string(&buf[pos..])
779 .ok_or_else(|| "Failed to decode never-indexed name string".to_string())?;
780 pos += nc;
781 String::from_utf8(name_bytes)
782 .map_err(|e| format!("Invalid UTF-8 in header name: {e}"))?
783 } else {
784 let (n, _) = self
785 .table_entry(idx as usize)
786 .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
787 n
788 };
789
790 let (value_bytes, vc) = decode_string(&buf[pos..])
791 .ok_or_else(|| "Failed to decode never-indexed value string".to_string())?;
792 pos += vc;
793 let value = String::from_utf8(value_bytes)
794 .map_err(|e| format!("Invalid UTF-8 in header value: {e}"))?;
795
796 headers.push((name, value));
798 } else {
799 let (idx, consumed) = decode_integer(&buf[pos..], 4)
802 .ok_or_else(|| "Failed to decode without-indexing name index".to_string())?;
803 pos += consumed;
804
805 let name = if idx == 0 {
806 let (name_bytes, nc) = decode_string(&buf[pos..]).ok_or_else(|| {
807 "Failed to decode without-indexing name string".to_string()
808 })?;
809 pos += nc;
810 String::from_utf8(name_bytes)
811 .map_err(|e| format!("Invalid UTF-8 in header name: {e}"))?
812 } else {
813 let (n, _) = self
814 .table_entry(idx as usize)
815 .ok_or_else(|| format!("Invalid header table index: {idx}"))?;
816 n
817 };
818
819 let (value_bytes, vc) = decode_string(&buf[pos..])
820 .ok_or_else(|| "Failed to decode without-indexing value string".to_string())?;
821 pos += vc;
822 let value = String::from_utf8(value_bytes)
823 .map_err(|e| format!("Invalid UTF-8 in header value: {e}"))?;
824
825 headers.push((name, value));
827 }
828 }
829
830 Ok(headers)
831 }
832
833 fn dynamic_table_entry(&self, index: usize) -> Option<(&str, &str)> {
835 self.dynamic_table
836 .get(index)
837 .map(|(n, v)| (n.as_str(), v.as_str()))
838 }
839
840 fn static_table_entry(index: usize) -> Option<(&'static str, &'static str)> {
842 if index >= 1 && index <= STATIC_TABLE.len() {
843 Some(STATIC_TABLE[index - 1])
844 } else {
845 None
846 }
847 }
848
849 fn table_entry(&self, index: usize) -> Option<(String, String)> {
853 if index == 0 {
854 return None;
855 }
856
857 if index <= STATIC_TABLE.len() {
858 let (n, v) = Self::static_table_entry(index)?;
859 return Some((n.to_string(), v.to_string()));
860 }
861
862 let dynamic_idx = index - STATIC_TABLE.len() - 1;
863 let (n, v) = self.dynamic_table_entry(dynamic_idx)?;
864 Some((n.to_string(), v.to_string()))
865 }
866
867 fn add_to_dynamic_table(&mut self, name: String, value: String) {
869 let size = entry_size(&name, &value);
870 self.evict_to_fit(size);
871 if size <= self.max_table_size {
872 self.current_size += size;
873 self.dynamic_table.insert(0, (name, value));
874 }
875 }
876
877 fn evict_to_fit(&mut self, new_entry_size: usize) {
880 while !self.dynamic_table.is_empty()
881 && self.current_size + new_entry_size > self.max_table_size
882 {
883 if let Some(entry) = self.dynamic_table.pop() {
884 self.current_size = self
885 .current_size
886 .saturating_sub(entry_size(&entry.0, &entry.1));
887 }
888 }
889 }
890}
891
892pub struct HpackEncoder {
902 #[allow(dead_code)]
904 dynamic_table: Vec<(String, String)>,
905 #[allow(dead_code)]
907 max_table_size: usize,
908}
909
910impl Default for HpackEncoder {
911 fn default() -> Self {
912 Self::new()
913 }
914}
915
916impl HpackEncoder {
917 #[must_use]
919 pub fn new() -> Self {
920 HpackEncoder {
921 dynamic_table: Vec::new(),
922 max_table_size: 4096,
923 }
924 }
925
926 #[must_use]
932 pub fn encode(&self, headers: &[(&str, &str)]) -> Vec<u8> {
933 let mut out = Vec::new();
934
935 for &(name, value) in headers {
936 if let Some(static_idx) = self.find_static_exact(name, value) {
938 out.extend_from_slice(&encode_integer(static_idx as u64, 7, 0x80));
941 continue;
942 }
943
944 if let Some(name_idx) = self.find_static_name(name) {
946 out.extend_from_slice(&encode_integer(name_idx as u64, 6, 0x40));
949 out.extend_from_slice(&encode_string_literal(value.as_bytes()));
950 continue;
951 }
952
953 out.push(0x00);
956 out.extend_from_slice(&encode_string_literal(name.as_bytes()));
957 out.extend_from_slice(&encode_string_literal(value.as_bytes()));
958 }
959
960 out
961 }
962
963 #[must_use]
965 pub fn encode_huffman(&self, headers: &[(&str, &str)]) -> Vec<u8> {
966 let mut out = Vec::new();
967
968 for &(name, value) in headers {
969 if let Some(static_idx) = self.find_static_exact(name, value) {
971 out.extend_from_slice(&encode_integer(static_idx as u64, 7, 0x80));
972 continue;
973 }
974
975 out.push(0x00);
977 out.extend_from_slice(&encode_string_huffman(name.as_bytes()));
978 out.extend_from_slice(&encode_string_huffman(value.as_bytes()));
979 }
980
981 out
982 }
983
984 fn find_static_exact(&self, name: &str, value: &str) -> Option<usize> {
987 for (i, &(n, v)) in STATIC_TABLE.iter().enumerate() {
988 if n == name && v == value {
989 return Some(i + 1);
990 }
991 }
992 None
993 }
994
995 fn find_static_name(&self, name: &str) -> Option<usize> {
998 for (i, &(n, _)) in STATIC_TABLE.iter().enumerate() {
999 if n == name {
1000 return Some(i + 1);
1001 }
1002 }
1003 None
1004 }
1005}
1006
1007#[cfg(test)]
1012mod tests {
1013 use super::*;
1014
1015 #[test]
1020 fn test_decode_integer_small() {
1021 let buf = [0x05u8];
1023 let (val, consumed) = decode_integer(&buf, 5).unwrap();
1024 assert_eq!(val, 5);
1025 assert_eq!(consumed, 1);
1026 }
1027
1028 #[test]
1029 fn test_decode_integer_multi_byte() {
1030 let buf = [0b00011111u8, 0x9A, 0x0A];
1039 let (val, consumed) = decode_integer(&buf, 5).unwrap();
1040 assert_eq!(val, 1337);
1041 assert_eq!(consumed, 3);
1042 }
1043
1044 #[test]
1045 fn test_encode_integer_small() {
1046 let encoded = encode_integer(5, 5, 0x00);
1047 assert_eq!(encoded, vec![0x05]);
1048 }
1049
1050 #[test]
1051 fn test_encode_integer_large() {
1052 let encoded = encode_integer(1337, 5, 0x00);
1054 assert_eq!(encoded, vec![0b00011111, 0x9A, 0x0A]);
1055 }
1056
1057 #[test]
1058 fn test_encode_decode_integer_roundtrip() {
1059 for val in [0u64, 1, 30, 31, 127, 128, 255, 1000, 65535, 1337] {
1060 for prefix in [4u8, 5, 6, 7] {
1061 let encoded = encode_integer(val, prefix, 0x00);
1062 let (decoded, _) = decode_integer(&encoded, prefix).unwrap();
1063 assert_eq!(
1064 val, decoded,
1065 "Roundtrip failed for val={} prefix={}",
1066 val, prefix
1067 );
1068 }
1069 }
1070 }
1071
1072 #[test]
1077 fn test_decode_string_literal() {
1078 let mut buf = vec![0x05u8]; buf.extend_from_slice(b"hello");
1081 let (s, consumed) = decode_string(&buf).unwrap();
1082 assert_eq!(s, b"hello");
1083 assert_eq!(consumed, 6);
1084 }
1085
1086 #[test]
1087 fn test_huffman_encode_decode_roundtrip() {
1088 let original = b"www.example.com";
1089 let encoded = huffman_encode(original);
1090 let decoded = huffman_decode(&encoded).unwrap();
1091 assert_eq!(decoded, original);
1092 }
1093
1094 #[test]
1095 fn test_huffman_encode_empty() {
1096 let encoded = huffman_encode(b"");
1097 assert!(encoded.is_empty());
1098 }
1099
1100 #[test]
1101 fn test_huffman_decode_rfc7541_example() {
1102 let encoded = [
1105 0xf1u8, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
1106 ];
1107 let decoded = huffman_decode(&encoded).unwrap();
1108 assert_eq!(decoded, b"www.example.com");
1109 }
1110
1111 #[test]
1112 fn test_decode_string_huffman() {
1113 let original = b"no-cache";
1114 let encoded = huffman_encode(original);
1115 let mut buf = encode_integer(encoded.len() as u64, 7, 0x80);
1117 buf.extend_from_slice(&encoded);
1118 let (decoded, _) = decode_string(&buf).unwrap();
1119 assert_eq!(decoded, original);
1120 }
1121
1122 #[test]
1127 fn test_static_table_size() {
1128 assert_eq!(STATIC_TABLE.len(), 61);
1129 }
1130
1131 #[test]
1132 fn test_static_table_first_entry() {
1133 assert_eq!(STATIC_TABLE[0], (":authority", ""));
1134 }
1135
1136 #[test]
1137 fn test_static_table_method_get() {
1138 assert_eq!(STATIC_TABLE[1], (":method", "GET"));
1139 }
1140
1141 #[test]
1146 fn test_decoder_indexed_static() {
1147 let buf = [0x82u8];
1150 let mut decoder = HpackDecoder::new();
1151 let headers = decoder.decode(&buf).unwrap();
1152 assert_eq!(headers.len(), 1);
1153 assert_eq!(headers[0], (":method".to_string(), "GET".to_string()));
1154 }
1155
1156 #[test]
1157 fn test_decoder_literal_with_indexing() {
1158 let mut buf = vec![0x40u8]; buf.extend_from_slice(&encode_string_literal(b"custom-key"));
1162 buf.extend_from_slice(&encode_string_literal(b"custom-header"));
1163
1164 let mut decoder = HpackDecoder::new();
1165 let headers = decoder.decode(&buf).unwrap();
1166 assert_eq!(headers.len(), 1);
1167 assert_eq!(
1168 headers[0],
1169 ("custom-key".to_string(), "custom-header".to_string())
1170 );
1171 }
1172
1173 #[test]
1174 fn test_decoder_literal_without_indexing() {
1175 let mut buf = vec![0x00u8];
1177 buf.extend_from_slice(&encode_string_literal(b"x-custom"));
1178 buf.extend_from_slice(&encode_string_literal(b"value"));
1179
1180 let mut decoder = HpackDecoder::new();
1181 let headers = decoder.decode(&buf).unwrap();
1182 assert_eq!(headers.len(), 1);
1183 assert_eq!(headers[0], ("x-custom".to_string(), "value".to_string()));
1184 }
1185
1186 #[test]
1187 fn test_decoder_dynamic_table_eviction() {
1188 let mut decoder = HpackDecoder::with_max_size(64);
1189 let mut buf = vec![0x40u8];
1191 buf.extend_from_slice(&encode_string_literal(b"x-a"));
1192 buf.extend_from_slice(&encode_string_literal(b"b"));
1193 decoder.decode(&buf).unwrap();
1194 assert_eq!(decoder.dynamic_table.len(), 1);
1195
1196 let mut buf2 = vec![0x40u8];
1198 buf2.extend_from_slice(&encode_string_literal(b"x-c"));
1199 buf2.extend_from_slice(&encode_string_literal(b"d"));
1200 decoder.decode(&buf2).unwrap();
1201 assert!(decoder.dynamic_table.len() <= 2);
1203 }
1204
1205 #[test]
1210 fn test_encoder_static_exact_match() {
1211 let encoder = HpackEncoder::new();
1212 let encoded = encoder.encode(&[(":method", "GET")]);
1214 assert_eq!(encoded, vec![0x82]);
1215 }
1216
1217 #[test]
1218 fn test_encoder_static_name_match() {
1219 let encoder = HpackEncoder::new();
1220 let encoded = encoder.encode(&[(":method", "DELETE")]);
1222 assert_eq!(encoded[0], 0x42); }
1225
1226 #[test]
1227 fn test_encoder_roundtrip() {
1228 let encoder = HpackEncoder::new();
1229 let mut decoder = HpackDecoder::new();
1230
1231 let headers = vec![
1232 (":method", "GET"),
1233 (":path", "/"),
1234 (":scheme", "https"),
1235 ("user-agent", "test/1.0"),
1236 ("x-custom", "value"),
1237 ];
1238 let encoded = encoder.encode(&headers);
1239 let decoded = decoder.decode(&encoded).unwrap();
1240
1241 assert_eq!(decoded.len(), headers.len());
1242 for (i, (name, value)) in headers.iter().enumerate() {
1243 assert_eq!(decoded[i].0, *name);
1244 assert_eq!(decoded[i].1, *value);
1245 }
1246 }
1247
1248 #[test]
1249 fn test_encoder_huffman_roundtrip() {
1250 let encoder = HpackEncoder::new();
1251 let mut decoder = HpackDecoder::new();
1252
1253 let headers = vec![
1254 ("x-request-id", "abc-123"),
1255 ("content-type", "application/json"),
1256 ];
1257 let encoded = encoder.encode_huffman(&headers);
1258 let decoded = decoder.decode(&encoded).unwrap();
1259
1260 assert_eq!(decoded.len(), headers.len());
1261 for (i, (name, value)) in headers.iter().enumerate() {
1262 assert_eq!(decoded[i].0, *name);
1263 assert_eq!(decoded[i].1, *value);
1264 }
1265 }
1266}