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 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 return None;
409 }
410 }
411
412 Some((value, i))
413}
414
415#[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 return vec![prefix_byte | (value as u8)];
431 }
432
433 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#[derive(Default)]
455struct HuffmanNode {
456 symbol: Option<u16>, 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
469fn 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 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#[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 return None;
515 }
516 output.push(sym as u8);
517 node = &root;
518 }
519 }
520 }
521
522 Some(output)
531}
532
533#[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 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#[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#[must_use]
611pub fn encode_string_literal(data: &[u8]) -> Vec<u8> {
612 let mut out = encode_integer(data.len() as u64, 7, 0x00); out.extend_from_slice(data);
614 out
615}
616
617#[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); out.extend_from_slice(&encoded);
625 out
626}
627
628fn entry_size(name: &str, value: &str) -> usize {
634 name.len() + value.len() + 32
635}
636
637pub struct HpackDecoder {
642 dynamic_table: Vec<(String, String)>,
644 max_table_size: usize,
646 current_size: usize,
648}
649
650impl Default for HpackDecoder {
651 fn default() -> Self {
652 Self::new()
653 }
654}
655
656impl HpackDecoder {
657 #[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 #[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 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 #[must_use]
685 pub fn dynamic_table_len(&self) -> usize {
686 self.dynamic_table.len()
687 }
688
689 #[must_use]
691 pub fn dynamic_table_current_size(&self) -> usize {
692 self.current_size
693 }
694
695 #[must_use]
698 pub fn dynamic_table_entries(&self) -> &[(String, String)] {
699 &self.dynamic_table
700 }
701
702 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 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 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 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 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 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 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 headers.push((name, value));
804 } else {
805 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 headers.push((name, value));
833 }
834 }
835
836 Ok(headers)
837 }
838
839 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 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 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 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 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
898pub struct HpackEncoder {
908 #[allow(dead_code)]
910 dynamic_table: Vec<(String, String)>,
911 #[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 #[must_use]
925 pub fn new() -> Self {
926 HpackEncoder {
927 dynamic_table: Vec::new(),
928 max_table_size: 4096,
929 }
930 }
931
932 #[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 if let Some(static_idx) = self.find_static_exact(name, value) {
944 out.extend_from_slice(&encode_integer(static_idx as u64, 7, 0x80));
947 continue;
948 }
949
950 if let Some(name_idx) = self.find_static_name(name) {
952 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 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 #[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 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 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 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 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#[cfg(test)]
1018mod tests {
1019 use super::*;
1020
1021 #[test]
1026 fn test_decode_integer_small() {
1027 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 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 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 #[test]
1083 fn test_decode_string_literal() {
1084 let mut buf = vec![0x05u8]; 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 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 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 #[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 #[test]
1152 fn test_decoder_indexed_static() {
1153 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 let mut buf = vec![0x40u8]; 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 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 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 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 assert!(decoder.dynamic_table.len() <= 2);
1209 }
1210
1211 #[test]
1216 fn test_encoder_static_exact_match() {
1217 let encoder = HpackEncoder::new();
1218 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 let encoded = encoder.encode(&[(":method", "DELETE")]);
1228 assert_eq!(encoded[0], 0x42); }
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}