http: hpack primitive types 44/42344/9
authorMatus Fabian <[email protected]>
Fri, 7 Feb 2025 19:34:42 +0000 (14:34 -0500)
committerFlorin Coras <[email protected]>
Wed, 19 Feb 2025 06:01:58 +0000 (06:01 +0000)
RFC7541 section 5, Huffman encoding included

Type: feature

Change-Id: I5a024f654610729b8f3f53734aa242bb4b5a25fe
Signed-off-by: Matus Fabian <[email protected]>
src/plugins/http/CMakeLists.txt
src/plugins/http/extras/mk_huffman_table.py [new file with mode: 0644]
src/plugins/http/http2/hpack.c [new file with mode: 0644]
src/plugins/http/http2/hpack.h [new file with mode: 0644]
src/plugins/http/http2/huffman_table.h [new file with mode: 0644]
src/plugins/http/test/http_test.c

index ad58482..95b5102 100644 (file)
@@ -13,6 +13,7 @@
 
 add_vpp_plugin(http
   SOURCES
+  http2/hpack.c
   http.c
   http_buffer.c
   http_timer.c
diff --git a/src/plugins/http/extras/mk_huffman_table.py b/src/plugins/http/extras/mk_huffman_table.py
new file mode 100644 (file)
index 0000000..378544b
--- /dev/null
@@ -0,0 +1,416 @@
+#!/usr/bin/env python3
+from io import StringIO
+
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2025 Cisco Systems, Inc.
+
+
+# e.g. 5 bit code symbol has 8 slots (2^8-5), last 3 bits are irrelevant
+def generate_slots(fh, s, cl):
+    for i in range(1 << 8 - cl):
+        fh.write("  { 0x%02X, %d },\n" % (s, cl))
+
+
+# list of code and code length tuples
+huff_code_table = []
+
+# Huffman code from RFC7541 Appendix B, EOS removed
+rfc7541_huffman_code = """\
+    (  0)  |11111111|11000                             1ff8  [13]
+    (  1)  |11111111|11111111|1011000                7fffd8  [23]
+    (  2)  |11111111|11111111|11111110|0010         fffffe2  [28]
+    (  3)  |11111111|11111111|11111110|0011         fffffe3  [28]
+    (  4)  |11111111|11111111|11111110|0100         fffffe4  [28]
+    (  5)  |11111111|11111111|11111110|0101         fffffe5  [28]
+    (  6)  |11111111|11111111|11111110|0110         fffffe6  [28]
+    (  7)  |11111111|11111111|11111110|0111         fffffe7  [28]
+    (  8)  |11111111|11111111|11111110|1000         fffffe8  [28]
+    (  9)  |11111111|11111111|11101010               ffffea  [24]
+    ( 10)  |11111111|11111111|11111111|111100      3ffffffc  [30]
+    ( 11)  |11111111|11111111|11111110|1001         fffffe9  [28]
+    ( 12)  |11111111|11111111|11111110|1010         fffffea  [28]
+    ( 13)  |11111111|11111111|11111111|111101      3ffffffd  [30]
+    ( 14)  |11111111|11111111|11111110|1011         fffffeb  [28]
+    ( 15)  |11111111|11111111|11111110|1100         fffffec  [28]
+    ( 16)  |11111111|11111111|11111110|1101         fffffed  [28]
+    ( 17)  |11111111|11111111|11111110|1110         fffffee  [28]
+    ( 18)  |11111111|11111111|11111110|1111         fffffef  [28]
+    ( 19)  |11111111|11111111|11111111|0000         ffffff0  [28]
+    ( 20)  |11111111|11111111|11111111|0001         ffffff1  [28]
+    ( 21)  |11111111|11111111|11111111|0010         ffffff2  [28]
+    ( 22)  |11111111|11111111|11111111|111110      3ffffffe  [30]
+    ( 23)  |11111111|11111111|11111111|0011         ffffff3  [28]
+    ( 24)  |11111111|11111111|11111111|0100         ffffff4  [28]
+    ( 25)  |11111111|11111111|11111111|0101         ffffff5  [28]
+    ( 26)  |11111111|11111111|11111111|0110         ffffff6  [28]
+    ( 27)  |11111111|11111111|11111111|0111         ffffff7  [28]
+    ( 28)  |11111111|11111111|11111111|1000         ffffff8  [28]
+    ( 29)  |11111111|11111111|11111111|1001         ffffff9  [28]
+    ( 30)  |11111111|11111111|11111111|1010         ffffffa  [28]
+    ( 31)  |11111111|11111111|11111111|1011         ffffffb  [28]
+' ' ( 32)  |010100                                       14  [ 6]
+'!' ( 33)  |11111110|00                                 3f8  [10]
+'"' ( 34)  |11111110|01                                 3f9  [10]
+'#' ( 35)  |11111111|1010                               ffa  [12]
+'$' ( 36)  |11111111|11001                             1ff9  [13]
+'%' ( 37)  |010101                                       15  [ 6]
+'&' ( 38)  |11111000                                     f8  [ 8]
+''' ( 39)  |11111111|010                                7fa  [11]
+'(' ( 40)  |11111110|10                                 3fa  [10]
+')' ( 41)  |11111110|11                                 3fb  [10]
+'*' ( 42)  |11111001                                     f9  [ 8]
+'+' ( 43)  |11111111|011                                7fb  [11]
+',' ( 44)  |11111010                                     fa  [ 8]
+'-' ( 45)  |010110                                       16  [ 6]
+'.' ( 46)  |010111                                       17  [ 6]
+'/' ( 47)  |011000                                       18  [ 6]
+'0' ( 48)  |00000                                         0  [ 5]
+'1' ( 49)  |00001                                         1  [ 5]
+'2' ( 50)  |00010                                         2  [ 5]
+'3' ( 51)  |011001                                       19  [ 6]
+'4' ( 52)  |011010                                       1a  [ 6]
+'5' ( 53)  |011011                                       1b  [ 6]
+'6' ( 54)  |011100                                       1c  [ 6]
+'7' ( 55)  |011101                                       1d  [ 6]
+'8' ( 56)  |011110                                       1e  [ 6]
+'9' ( 57)  |011111                                       1f  [ 6]
+':' ( 58)  |1011100                                      5c  [ 7]
+';' ( 59)  |11111011                                     fb  [ 8]
+'<' ( 60)  |11111111|1111100                           7ffc  [15]
+'=' ( 61)  |100000                                       20  [ 6]
+'>' ( 62)  |11111111|1011                               ffb  [12]
+'?' ( 63)  |11111111|00                                 3fc  [10]
+'@' ( 64)  |11111111|11010                             1ffa  [13]
+'A' ( 65)  |100001                                       21  [ 6]
+'B' ( 66)  |1011101                                      5d  [ 7]
+'C' ( 67)  |1011110                                      5e  [ 7]
+'D' ( 68)  |1011111                                      5f  [ 7]
+'E' ( 69)  |1100000                                      60  [ 7]
+'F' ( 70)  |1100001                                      61  [ 7]
+'G' ( 71)  |1100010                                      62  [ 7]
+'H' ( 72)  |1100011                                      63  [ 7]
+'I' ( 73)  |1100100                                      64  [ 7]
+'J' ( 74)  |1100101                                      65  [ 7]
+'K' ( 75)  |1100110                                      66  [ 7]
+'L' ( 76)  |1100111                                      67  [ 7]
+'M' ( 77)  |1101000                                      68  [ 7]
+'N' ( 78)  |1101001                                      69  [ 7]
+'O' ( 79)  |1101010                                      6a  [ 7]
+'P' ( 80)  |1101011                                      6b  [ 7]
+'Q' ( 81)  |1101100                                      6c  [ 7]
+'R' ( 82)  |1101101                                      6d  [ 7]
+'S' ( 83)  |1101110                                      6e  [ 7]
+'T' ( 84)  |1101111                                      6f  [ 7]
+'U' ( 85)  |1110000                                      70  [ 7]
+'V' ( 86)  |1110001                                      71  [ 7]
+'W' ( 87)  |1110010                                      72  [ 7]
+'X' ( 88)  |11111100                                     fc  [ 8]
+'Y' ( 89)  |1110011                                      73  [ 7]
+'Z' ( 90)  |11111101                                     fd  [ 8]
+'[' ( 91)  |11111111|11011                             1ffb  [13]
+'\' ( 92)  |11111111|11111110|000                     7fff0  [19]
+']' ( 93)  |11111111|11100                             1ffc  [13]
+'^' ( 94)  |11111111|111100                            3ffc  [14]
+'_' ( 95)  |100010                                       22  [ 6]
+'`' ( 96)  |11111111|1111101                           7ffd  [15]
+'a' ( 97)  |00011                                         3  [ 5]
+'b' ( 98)  |100011                                       23  [ 6]
+'c' ( 99)  |00100                                         4  [ 5]
+'d' (100)  |100100                                       24  [ 6]
+'e' (101)  |00101                                         5  [ 5]
+'f' (102)  |100101                                       25  [ 6]
+'g' (103)  |100110                                       26  [ 6]
+'h' (104)  |100111                                       27  [ 6]
+'i' (105)  |00110                                         6  [ 5]
+'j' (106)  |1110100                                      74  [ 7]
+'k' (107)  |1110101                                      75  [ 7]
+'l' (108)  |101000                                       28  [ 6]
+'m' (109)  |101001                                       29  [ 6]
+'n' (110)  |101010                                       2a  [ 6]
+'o' (111)  |00111                                         7  [ 5]
+'p' (112)  |101011                                       2b  [ 6]
+'q' (113)  |1110110                                      76  [ 7]
+'r' (114)  |101100                                       2c  [ 6]
+'s' (115)  |01000                                         8  [ 5]
+'t' (116)  |01001                                         9  [ 5]
+'u' (117)  |101101                                       2d  [ 6]
+'v' (118)  |1110111                                      77  [ 7]
+'w' (119)  |1111000                                      78  [ 7]
+'x' (120)  |1111001                                      79  [ 7]
+'y' (121)  |1111010                                      7a  [ 7]
+'z' (122)  |1111011                                      7b  [ 7]
+'{' (123)  |11111111|1111110                           7ffe  [15]
+'|' (124)  |11111111|100                                7fc  [11]
+'}' (125)  |11111111|111101                            3ffd  [14]
+'~' (126)  |11111111|11101                             1ffd  [13]
+    (127)  |11111111|11111111|11111111|1100         ffffffc  [28]
+    (128)  |11111111|11111110|0110                    fffe6  [20]
+    (129)  |11111111|11111111|010010                 3fffd2  [22]
+    (130)  |11111111|11111110|0111                    fffe7  [20]
+    (131)  |11111111|11111110|1000                    fffe8  [20]
+    (132)  |11111111|11111111|010011                 3fffd3  [22]
+    (133)  |11111111|11111111|010100                 3fffd4  [22]
+    (134)  |11111111|11111111|010101                 3fffd5  [22]
+    (135)  |11111111|11111111|1011001                7fffd9  [23]
+    (136)  |11111111|11111111|010110                 3fffd6  [22]
+    (137)  |11111111|11111111|1011010                7fffda  [23]
+    (138)  |11111111|11111111|1011011                7fffdb  [23]
+    (139)  |11111111|11111111|1011100                7fffdc  [23]
+    (140)  |11111111|11111111|1011101                7fffdd  [23]
+    (141)  |11111111|11111111|1011110                7fffde  [23]
+    (142)  |11111111|11111111|11101011               ffffeb  [24]
+    (143)  |11111111|11111111|1011111                7fffdf  [23]
+    (144)  |11111111|11111111|11101100               ffffec  [24]
+    (145)  |11111111|11111111|11101101               ffffed  [24]
+    (146)  |11111111|11111111|010111                 3fffd7  [22]
+    (147)  |11111111|11111111|1100000                7fffe0  [23]
+    (148)  |11111111|11111111|11101110               ffffee  [24]
+    (149)  |11111111|11111111|1100001                7fffe1  [23]
+    (150)  |11111111|11111111|1100010                7fffe2  [23]
+    (151)  |11111111|11111111|1100011                7fffe3  [23]
+    (152)  |11111111|11111111|1100100                7fffe4  [23]
+    (153)  |11111111|11111110|11100                  1fffdc  [21]
+    (154)  |11111111|11111111|011000                 3fffd8  [22]
+    (155)  |11111111|11111111|1100101                7fffe5  [23]
+    (156)  |11111111|11111111|011001                 3fffd9  [22]
+    (157)  |11111111|11111111|1100110                7fffe6  [23]
+    (158)  |11111111|11111111|1100111                7fffe7  [23]
+    (159)  |11111111|11111111|11101111               ffffef  [24]
+    (160)  |11111111|11111111|011010                 3fffda  [22]
+    (161)  |11111111|11111110|11101                  1fffdd  [21]
+    (162)  |11111111|11111110|1001                    fffe9  [20]
+    (163)  |11111111|11111111|011011                 3fffdb  [22]
+    (164)  |11111111|11111111|011100                 3fffdc  [22]
+    (165)  |11111111|11111111|1101000                7fffe8  [23]
+    (166)  |11111111|11111111|1101001                7fffe9  [23]
+    (167)  |11111111|11111110|11110                  1fffde  [21]
+    (168)  |11111111|11111111|1101010                7fffea  [23]
+    (169)  |11111111|11111111|011101                 3fffdd  [22]
+    (170)  |11111111|11111111|011110                 3fffde  [22]
+    (171)  |11111111|11111111|11110000               fffff0  [24]
+    (172)  |11111111|11111110|11111                  1fffdf  [21]
+    (173)  |11111111|11111111|011111                 3fffdf  [22]
+    (174)  |11111111|11111111|1101011                7fffeb  [23]
+    (175)  |11111111|11111111|1101100                7fffec  [23]
+    (176)  |11111111|11111111|00000                  1fffe0  [21]
+    (177)  |11111111|11111111|00001                  1fffe1  [21]
+    (178)  |11111111|11111111|100000                 3fffe0  [22]
+    (179)  |11111111|11111111|00010                  1fffe2  [21]
+    (180)  |11111111|11111111|1101101                7fffed  [23]
+    (181)  |11111111|11111111|100001                 3fffe1  [22]
+    (182)  |11111111|11111111|1101110                7fffee  [23]
+    (183)  |11111111|11111111|1101111                7fffef  [23]
+    (184)  |11111111|11111110|1010                    fffea  [20]
+    (185)  |11111111|11111111|100010                 3fffe2  [22]
+    (186)  |11111111|11111111|100011                 3fffe3  [22]
+    (187)  |11111111|11111111|100100                 3fffe4  [22]
+    (188)  |11111111|11111111|1110000                7ffff0  [23]
+    (189)  |11111111|11111111|100101                 3fffe5  [22]
+    (190)  |11111111|11111111|100110                 3fffe6  [22]
+    (191)  |11111111|11111111|1110001                7ffff1  [23]
+    (192)  |11111111|11111111|11111000|00           3ffffe0  [26]
+    (193)  |11111111|11111111|11111000|01           3ffffe1  [26]
+    (194)  |11111111|11111110|1011                    fffeb  [20]
+    (195)  |11111111|11111110|001                     7fff1  [19]
+    (196)  |11111111|11111111|100111                 3fffe7  [22]
+    (197)  |11111111|11111111|1110010                7ffff2  [23]
+    (198)  |11111111|11111111|101000                 3fffe8  [22]
+    (199)  |11111111|11111111|11110110|0            1ffffec  [25]
+    (200)  |11111111|11111111|11111000|10           3ffffe2  [26]
+    (201)  |11111111|11111111|11111000|11           3ffffe3  [26]
+    (202)  |11111111|11111111|11111001|00           3ffffe4  [26]
+    (203)  |11111111|11111111|11111011|110          7ffffde  [27]
+    (204)  |11111111|11111111|11111011|111          7ffffdf  [27]
+    (205)  |11111111|11111111|11111001|01           3ffffe5  [26]
+    (206)  |11111111|11111111|11110001               fffff1  [24]
+    (207)  |11111111|11111111|11110110|1            1ffffed  [25]
+    (208)  |11111111|11111110|010                     7fff2  [19]
+    (209)  |11111111|11111111|00011                  1fffe3  [21]
+    (210)  |11111111|11111111|11111001|10           3ffffe6  [26]
+    (211)  |11111111|11111111|11111100|000          7ffffe0  [27]
+    (212)  |11111111|11111111|11111100|001          7ffffe1  [27]
+    (213)  |11111111|11111111|11111001|11           3ffffe7  [26]
+    (214)  |11111111|11111111|11111100|010          7ffffe2  [27]
+    (215)  |11111111|11111111|11110010               fffff2  [24]
+    (216)  |11111111|11111111|00100                  1fffe4  [21]
+    (217)  |11111111|11111111|00101                  1fffe5  [21]
+    (218)  |11111111|11111111|11111010|00           3ffffe8  [26]
+    (219)  |11111111|11111111|11111010|01           3ffffe9  [26]
+    (220)  |11111111|11111111|11111111|1101         ffffffd  [28]
+    (221)  |11111111|11111111|11111100|011          7ffffe3  [27]
+    (222)  |11111111|11111111|11111100|100          7ffffe4  [27]
+    (223)  |11111111|11111111|11111100|101          7ffffe5  [27]
+    (224)  |11111111|11111110|1100                    fffec  [20]
+    (225)  |11111111|11111111|11110011               fffff3  [24]
+    (226)  |11111111|11111110|1101                    fffed  [20]
+    (227)  |11111111|11111111|00110                  1fffe6  [21]
+    (228)  |11111111|11111111|101001                 3fffe9  [22]
+    (229)  |11111111|11111111|00111                  1fffe7  [21]
+    (230)  |11111111|11111111|01000                  1fffe8  [21]
+    (231)  |11111111|11111111|1110011                7ffff3  [23]
+    (232)  |11111111|11111111|101010                 3fffea  [22]
+    (233)  |11111111|11111111|101011                 3fffeb  [22]
+    (234)  |11111111|11111111|11110111|0            1ffffee  [25]
+    (235)  |11111111|11111111|11110111|1            1ffffef  [25]
+    (236)  |11111111|11111111|11110100               fffff4  [24]
+    (237)  |11111111|11111111|11110101               fffff5  [24]
+    (238)  |11111111|11111111|11111010|10           3ffffea  [26]
+    (239)  |11111111|11111111|1110100                7ffff4  [23]
+    (240)  |11111111|11111111|11111010|11           3ffffeb  [26]
+    (241)  |11111111|11111111|11111100|110          7ffffe6  [27]
+    (242)  |11111111|11111111|11111011|00           3ffffec  [26]
+    (243)  |11111111|11111111|11111011|01           3ffffed  [26]
+    (244)  |11111111|11111111|11111100|111          7ffffe7  [27]
+    (245)  |11111111|11111111|11111101|000          7ffffe8  [27]
+    (246)  |11111111|11111111|11111101|001          7ffffe9  [27]
+    (247)  |11111111|11111111|11111101|010          7ffffea  [27]
+    (248)  |11111111|11111111|11111101|011          7ffffeb  [27]
+    (249)  |11111111|11111111|11111111|1110         ffffffe  [28]
+    (250)  |11111111|11111111|11111101|100          7ffffec  [27]
+    (251)  |11111111|11111111|11111101|101          7ffffed  [27]
+    (252)  |11111111|11111111|11111101|110          7ffffee  [27]
+    (253)  |11111111|11111111|11111101|111          7ffffef  [27]
+    (254)  |11111111|11111111|11111110|000          7fffff0  [27]
+    (255)  |11111111|11111111|11111011|10           3ffffee  [26]"""
+
+# parse Huffman code
+for line in StringIO(rfc7541_huffman_code):
+    # we need just last two columns
+    l = line.rstrip().split("  ")
+    # len in bits
+    code_len = l[-1][1:-1].strip()
+    # code as hex aligned to LSB
+    code = l[-2].strip()
+    huff_code_table.append((code_len, code))
+
+f = open("../http2/huffman_table.h", "w")
+f.write(
+    """/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+/* generated by mk_huffman_table.py */
+
+#ifndef SRC_PLUGINS_HTTP_HUFFMAN_TABLE_H_
+#define SRC_PLUGINS_HTTP_HUFFMAN_TABLE_H_
+
+#include <vppinfra/types.h>
+
+typedef struct
+{
+  u8 code_len;
+  u32 code;
+} hpack_huffman_symbol_t;
+
+static hpack_huffman_symbol_t huff_sym_table[] = {
+"""
+)
+
+# encoding table
+[f.write("  {" + code[0] + ", 0x" + code[1] + "},\n") for code in huff_code_table]
+
+f.write(
+    """};
+
+typedef struct
+{
+  u8 symbol;
+  u8 code_len;
+} hpack_huffman_code_t;
+
+static hpack_huffman_code_t huff_code_table_fast[] = {
+"""
+)
+
+# fast decoding table, symbols with code length from 5 to 8 bits (most of printable ASCII characters)
+[generate_slots(f, i, 5) for i, code in enumerate(huff_code_table) if code[0] == "5"]
+[generate_slots(f, i, 6) for i, code in enumerate(huff_code_table) if code[0] == "6"]
+[generate_slots(f, i, 7) for i, code in enumerate(huff_code_table) if code[0] == "7"]
+[generate_slots(f, i, 8) for i, code in enumerate(huff_code_table) if code[0] == "8"]
+
+# last 2 entries are longer codes prefixes, code_len set to 0
+f.write("  { 0x00, 0 },\n")
+f.write("  { 0x00, 0 },\n")
+
+f.write(
+    """};
+
+typedef struct
+{
+  u32 first_code;
+  u8 code_len;
+  u8 symbols[29];
+} hpack_huffman_group_t;
+
+/* clang-format off */
+
+static hpack_huffman_group_t huff_code_table_slow[] = {
+"""
+)
+for i in range(10, 31):
+    symbols = [
+        (symbol, code[1])
+        for symbol, code in enumerate(huff_code_table)
+        if code[0] == str(i)
+    ]
+    if symbols:
+        _, first_code = symbols[0]
+        f.write("  {\n    0x" + first_code + ", /* first_code */\n")
+        f.write("    " + str(i) + ", /* code_len */\n")
+        f.write("    {\n     ")
+        [f.write(" 0x%02X," % s) for s, c in symbols[:10]]
+        if len(symbols) > 10:
+            f.write("\n     ")
+            [f.write(" 0x%02X," % s) for s, c in symbols[10:20]]
+            if len(symbols) > 20:
+                f.write("\n     ")
+                [f.write(" 0x%02X," % s) for s, c in symbols[20:30]]
+        f.write("\n    } /* symbols */\n  },\n")
+
+f.write(
+    """};
+
+/* clang format-on */
+
+always_inline hpack_huffman_group_t *
+hpack_huffman_get_group (u32 value)
+{
+"""
+)
+
+index = 0
+
+symbols = [
+    (symbol, code[1]) for symbol, code in enumerate(huff_code_table) if code[0] == "10"
+]
+_, last_code = symbols[-1]
+boundary = (int(last_code, 16) + 1) << 22
+f.write("  if (value < 0x%X)\n" % boundary)
+f.write("    return &huff_code_table_slow[%d];\n" % index)
+index += 1
+
+for i in range(11, 30):
+    symbols = [
+        (symbol, code[1])
+        for symbol, code in enumerate(huff_code_table)
+        if code[0] == str(i)
+    ]
+    if symbols:
+        _, last_code = symbols[-1]
+        boundary = (int(last_code, 16) + 1) << (32 - i)
+        f.write("  else if (value < 0x%X)\n" % boundary)
+        f.write("    return &huff_code_table_slow[%d];\n" % index)
+        index += 1
+
+f.write("  else\n")
+f.write("    return &huff_code_table_slow[%d];\n" % index)
+
+f.write(
+    """}
+
+#endif /* SRC_PLUGINS_HTTP_HUFFMAN_TABLE_H_ */
+"""
+)
+
+f.close()
diff --git a/src/plugins/http/http2/hpack.c b/src/plugins/http/http2/hpack.c
new file mode 100644 (file)
index 0000000..f279bd9
--- /dev/null
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vppinfra/error.h>
+#include <http/http2/hpack.h>
+#include <http/http2/huffman_table.h>
+
+__clib_export uword
+hpack_decode_int (u8 **src, u8 *end, u8 prefix_len)
+{
+  uword value, new_value;
+  u8 *p, shift = 0, byte;
+  u16 prefix_max;
+
+  ASSERT (*src < end);
+  ASSERT (prefix_len >= 1 && prefix_len <= 8);
+
+  p = *src;
+  prefix_max = (1 << prefix_len) - 1;
+  value = *p & (u8) prefix_max;
+  p++;
+  /* if integer value is less than 2^prefix_len-1 it's encoded within prefix */
+  if (value != prefix_max)
+    {
+      *src = p;
+      return value;
+    }
+
+  while (p != end)
+    {
+      byte = *p;
+      p++;
+      new_value = value + ((uword) (byte & 0x7F) << shift);
+      shift += 7;
+      /* check for overflow */
+      if (new_value < value)
+       return HPACK_INVALID_INT;
+      value = new_value;
+      /* MSB of the last byte is zero */
+      if ((byte & 0x80) == 0)
+       {
+         *src = p;
+         return value;
+       }
+    }
+
+  return HPACK_INVALID_INT;
+}
+
+int
+hpack_decode_huffman (u8 **src, u8 *end, u8 **buf, uword *buf_len)
+{
+  u64 accumulator = 0;
+  u8 accumulator_len = 0;
+  u8 *p;
+  hpack_huffman_code_t *code;
+
+  p = *src;
+  while (1)
+    {
+      /* out of space?  */
+      if (*buf_len == 0)
+       return -1;
+      /* refill */
+      while (p < end && accumulator_len <= 56)
+       {
+         accumulator <<= 8;
+         accumulator_len += 8;
+         accumulator |= (u64) *p++;
+       }
+      /* first try short codes (5 - 8 bits) */
+      code =
+       &huff_code_table_fast[(u8) (accumulator >> (accumulator_len - 8))];
+      /* zero code length mean no luck */
+      if (PREDICT_TRUE (code->code_len))
+       {
+         **buf = code->symbol;
+         (*buf)++;
+         (*buf_len)--;
+         accumulator_len -= code->code_len;
+       }
+      else
+       {
+         /* slow path / long codes (10 - 30 bits) */
+         u32 tmp;
+         /* group boundaries are aligned to 32 bits */
+         if (accumulator_len < 32)
+           tmp = accumulator << (32 - accumulator_len);
+         else
+           tmp = accumulator >> (accumulator_len - 32);
+         /* figure out which interval code falls into, this is possible
+          * because HPACK use canonical Huffman codes
+          * see Schwartz, E. and B. Kallick, “Generating a canonical prefix
+          * encoding”
+          */
+         hpack_huffman_group_t *hg = hpack_huffman_get_group (tmp);
+         /* trim code to correct length */
+         u32 code = (accumulator >> (accumulator_len - hg->code_len)) &
+                    ((1 << hg->code_len) - 1);
+         /* find symbol in the list */
+         **buf = hg->symbols[code - hg->first_code];
+         (*buf)++;
+         (*buf_len)--;
+         accumulator_len -= hg->code_len;
+       }
+      /* all done */
+      if (p == end && accumulator_len < 8)
+       {
+         /* there might be one more symbol encoded with short code */
+         if (accumulator_len >= 5)
+           {
+             /* first check EOF case */
+             if (((1 << accumulator_len) - 1) ==
+                 (accumulator & ((1 << accumulator_len) - 1)))
+               break;
+
+             /* out of space?  */
+             if (*buf_len == 0)
+               return -1;
+
+             /* if bogus EOF check bellow will fail */
+             code = &huff_code_table_fast[(u8) (accumulator
+                                                << (8 - accumulator_len))];
+             **buf = code->symbol;
+             (*buf)++;
+             (*buf_len)--;
+             accumulator_len -= code->code_len;
+             /* end at byte boundary? */
+             if (accumulator_len == 0)
+               break;
+           }
+         /* we must end with EOF here */
+         if (((1 << accumulator_len) - 1) !=
+             (accumulator & ((1 << accumulator_len) - 1)))
+           return -1;
+         break;
+       }
+    }
+  return 0;
+}
+
+__clib_export int
+hpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len)
+{
+  u8 *p, is_huffman;
+  uword len;
+
+  ASSERT (*src < end);
+
+  p = *src;
+  /* H flag in first bit */
+  is_huffman = *p & 0x80;
+
+  /* length is integer with 7 bit prefix */
+  len = hpack_decode_int (&p, end, 7);
+  if (PREDICT_FALSE (len == HPACK_INVALID_INT))
+    return -1;
+
+  /* do we have everything? */
+  if (len > (end - p))
+    return -1;
+
+  if (is_huffman)
+    {
+      *src = (p + len);
+      return hpack_decode_huffman (&p, p + len, buf, buf_len);
+    }
+  else
+    {
+      /* enough space? */
+      if (len > *buf_len)
+       return -1;
+
+      clib_memcpy (*buf, p, len);
+      *buf_len -= len;
+      *buf += len;
+      *src = (p + len);
+      return 0;
+    }
+}
+
+__clib_export u8 *
+hpack_encode_int (u8 *dst, uword value, u8 prefix_len)
+{
+  u16 prefix_max;
+
+  ASSERT (prefix_len >= 1 && prefix_len <= 8);
+
+  prefix_max = (1 << prefix_len) - 1;
+
+  /* if integer value is less than 2^prefix_len-1 it's encoded within prefix */
+  if (value < prefix_max)
+    {
+      *dst++ |= (u8) value;
+      return dst;
+    }
+
+  /* otherwise all bits of the prefix are set to 1 */
+  *dst++ |= (u8) prefix_max;
+  /* and the value is decreased by 2^prefix_len-1 */
+  value -= prefix_max;
+  /* MSB of each byte is used as continuation flag */
+  for (; value >= 0x80; value >>= 7)
+    *dst++ = 0x80 | (value & 0x7F);
+  /* except for the last byte */
+  *dst++ = (u8) value;
+
+  return dst;
+}
+
+uword
+hpack_huffman_encoded_len (const u8 *value, uword value_len)
+{
+  uword len = 0;
+  u8 *end;
+  hpack_huffman_symbol_t *sym;
+
+  end = (u8 *) value + value_len;
+  while (value != end)
+    {
+      sym = &huff_sym_table[*value++];
+      len += sym->code_len;
+    }
+  /* round up to byte boundary */
+  return (len + 7) / 8;
+}
+
+u8 *
+hpack_encode_huffman (u8 *dst, const u8 *value, uword value_len)
+{
+  u8 *end;
+  hpack_huffman_symbol_t *sym;
+  u8 accumulator_len = 40; /* leftover (1 byte) + max code_len (4 bytes) */
+  u64 accumulator = 0;    /* to fit leftover and current code */
+
+  end = (u8 *) value + value_len;
+
+  while (value != end)
+    {
+      sym = &huff_sym_table[*value++];
+      /* add current code to leftover of previous one */
+      accumulator |= (u64) sym->code << (accumulator_len - sym->code_len);
+      accumulator_len -= sym->code_len;
+      /* write only fully occupied bytes (max 4) */
+      switch (accumulator_len)
+       {
+       case 1 ... 8:
+#define WRITE_BYTE()                                                          \
+  *dst = (u8) (accumulator >> 32);                                            \
+  accumulator_len += 8;                                                       \
+  accumulator <<= 8;                                                          \
+  dst++;
+         WRITE_BYTE ();
+       case 9 ... 16:
+         WRITE_BYTE ();
+       case 17 ... 24:
+         WRITE_BYTE ();
+       case 25 ... 32:
+         WRITE_BYTE ();
+       default:
+         break;
+       }
+    }
+
+  /* padding (0-7 bits)*/
+  ASSERT (accumulator_len > 32 && accumulator_len <= 40);
+  if (accumulator_len != 40)
+    {
+      accumulator |= (u64) 0x7F << (accumulator_len - 7);
+      *dst = (u8) (accumulator >> 32);
+      dst++;
+    }
+  return dst;
+}
+
+__clib_export u8 *
+hpack_encode_string (u8 *dst, const u8 *value, uword value_len)
+{
+  uword huff_len;
+
+  huff_len = hpack_huffman_encoded_len (value, value_len);
+  /* raw bytes might take fewer bytes */
+  if (huff_len >= value_len)
+    {
+      *dst = 0; /* clear H flag */
+      dst = hpack_encode_int (dst, value_len, 7);
+      clib_memcpy (dst, value, value_len);
+      return dst + value_len;
+    }
+
+  *dst = 0x80; /* set H flag */
+  dst = hpack_encode_int (dst, huff_len, 7);
+  dst = hpack_encode_huffman (dst, value, value_len);
+
+  return dst;
+}
diff --git a/src/plugins/http/http2/hpack.h b/src/plugins/http/http2/hpack.h
new file mode 100644 (file)
index 0000000..6bd6a3c
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_PLUGINS_HTTP_HPACK_H_
+#define SRC_PLUGINS_HTTP_HPACK_H_
+
+#include <vppinfra/types.h>
+
+#define HPACK_INVALID_INT CLIB_UWORD_MAX
+#if uword_bits == 64
+#define HPACK_ENCODED_INT_MAX_LEN 10
+#else
+#define HPACK_ENCODED_INT_MAX_LEN 6
+#endif
+
+/**
+ * Decode unsigned variable-length integer (RFC7541 section 5.1)
+ *
+ * @param src        Pointer to source buffer which will be advanced
+ * @param end        End of the source buffer
+ * @param prefix_len Number of bits of the prefix (between 1 and 8)
+ *
+ * @return Decoded integer or @c HPACK_INVALID_INT in case of error
+ */
+uword hpack_decode_int (u8 **src, u8 *end, u8 prefix_len);
+
+/**
+ * Encode given value as unsigned variable-length integer (RFC7541 section 5.1)
+ *
+ * @param dst        Pointer to destination buffer, should have enough space
+ * @param value      Integer value to encode (up to @c CLIB_WORD_MAX)
+ * @param prefix_len Number of bits of the prefix (between 1 and 8)
+ *
+ * @return Advanced pointer to the destination buffer
+ *
+ * @note Encoded integer will take maximum @c HPACK_ENCODED_INT_MAX_LEN bytes
+ */
+u8 *hpack_encode_int (u8 *dst, uword value, u8 prefix_len);
+
+/**
+ * Decode
+ *
+ * @param src Pointer to source buffer which will be advanced
+ * @param end End of the source buffer
+ * @param buf     Pointer to the buffer where string is decoded which will be
+ *                advanced by number of written bytes
+ * @param buf_len Length the buffer, will be decreased
+ *
+ * @return @c 0 on success.
+ */
+int hpack_decode_huffman (u8 **src, u8 *end, u8 **buf, uword *buf_len);
+
+/**
+ * Encode given string in Huffman codes.
+ *
+ * @param dst       Pointer to destination buffer, should have enough space
+ * @param value     String to encode
+ * @param value_len Length of the string
+ *
+ * @return Advanced pointer to the destination buffer
+ */
+u8 *hpack_encode_huffman (u8 *dst, const u8 *value, uword value_len);
+
+/**
+ * Number of bytes required to encode given string in Huffman codes
+ *
+ * @param value     Pointer to buffer with string to encode
+ * @param value_len Length of the string
+ *
+ * @return number of bytes required to encode string in Huffman codes, round up
+ * to byte boundary
+ */
+uword hpack_huffman_encoded_len (const u8 *value, uword value_len);
+
+#endif /* SRC_PLUGINS_HTTP_HPACK_H_ */
diff --git a/src/plugins/http/http2/huffman_table.h b/src/plugins/http/http2/huffman_table.h
new file mode 100644 (file)
index 0000000..66afffb
--- /dev/null
@@ -0,0 +1,319 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+/* generated by mk_huffman_table.py */
+
+#ifndef SRC_PLUGINS_HTTP_HUFFMAN_TABLE_H_
+#define SRC_PLUGINS_HTTP_HUFFMAN_TABLE_H_
+
+#include <vppinfra/types.h>
+
+typedef struct
+{
+  u8 code_len;
+  u32 code;
+} hpack_huffman_symbol_t;
+
+static hpack_huffman_symbol_t huff_sym_table[] = {
+  { 13, 0x1ff8 },    { 23, 0x7fffd8 },  { 28, 0xfffffe2 },  { 28, 0xfffffe3 },
+  { 28, 0xfffffe4 }, { 28, 0xfffffe5 },         { 28, 0xfffffe6 },  { 28, 0xfffffe7 },
+  { 28, 0xfffffe8 }, { 24, 0xffffea },  { 30, 0x3ffffffc }, { 28, 0xfffffe9 },
+  { 28, 0xfffffea }, { 30, 0x3ffffffd }, { 28, 0xfffffeb },  { 28, 0xfffffec },
+  { 28, 0xfffffed }, { 28, 0xfffffee },         { 28, 0xfffffef },  { 28, 0xffffff0 },
+  { 28, 0xffffff1 }, { 28, 0xffffff2 },         { 30, 0x3ffffffe }, { 28, 0xffffff3 },
+  { 28, 0xffffff4 }, { 28, 0xffffff5 },         { 28, 0xffffff6 },  { 28, 0xffffff7 },
+  { 28, 0xffffff8 }, { 28, 0xffffff9 },         { 28, 0xffffffa },  { 28, 0xffffffb },
+  { 6, 0x14 },      { 10, 0x3f8 },      { 10, 0x3f9 },      { 12, 0xffa },
+  { 13, 0x1ff9 },    { 6, 0x15 },       { 8, 0xf8 },        { 11, 0x7fa },
+  { 10, 0x3fa },     { 10, 0x3fb },     { 8, 0xf9 },        { 11, 0x7fb },
+  { 8, 0xfa },      { 6, 0x16 },        { 6, 0x17 },        { 6, 0x18 },
+  { 5, 0x0 },       { 5, 0x1 },         { 5, 0x2 },         { 6, 0x19 },
+  { 6, 0x1a },      { 6, 0x1b },        { 6, 0x1c },        { 6, 0x1d },
+  { 6, 0x1e },      { 6, 0x1f },        { 7, 0x5c },        { 8, 0xfb },
+  { 15, 0x7ffc },    { 6, 0x20 },       { 12, 0xffb },      { 10, 0x3fc },
+  { 13, 0x1ffa },    { 6, 0x21 },       { 7, 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 },
+  { 8, 0xfc },      { 7, 0x73 },        { 8, 0xfd },        { 13, 0x1ffb },
+  { 19, 0x7fff0 },   { 13, 0x1ffc },    { 14, 0x3ffc },     { 6, 0x22 },
+  { 15, 0x7ffd },    { 5, 0x3 },        { 6, 0x23 },        { 5, 0x4 },
+  { 6, 0x24 },      { 5, 0x5 },         { 6, 0x25 },        { 6, 0x26 },
+  { 6, 0x27 },      { 5, 0x6 },         { 7, 0x74 },        { 7, 0x75 },
+  { 6, 0x28 },      { 6, 0x29 },        { 6, 0x2a },        { 5, 0x7 },
+  { 6, 0x2b },      { 7, 0x76 },        { 6, 0x2c },        { 5, 0x8 },
+  { 5, 0x9 },       { 6, 0x2d },        { 7, 0x77 },        { 7, 0x78 },
+  { 7, 0x79 },      { 7, 0x7a },        { 7, 0x7b },        { 15, 0x7ffe },
+  { 11, 0x7fc },     { 14, 0x3ffd },    { 13, 0x1ffd },     { 28, 0xffffffc },
+  { 20, 0xfffe6 },   { 22, 0x3fffd2 },  { 20, 0xfffe7 },    { 20, 0xfffe8 },
+  { 22, 0x3fffd3 },  { 22, 0x3fffd4 },  { 22, 0x3fffd5 },   { 23, 0x7fffd9 },
+  { 22, 0x3fffd6 },  { 23, 0x7fffda },  { 23, 0x7fffdb },   { 23, 0x7fffdc },
+  { 23, 0x7fffdd },  { 23, 0x7fffde },  { 24, 0xffffeb },   { 23, 0x7fffdf },
+  { 24, 0xffffec },  { 24, 0xffffed },  { 22, 0x3fffd7 },   { 23, 0x7fffe0 },
+  { 24, 0xffffee },  { 23, 0x7fffe1 },  { 23, 0x7fffe2 },   { 23, 0x7fffe3 },
+  { 23, 0x7fffe4 },  { 21, 0x1fffdc },  { 22, 0x3fffd8 },   { 23, 0x7fffe5 },
+  { 22, 0x3fffd9 },  { 23, 0x7fffe6 },  { 23, 0x7fffe7 },   { 24, 0xffffef },
+  { 22, 0x3fffda },  { 21, 0x1fffdd },  { 20, 0xfffe9 },    { 22, 0x3fffdb },
+  { 22, 0x3fffdc },  { 23, 0x7fffe8 },  { 23, 0x7fffe9 },   { 21, 0x1fffde },
+  { 23, 0x7fffea },  { 22, 0x3fffdd },  { 22, 0x3fffde },   { 24, 0xfffff0 },
+  { 21, 0x1fffdf },  { 22, 0x3fffdf },  { 23, 0x7fffeb },   { 23, 0x7fffec },
+  { 21, 0x1fffe0 },  { 21, 0x1fffe1 },  { 22, 0x3fffe0 },   { 21, 0x1fffe2 },
+  { 23, 0x7fffed },  { 22, 0x3fffe1 },  { 23, 0x7fffee },   { 23, 0x7fffef },
+  { 20, 0xfffea },   { 22, 0x3fffe2 },  { 22, 0x3fffe3 },   { 22, 0x3fffe4 },
+  { 23, 0x7ffff0 },  { 22, 0x3fffe5 },  { 22, 0x3fffe6 },   { 23, 0x7ffff1 },
+  { 26, 0x3ffffe0 }, { 26, 0x3ffffe1 },         { 20, 0xfffeb },    { 19, 0x7fff1 },
+  { 22, 0x3fffe7 },  { 23, 0x7ffff2 },  { 22, 0x3fffe8 },   { 25, 0x1ffffec },
+  { 26, 0x3ffffe2 }, { 26, 0x3ffffe3 },         { 26, 0x3ffffe4 },  { 27, 0x7ffffde },
+  { 27, 0x7ffffdf }, { 26, 0x3ffffe5 },         { 24, 0xfffff1 },   { 25, 0x1ffffed },
+  { 19, 0x7fff2 },   { 21, 0x1fffe3 },  { 26, 0x3ffffe6 },  { 27, 0x7ffffe0 },
+  { 27, 0x7ffffe1 }, { 26, 0x3ffffe7 },         { 27, 0x7ffffe2 },  { 24, 0xfffff2 },
+  { 21, 0x1fffe4 },  { 21, 0x1fffe5 },  { 26, 0x3ffffe8 },  { 26, 0x3ffffe9 },
+  { 28, 0xffffffd }, { 27, 0x7ffffe3 },         { 27, 0x7ffffe4 },  { 27, 0x7ffffe5 },
+  { 20, 0xfffec },   { 24, 0xfffff3 },  { 20, 0xfffed },    { 21, 0x1fffe6 },
+  { 22, 0x3fffe9 },  { 21, 0x1fffe7 },  { 21, 0x1fffe8 },   { 23, 0x7ffff3 },
+  { 22, 0x3fffea },  { 22, 0x3fffeb },  { 25, 0x1ffffee },  { 25, 0x1ffffef },
+  { 24, 0xfffff4 },  { 24, 0xfffff5 },  { 26, 0x3ffffea },  { 23, 0x7ffff4 },
+  { 26, 0x3ffffeb }, { 27, 0x7ffffe6 },         { 26, 0x3ffffec },  { 26, 0x3ffffed },
+  { 27, 0x7ffffe7 }, { 27, 0x7ffffe8 },         { 27, 0x7ffffe9 },  { 27, 0x7ffffea },
+  { 27, 0x7ffffeb }, { 28, 0xffffffe },         { 27, 0x7ffffec },  { 27, 0x7ffffed },
+  { 27, 0x7ffffee }, { 27, 0x7ffffef },         { 27, 0x7fffff0 },  { 26, 0x3ffffee },
+};
+
+typedef struct
+{
+  u8 symbol;
+  u8 code_len;
+} hpack_huffman_code_t;
+
+static hpack_huffman_code_t huff_code_table_fast[] = {
+  { 0x30, 5 }, { 0x30, 5 }, { 0x30, 5 }, { 0x30, 5 }, { 0x30, 5 }, { 0x30, 5 },
+  { 0x30, 5 }, { 0x30, 5 }, { 0x31, 5 }, { 0x31, 5 }, { 0x31, 5 }, { 0x31, 5 },
+  { 0x31, 5 }, { 0x31, 5 }, { 0x31, 5 }, { 0x31, 5 }, { 0x32, 5 }, { 0x32, 5 },
+  { 0x32, 5 }, { 0x32, 5 }, { 0x32, 5 }, { 0x32, 5 }, { 0x32, 5 }, { 0x32, 5 },
+  { 0x61, 5 }, { 0x61, 5 }, { 0x61, 5 }, { 0x61, 5 }, { 0x61, 5 }, { 0x61, 5 },
+  { 0x61, 5 }, { 0x61, 5 }, { 0x63, 5 }, { 0x63, 5 }, { 0x63, 5 }, { 0x63, 5 },
+  { 0x63, 5 }, { 0x63, 5 }, { 0x63, 5 }, { 0x63, 5 }, { 0x65, 5 }, { 0x65, 5 },
+  { 0x65, 5 }, { 0x65, 5 }, { 0x65, 5 }, { 0x65, 5 }, { 0x65, 5 }, { 0x65, 5 },
+  { 0x69, 5 }, { 0x69, 5 }, { 0x69, 5 }, { 0x69, 5 }, { 0x69, 5 }, { 0x69, 5 },
+  { 0x69, 5 }, { 0x69, 5 }, { 0x6F, 5 }, { 0x6F, 5 }, { 0x6F, 5 }, { 0x6F, 5 },
+  { 0x6F, 5 }, { 0x6F, 5 }, { 0x6F, 5 }, { 0x6F, 5 }, { 0x73, 5 }, { 0x73, 5 },
+  { 0x73, 5 }, { 0x73, 5 }, { 0x73, 5 }, { 0x73, 5 }, { 0x73, 5 }, { 0x73, 5 },
+  { 0x74, 5 }, { 0x74, 5 }, { 0x74, 5 }, { 0x74, 5 }, { 0x74, 5 }, { 0x74, 5 },
+  { 0x74, 5 }, { 0x74, 5 }, { 0x20, 6 }, { 0x20, 6 }, { 0x20, 6 }, { 0x20, 6 },
+  { 0x25, 6 }, { 0x25, 6 }, { 0x25, 6 }, { 0x25, 6 }, { 0x2D, 6 }, { 0x2D, 6 },
+  { 0x2D, 6 }, { 0x2D, 6 }, { 0x2E, 6 }, { 0x2E, 6 }, { 0x2E, 6 }, { 0x2E, 6 },
+  { 0x2F, 6 }, { 0x2F, 6 }, { 0x2F, 6 }, { 0x2F, 6 }, { 0x33, 6 }, { 0x33, 6 },
+  { 0x33, 6 }, { 0x33, 6 }, { 0x34, 6 }, { 0x34, 6 }, { 0x34, 6 }, { 0x34, 6 },
+  { 0x35, 6 }, { 0x35, 6 }, { 0x35, 6 }, { 0x35, 6 }, { 0x36, 6 }, { 0x36, 6 },
+  { 0x36, 6 }, { 0x36, 6 }, { 0x37, 6 }, { 0x37, 6 }, { 0x37, 6 }, { 0x37, 6 },
+  { 0x38, 6 }, { 0x38, 6 }, { 0x38, 6 }, { 0x38, 6 }, { 0x39, 6 }, { 0x39, 6 },
+  { 0x39, 6 }, { 0x39, 6 }, { 0x3D, 6 }, { 0x3D, 6 }, { 0x3D, 6 }, { 0x3D, 6 },
+  { 0x41, 6 }, { 0x41, 6 }, { 0x41, 6 }, { 0x41, 6 }, { 0x5F, 6 }, { 0x5F, 6 },
+  { 0x5F, 6 }, { 0x5F, 6 }, { 0x62, 6 }, { 0x62, 6 }, { 0x62, 6 }, { 0x62, 6 },
+  { 0x64, 6 }, { 0x64, 6 }, { 0x64, 6 }, { 0x64, 6 }, { 0x66, 6 }, { 0x66, 6 },
+  { 0x66, 6 }, { 0x66, 6 }, { 0x67, 6 }, { 0x67, 6 }, { 0x67, 6 }, { 0x67, 6 },
+  { 0x68, 6 }, { 0x68, 6 }, { 0x68, 6 }, { 0x68, 6 }, { 0x6C, 6 }, { 0x6C, 6 },
+  { 0x6C, 6 }, { 0x6C, 6 }, { 0x6D, 6 }, { 0x6D, 6 }, { 0x6D, 6 }, { 0x6D, 6 },
+  { 0x6E, 6 }, { 0x6E, 6 }, { 0x6E, 6 }, { 0x6E, 6 }, { 0x70, 6 }, { 0x70, 6 },
+  { 0x70, 6 }, { 0x70, 6 }, { 0x72, 6 }, { 0x72, 6 }, { 0x72, 6 }, { 0x72, 6 },
+  { 0x75, 6 }, { 0x75, 6 }, { 0x75, 6 }, { 0x75, 6 }, { 0x3A, 7 }, { 0x3A, 7 },
+  { 0x42, 7 }, { 0x42, 7 }, { 0x43, 7 }, { 0x43, 7 }, { 0x44, 7 }, { 0x44, 7 },
+  { 0x45, 7 }, { 0x45, 7 }, { 0x46, 7 }, { 0x46, 7 }, { 0x47, 7 }, { 0x47, 7 },
+  { 0x48, 7 }, { 0x48, 7 }, { 0x49, 7 }, { 0x49, 7 }, { 0x4A, 7 }, { 0x4A, 7 },
+  { 0x4B, 7 }, { 0x4B, 7 }, { 0x4C, 7 }, { 0x4C, 7 }, { 0x4D, 7 }, { 0x4D, 7 },
+  { 0x4E, 7 }, { 0x4E, 7 }, { 0x4F, 7 }, { 0x4F, 7 }, { 0x50, 7 }, { 0x50, 7 },
+  { 0x51, 7 }, { 0x51, 7 }, { 0x52, 7 }, { 0x52, 7 }, { 0x53, 7 }, { 0x53, 7 },
+  { 0x54, 7 }, { 0x54, 7 }, { 0x55, 7 }, { 0x55, 7 }, { 0x56, 7 }, { 0x56, 7 },
+  { 0x57, 7 }, { 0x57, 7 }, { 0x59, 7 }, { 0x59, 7 }, { 0x6A, 7 }, { 0x6A, 7 },
+  { 0x6B, 7 }, { 0x6B, 7 }, { 0x71, 7 }, { 0x71, 7 }, { 0x76, 7 }, { 0x76, 7 },
+  { 0x77, 7 }, { 0x77, 7 }, { 0x78, 7 }, { 0x78, 7 }, { 0x79, 7 }, { 0x79, 7 },
+  { 0x7A, 7 }, { 0x7A, 7 }, { 0x26, 8 }, { 0x2A, 8 }, { 0x2C, 8 }, { 0x3B, 8 },
+  { 0x58, 8 }, { 0x5A, 8 }, { 0x00, 0 }, { 0x00, 0 },
+};
+
+typedef struct
+{
+  u32 first_code;
+  u8 code_len;
+  u8 symbols[29];
+} hpack_huffman_group_t;
+
+/* clang-format off */
+
+static hpack_huffman_group_t huff_code_table_slow[] = {
+  {
+    0x3f8, /* first_code */
+    10, /* code_len */
+    {
+      0x21, 0x22, 0x28, 0x29, 0x3F,
+    } /* symbols */
+  },
+  {
+    0x7fa, /* first_code */
+    11, /* code_len */
+    {
+      0x27, 0x2B, 0x7C,
+    } /* symbols */
+  },
+  {
+    0xffa, /* first_code */
+    12, /* code_len */
+    {
+      0x23, 0x3E,
+    } /* symbols */
+  },
+  {
+    0x1ff8, /* first_code */
+    13, /* code_len */
+    {
+      0x00, 0x24, 0x40, 0x5B, 0x5D, 0x7E,
+    } /* symbols */
+  },
+  {
+    0x3ffc, /* first_code */
+    14, /* code_len */
+    {
+      0x5E, 0x7D,
+    } /* symbols */
+  },
+  {
+    0x7ffc, /* first_code */
+    15, /* code_len */
+    {
+      0x3C, 0x60, 0x7B,
+    } /* symbols */
+  },
+  {
+    0x7fff0, /* first_code */
+    19, /* code_len */
+    {
+      0x5C, 0xC3, 0xD0,
+    } /* symbols */
+  },
+  {
+    0xfffe6, /* first_code */
+    20, /* code_len */
+    {
+      0x80, 0x82, 0x83, 0xA2, 0xB8, 0xC2, 0xE0, 0xE2,
+    } /* symbols */
+  },
+  {
+    0x1fffdc, /* first_code */
+    21, /* code_len */
+    {
+      0x99, 0xA1, 0xA7, 0xAC, 0xB0, 0xB1, 0xB3, 0xD1, 0xD8, 0xD9,
+      0xE3, 0xE5, 0xE6,
+    } /* symbols */
+  },
+  {
+    0x3fffd2, /* first_code */
+    22, /* code_len */
+    {
+      0x81, 0x84, 0x85, 0x86, 0x88, 0x92, 0x9A, 0x9C, 0xA0, 0xA3,
+      0xA4, 0xA9, 0xAA, 0xAD, 0xB2, 0xB5, 0xB9, 0xBA, 0xBB, 0xBD,
+      0xBE, 0xC4, 0xC6, 0xE4, 0xE8, 0xE9,
+    } /* symbols */
+  },
+  {
+    0x7fffd8, /* first_code */
+    23, /* code_len */
+    {
+      0x01, 0x87, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8F, 0x93, 0x95,
+      0x96, 0x97, 0x98, 0x9B, 0x9D, 0x9E, 0xA5, 0xA6, 0xA8, 0xAE,
+      0xAF, 0xB4, 0xB6, 0xB7, 0xBC, 0xBF, 0xC5, 0xE7, 0xEF,
+    } /* symbols */
+  },
+  {
+    0xffffea, /* first_code */
+    24, /* code_len */
+    {
+      0x09, 0x8E, 0x90, 0x91, 0x94, 0x9F, 0xAB, 0xCE, 0xD7, 0xE1,
+      0xEC, 0xED,
+    } /* symbols */
+  },
+  {
+    0x1ffffec, /* first_code */
+    25, /* code_len */
+    {
+      0xC7, 0xCF, 0xEA, 0xEB,
+    } /* symbols */
+  },
+  {
+    0x3ffffe0, /* first_code */
+    26, /* code_len */
+    {
+      0xC0, 0xC1, 0xC8, 0xC9, 0xCA, 0xCD, 0xD2, 0xD5, 0xDA, 0xDB,
+      0xEE, 0xF0, 0xF2, 0xF3, 0xFF,
+    } /* symbols */
+  },
+  {
+    0x7ffffde, /* first_code */
+    27, /* code_len */
+    {
+      0xCB, 0xCC, 0xD3, 0xD4, 0xD6, 0xDD, 0xDE, 0xDF, 0xF1, 0xF4,
+      0xF5, 0xF6, 0xF7, 0xF8, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,
+    } /* symbols */
+  },
+  {
+    0xfffffe2, /* first_code */
+    28, /* code_len */
+    {
+      0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E,
+      0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x17, 0x18, 0x19,
+      0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x7F, 0xDC, 0xF9,
+    } /* symbols */
+  },
+  {
+    0x3ffffffc, /* first_code */
+    30, /* code_len */
+    {
+      0x0A, 0x0D, 0x16,
+    } /* symbols */
+  },
+};
+
+/* clang format-on */
+
+always_inline hpack_huffman_group_t *
+hpack_huffman_get_group (u32 value)
+{
+  if (value < 0xFF400000)
+    return &huff_code_table_slow[0];
+  else if (value < 0xFFA00000)
+    return &huff_code_table_slow[1];
+  else if (value < 0xFFC00000)
+    return &huff_code_table_slow[2];
+  else if (value < 0xFFF00000)
+    return &huff_code_table_slow[3];
+  else if (value < 0xFFF80000)
+    return &huff_code_table_slow[4];
+  else if (value < 0xFFFE0000)
+    return &huff_code_table_slow[5];
+  else if (value < 0xFFFE6000)
+    return &huff_code_table_slow[6];
+  else if (value < 0xFFFEE000)
+    return &huff_code_table_slow[7];
+  else if (value < 0xFFFF4800)
+    return &huff_code_table_slow[8];
+  else if (value < 0xFFFFB000)
+    return &huff_code_table_slow[9];
+  else if (value < 0xFFFFEA00)
+    return &huff_code_table_slow[10];
+  else if (value < 0xFFFFF600)
+    return &huff_code_table_slow[11];
+  else if (value < 0xFFFFF800)
+    return &huff_code_table_slow[12];
+  else if (value < 0xFFFFFBC0)
+    return &huff_code_table_slow[13];
+  else if (value < 0xFFFFFE20)
+    return &huff_code_table_slow[14];
+  else if (value < 0xFFFFFFF0)
+    return &huff_code_table_slow[15];
+  else
+    return &huff_code_table_slow[16];
+}
+
+#endif /* SRC_PLUGINS_HTTP_HUFFMAN_TABLE_H_ */
index bfaa285..04db580 100644 (file)
@@ -6,6 +6,7 @@
 #include <vpp/app/version.h>
 #include <http/http.h>
 #include <http/http_header_names.h>
+#include <http/http2/hpack.h>
 
 #define HTTP_TEST_I(_cond, _comment, _args...)                                \
   ({                                                                          \
@@ -533,6 +534,203 @@ http_test_http_header_table (vlib_main_t *vm)
   return 0;
 }
 
+static int
+http_test_hpack (vlib_main_t *vm)
+{
+  vlib_cli_output (vm, "hpack_decode_int");
+
+  static uword (*_hpack_decode_int) (u8 * *pos, u8 * end, u8 prefix_len);
+  _hpack_decode_int =
+    vlib_get_plugin_symbol ("http_plugin.so", "hpack_decode_int");
+
+  u8 *pos, *end, *input = 0;
+  uword value;
+#define TEST(i, pl, e)                                                        \
+  vec_validate (input, sizeof (i) - 2);                                       \
+  memcpy (input, i, sizeof (i) - 1);                                          \
+  pos = input;                                                                \
+  end = vec_end (input);                                                      \
+  value = _hpack_decode_int (&pos, end, (u8) pl);                             \
+  HTTP_TEST ((value == (uword) e && pos == end),                              \
+            "%U with prefix length %u is %llu", format_hex_bytes, input,     \
+            vec_len (input), (u8) pl, value);                                \
+  vec_free (input);
+
+  TEST ("\x00", 8, 0);
+  TEST ("\x2A", 8, 42);
+  TEST ("\x72", 4, 2);
+  TEST ("\x7F\x00", 7, 127);
+  TEST ("\x7F\x01", 7, 128);
+  TEST ("\x9F\x9A\x0A", 5, 1337);
+  TEST ("\xFF\x80\x01", 7, 255);
+  /* max value to decode is CLIB_WORD_MAX, CLIB_UWORD_MAX is error */
+  TEST ("\x7F\x80\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F", 7, CLIB_WORD_MAX);
+
+#undef TEST
+
+#define N_TEST(i, pl)                                                         \
+  vec_validate (input, sizeof (i) - 2);                                       \
+  memcpy (input, i, sizeof (i) - 1);                                          \
+  pos = input;                                                                \
+  end = vec_end (input);                                                      \
+  value = _hpack_decode_int (&pos, end, (u8) pl);                             \
+  HTTP_TEST ((value == HPACK_INVALID_INT),                                    \
+            "%U with prefix length %u should be invalid", format_hex_bytes,  \
+            input, vec_len (input), (u8) pl);                                \
+  vec_free (input);
+
+  /* incomplete */
+  N_TEST ("\x7F", 7);
+  N_TEST ("\x0F\xFF\xFF", 4);
+  /* overflow */
+  N_TEST ("\x0F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 4);
+  N_TEST ("\x0F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 4);
+
+#undef N_TEST
+
+  vlib_cli_output (vm, "hpack_encode_int");
+
+  static u8 *(*_hpack_encode_int) (u8 * dst, uword value, u8 prefix_len);
+  _hpack_encode_int =
+    vlib_get_plugin_symbol ("http_plugin.so", "hpack_encode_int");
+
+  u8 *buf = 0;
+  u8 *p;
+
+#define TEST(v, pl, e)                                                        \
+  vec_validate_init_empty (buf, 15, 0);                                       \
+  p = _hpack_encode_int (buf, v, (u8) pl);                                    \
+  HTTP_TEST (((p - buf) == (sizeof (e) - 1) && !memcmp (buf, e, p - buf)),    \
+            "%llu with prefix length %u is encoded as %U", v, (u8) pl,       \
+            format_hex_bytes, buf, p - buf);                                 \
+  vec_free (buf);
+
+  TEST (0, 8, "\x00");
+  TEST (2, 4, "\x02");
+  TEST (42, 8, "\x2A");
+  TEST (127, 7, "\x7F\x00");
+  TEST (128, 7, "\x7F\x01");
+  TEST (255, 7, "\x7F\x80\x01");
+  TEST (1337, 5, "\x1F\x9A\x0A");
+  TEST (CLIB_WORD_MAX, 7, "\x7F\x80\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F");
+#undef TEST
+
+  vlib_cli_output (vm, "hpack_decode_string");
+
+  static int (*_hpack_decode_string) (u8 * *src, u8 * end, u8 * *buf,
+                                     uword * buf_len);
+  _hpack_decode_string =
+    vlib_get_plugin_symbol ("http_plugin.so", "hpack_decode_string");
+
+  u8 *bp;
+  uword blen, len;
+  int rv;
+
+#define TEST(i, e)                                                            \
+  vec_validate (input, sizeof (i) - 2);                                       \
+  memcpy (input, i, sizeof (i) - 1);                                          \
+  pos = input;                                                                \
+  vec_validate_init_empty (buf, 63, 0);                                       \
+  bp = buf;                                                                   \
+  blen = vec_len (buf);                                                       \
+  rv = _hpack_decode_string (&pos, vec_end (input), &bp, &blen);              \
+  len = vec_len (buf) - blen;                                                 \
+  HTTP_TEST ((len == strlen (e) && !memcmp (buf, e, len) &&                   \
+             pos == vec_end (input) && bp == buf + len && rv == 0),          \
+            "%U is decoded as %U", format_hex_bytes, input, vec_len (input), \
+            format_http_bytes, buf, len);                                    \
+  vec_free (input);                                                           \
+  vec_free (buf);
+
+  /* raw coding */
+  TEST ("\x07private", "private");
+  /* Huffman coding */
+  TEST ("\x85\xAE\xC3\x77\x1A\x4B", "private");
+  TEST ("\x86\xA8\xEB\x10\x64\x9C\xBF", "no-cache");
+  TEST ("\x8C\xF1\xE3\xC2\xE5\xF2\x3A\x6B\xA0\xAB\x90\xF4\xFF",
+       "www.example.com");
+  TEST ("\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95\x04\x0B\x81\x66"
+       "\xE0\x82\xA6\x2D\x1B\xFF",
+       "Mon, 21 Oct 2013 20:13:21 GMT")
+  TEST ("\xAD\x94\xE7\x82\x1D\xD7\xF2\xE6\xC7\xB3\x35\xDF\xDF\xCD\x5B\x39\x60"
+       "\xD5\xAF\x27\x08\x7F\x36\x72\xC1\xAB\x27\x0F\xB5\x29\x1F\x95\x87\x31"
+       "\x60\x65\xC0\x03\xED\x4E\xE5\xB1\x06\x3D\x50\x07",
+       "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1");
+  TEST ("\x8A\x9C\xB4\x50\x75\x3C\x1E\xCA\x24\xFE\x3F", "hello world!")
+  TEST ("\x8A\xFF\xFE\x03\x18\xC6\x31\x8C\x63\x18\xC7", "\\aaaaaaaaaaaa");
+  TEST ("\x8C\x1F\xFF\xF0\x18\xC6\x31\x80\x03\x18\xC6\x31\x8F",
+       "a\\aaaaa00aaaaaaa");
+  TEST ("\x87\x1F\xFF\xF0\xFF\xFE\x11\xFF", "a\\\\b");
+  TEST ("\x84\x1F\xF9\xFE\xA3", "a?'b");
+  TEST ("\x84\x1F\xFA\xFF\x23", "a'?b");
+  TEST ("\x8D\x1F\xFF\xFF\xFF\x0C\x63\x18\xC0\x01\x8C\x63\x18\xC7",
+       "\x61\xF9\x61\x61\x61\x61\x61\x30\x30\x61\x61\x61\x61\x61\x61\x61")
+#undef TEST
+
+#define N_TEST(i)                                                             \
+  vec_validate (input, sizeof (i) - 2);                                       \
+  memcpy (input, i, sizeof (i) - 1);                                          \
+  pos = input;                                                                \
+  vec_validate_init_empty (buf, 15, 0);                                       \
+  bp = buf;                                                                   \
+  blen = vec_len (buf);                                                       \
+  rv = _hpack_decode_string (&pos, vec_end (input), &bp, &blen);              \
+  HTTP_TEST ((rv != 0), "%U should be invalid", format_hex_bytes, input,      \
+            vec_len (input));                                                \
+  vec_free (input);                                                           \
+  vec_free (buf);
+
+  /* incomplete */
+  N_TEST ("\x07priv");
+  /* invalid length */
+  N_TEST ("\x7Fprivate");
+  /* invalid EOF */
+  N_TEST ("\x81\x8C");
+  /* not enough space for decoding */
+  N_TEST (
+    "\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95\x04\x0B\x81\x66"
+    "\xE0\x82\xA6\x2D\x1B\xFF");
+#undef N_TEST
+
+  vlib_cli_output (vm, "hpack_encode_string");
+
+  static u8 *(*_hpack_encode_string) (u8 * dst, const u8 *value,
+                                     uword value_len);
+  _hpack_encode_string =
+    vlib_get_plugin_symbol ("http_plugin.so", "hpack_encode_string");
+
+#define TEST(i, e)                                                            \
+  vec_validate (input, sizeof (i) - 2);                                       \
+  memcpy (input, i, sizeof (i) - 1);                                          \
+  pos = input;                                                                \
+  vec_validate_init_empty (buf, 63, 0);                                       \
+  p = _hpack_encode_string (buf, input, vec_len (input));                     \
+  HTTP_TEST (((p - buf) == (sizeof (e) - 1) && !memcmp (buf, e, p - buf)),    \
+            "%v is encoded as %U", input, format_hex_bytes, buf, p - buf);   \
+  vec_free (input);                                                           \
+  vec_free (buf);
+
+  /* Huffman coding */
+  TEST ("private", "\x85\xAE\xC3\x77\x1A\x4B");
+  TEST ("no-cache", "\x86\xA8\xEB\x10\x64\x9C\xBF");
+  TEST ("www.example.com",
+       "\x8C\xF1\xE3\xC2\xE5\xF2\x3A\x6B\xA0\xAB\x90\xF4\xFF");
+  TEST ("Mon, 21 Oct 2013 20:13:21 GMT",
+       "\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95\x04\x0B\x81\x66"
+       "\xE0\x82\xA6\x2D\x1B\xFF")
+  TEST ("foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+       "\xAD\x94\xE7\x82\x1D\xD7\xF2\xE6\xC7\xB3\x35\xDF\xDF\xCD\x5B\x39\x60"
+       "\xD5\xAF\x27\x08\x7F\x36\x72\xC1\xAB\x27\x0F\xB5\x29\x1F\x95\x87\x31"
+       "\x60\x65\xC0\x03\xED\x4E\xE5\xB1\x06\x3D\x50\x07");
+  TEST ("hello world!", "\x8A\x9C\xB4\x50\x75\x3C\x1E\xCA\x24\xFE\x3F")
+  TEST ("\\aaaaaaaaaaaa", "\x8A\xFF\xFE\x03\x18\xC6\x31\x8C\x63\x18\xC7");
+  /* raw coding */
+  TEST ("[XZ]", "\x4[XZ]");
+#undef TEST
+
+  return 0;
+}
+
 static clib_error_t *
 test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
                      vlib_cli_command_t *cmd)
@@ -550,6 +748,8 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
        res = http_test_http_token_is_case (vm);
       else if (unformat (input, "header-table"))
        res = http_test_http_header_table (vm);
+      else if (unformat (input, "hpack"))
+       res = http_test_hpack (vm);
       else if (unformat (input, "all"))
        {
          if ((res = http_test_parse_authority (vm)))
@@ -562,6 +762,8 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
            goto done;
          if ((res = http_test_http_header_table (vm)))
            goto done;
+         if ((res = http_test_hpack (vm)))
+           goto done;
        }
       else
        break;