http: QPACK header encoding w/o dynamic table 80/43780/6
authorMatus Fabian <[email protected]>
Fri, 26 Sep 2025 15:59:29 +0000 (11:59 -0400)
committerFlorin Coras <[email protected]>
Tue, 30 Sep 2025 19:23:27 +0000 (19:23 +0000)
Type: feature

Change-Id: I10eeb9b7b5a5e8fc8733ea33463cffaece7bd162
Signed-off-by: Matus Fabian <[email protected]>
src/plugins/http/http.h
src/plugins/http/http2/hpack.c
src/plugins/http/http2/hpack_inlines.h
src/plugins/http/http3/qpack.c
src/plugins/http/test/http_test.c

index c106038..9eb3199 100644 (file)
@@ -290,6 +290,8 @@ typedef enum http_status_code_
   _ (CONTENT_LENGTH, "Content-Length", "content-length", 28)                  \
   _ (CONTENT_LOCATION, "Content-Location", "content-location", 29)            \
   _ (CONTENT_RANGE, "Content-Range", "content-range", 30)                     \
+  _ (CONTENT_SECURITY_POLICY, "Content-Security-Policy",                      \
+     "content-security-policy", 0)                                            \
   _ (CONTENT_TYPE, "Content-Type", "content-type", 31)                        \
   _ (COOKIE, "Cookie", "cookie", 32)                                          \
   _ (DATE, "Date", "date", 33)                                                \
@@ -299,6 +301,7 @@ typedef enum http_status_code_
   _ (EARLY_DATA, "Early-Data", "early-data", 0)                               \
   _ (ETAG, "ETag", "etag", 34)                                                \
   _ (EXPECT, "Expect", "expect", 35)                                          \
+  _ (EXPECT_CT, "Expect-CT", "expect-ct", 0)                                  \
   _ (EXPIRES, "Expires", "expires", 36)                                       \
   _ (FORWARDED, "Forwarded", "forwarded", 0)                                  \
   _ (FROM, "From", "from", 37)                                                \
@@ -320,6 +323,7 @@ typedef enum http_status_code_
      "proxy-authentication-info", 0)                                          \
   _ (PROXY_AUTHORIZATION, "Proxy-Authorization", "proxy-authorization", 49)   \
   _ (PROXY_STATUS, "Proxy-Status", "proxy-status", 0)                         \
+  _ (PURPOSE, "Purpose", "purpose", 0)                                        \
   _ (RANGE, "Range", "range", 50)                                             \
   _ (REFERER, "Referer", "referer", 51)                                       \
   _ (REFRESH, "Refresh", "refresh", 52)                                       \
@@ -332,15 +336,23 @@ typedef enum http_status_code_
   _ (STRICT_TRANSPORT_SECURITY, "Strict-Transport-Security",                  \
      "strict-transport-security", 56)                                         \
   _ (TE, "TE", "te", 0)                                                       \
+  _ (TIMING_ALLOW_ORIGIN, "Timing-Allow-Origin", "timing-allow-origin", 0)    \
   _ (TRAILER, "Trailer", "trailer", 0)                                        \
   _ (TRANSFER_ENCODING, "Transfer-Encoding", "transfer-encoding", 57)         \
   _ (UPGRADE, "Upgrade", "upgrade", 0)                                        \
+  _ (UPGRADE_INSECURE_REQUESTS, "Upgrade-Insecure-Requests",                  \
+     "upgrade-insecure-requests", 0)                                          \
   _ (USER_AGENT, "User-Agent", "user-agent", 58)                              \
   _ (VARY, "Vary", "vary", 59)                                                \
   _ (VIA, "Via", "via", 60)                                                   \
   _ (WANT_CONTENT_DIGEST, "Want-Content-Digest", "want-content-digest", 0)    \
   _ (WANT_REPR_DIGEST, "Want-Repr-Digest", "want-repr-digest", 0)             \
-  _ (WWW_AUTHENTICATE, "WWW-Authenticate", "www-authenticate", 61)
+  _ (WWW_AUTHENTICATE, "WWW-Authenticate", "www-authenticate", 61)            \
+  _ (X_CONTENT_TYPE_OPTIONS, "X-Content-Type-Options",                        \
+     "x-content-type-options", 0)                                             \
+  _ (X_FORWARDED_FOR, "X-Forwarded-For", "x-forwarded-for", 0)                \
+  _ (X_FRAME_OPTIONS, "X-Frame-Options", "x-frame-options", 0)                \
+  _ (X_XSS_PROTECTION, "X-XSS-Protection", "x-xss-protection", 0)
 
 typedef enum http_header_name_
 {
index b1461df..5fd049e 100644 (file)
@@ -86,20 +86,6 @@ static hpack_static_table_entry_t
     { name_val_token_lit ("www-authenticate", "") },
   };
 
-typedef struct
-{
-  char *base;
-  uword len;
-  u8 static_table_index;
-} hpack_token_t;
-
-static hpack_token_t hpack_headers[] = {
-#define _(sym, str_canonical, str_lower, hpack_index)                         \
-  { http_token_lit (str_lower), hpack_index },
-  foreach_http_header_name
-#undef _
-};
-
 static http_token_t http_methods[] = {
 #define _(s, str) { http_token_lit (str) },
   foreach_http_method
@@ -489,12 +475,11 @@ static inline u8 *
 hpack_encode_header (u8 *dst, http_header_name_t name, const u8 *value,
                     u32 value_len)
 {
-  hpack_token_t *name_token;
   u8 *a, *b;
   u32 orig_len, actual_size;
 
   orig_len = vec_len (dst);
-  name_token = &hpack_headers[name];
+  const hpack_token_t *name_token = &hpack_headers[name];
   if (name_token->static_table_index)
     {
       /* static table index with 4 bit prefix is max 2 bytes */
index 50a0825..804f9bd 100644 (file)
@@ -23,6 +23,20 @@ typedef hpack_error_t (hpack_header_decoder_fn) (u8 **src, u8 *end, u8 **buf,
                                                 u32 *value_len,
                                                 void *decoder_ctx);
 
+typedef struct
+{
+  char *base;
+  uword len;
+  u8 static_table_index;
+} hpack_token_t;
+
+static const hpack_token_t hpack_headers[] = {
+#define _(sym, str_canonical, str_lower, hpack_index)                         \
+  { http_token_lit (str_lower), hpack_index },
+  foreach_http_header_name
+#undef _
+};
+
 /**
  * Decode unsigned variable-length integer (RFC7541 section 5.1)
  *
index e0c9377..d2fa4ae 100644 (file)
@@ -130,6 +130,717 @@ static qpack_static_table_entry_t qpack_static_table[] = {
 STATIC_ASSERT (QPACK_STATIC_TABLE_SIZE == 99,
               "static table must have 99 entries");
 
+typedef int (*qpack_static_table_lookup_fn) (const char *value, u32 value_len,
+                                            u8 *full_match);
+
+static int
+qpack_lookup_no_match (const char *value, u32 value_len, u8 *full_match)
+{
+  return -1;
+}
+
+static int
+qpack_lookup_accept_encoding (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[31].value,
+                              qpack_static_table[31].value_len);
+  return 31;
+}
+
+static int
+qpack_lookup_accept_language (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 72;
+}
+
+static int
+qpack_lookup_accept_ranges (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[32].value,
+                              qpack_static_table[32].value_len);
+  return 32;
+}
+
+static int
+qpack_lookup_accept (const char *value, u32 value_len, u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[29].value,
+                    qpack_static_table[29].value_len))
+    {
+      *full_match = 1;
+      return 29;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[30].value,
+                    qpack_static_table[30].value_len))
+    {
+      *full_match = 1;
+      return 30;
+    }
+
+  *full_match = 0;
+  return 29;
+}
+
+static int
+qpack_lookup_access_control_allow_credentials (const char *value,
+                                              u32 value_len, u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[73].value,
+                    qpack_static_table[73].value_len))
+    {
+      *full_match = 1;
+      return 73;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[74].value,
+                    qpack_static_table[74].value_len))
+    {
+      *full_match = 1;
+      return 74;
+    }
+
+  *full_match = 0;
+  return 73;
+}
+
+static int
+qpack_lookup_access_control_allow_headers (const char *value, u32 value_len,
+                                          u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[33].value,
+                    qpack_static_table[33].value_len))
+    {
+      *full_match = 1;
+      return 33;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[34].value,
+                    qpack_static_table[34].value_len))
+    {
+      *full_match = 1;
+      return 34;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[75].value,
+                    qpack_static_table[75].value_len))
+    {
+      *full_match = 1;
+      return 75;
+    }
+
+  *full_match = 0;
+  return 33;
+}
+
+static int
+qpack_lookup_access_control_allow_methods (const char *value, u32 value_len,
+                                          u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[76].value,
+                    qpack_static_table[76].value_len))
+    {
+      *full_match = 1;
+      return 76;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[77].value,
+                    qpack_static_table[77].value_len))
+    {
+      *full_match = 1;
+      return 77;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[78].value,
+                    qpack_static_table[78].value_len))
+    {
+      *full_match = 1;
+      return 78;
+    }
+
+  *full_match = 0;
+  return 76;
+}
+
+static int
+qpack_lookup_access_control_allow_origin (const char *value, u32 value_len,
+                                         u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[35].value,
+                              qpack_static_table[35].value_len);
+  return 35;
+}
+
+static int
+qpack_lookup_access_control_expose_headers (const char *value, u32 value_len,
+                                           u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[79].value,
+                    qpack_static_table[79].value_len))
+    {
+      *full_match = 1;
+      return 79;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[80].value,
+                    qpack_static_table[80].value_len))
+    {
+      *full_match = 1;
+      return 80;
+    }
+
+  *full_match = 0;
+  return 79;
+}
+
+static int
+qpack_lookup_access_control_request_method (const char *value, u32 value_len,
+                                           u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[81].value,
+                    qpack_static_table[81].value_len))
+    {
+      *full_match = 1;
+      return 81;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[82].value,
+                    qpack_static_table[82].value_len))
+    {
+      *full_match = 1;
+      return 82;
+    }
+
+  *full_match = 0;
+  return 81;
+}
+
+static int
+qpack_lookup_age (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[2].value,
+                              qpack_static_table[2].value_len);
+  return 2;
+}
+
+static int
+qpack_lookup_alt_svc (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[83].value,
+                              qpack_static_table[83].value_len);
+  return 83;
+}
+
+static int
+qpack_lookup_authorization (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 84;
+}
+
+static int
+qpack_lookup_cache_control (const char *value, u32 value_len, u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[36].value,
+                    qpack_static_table[36].value_len))
+    {
+      *full_match = 1;
+      return 36;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[37].value,
+                    qpack_static_table[37].value_len))
+    {
+      *full_match = 1;
+      return 37;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[38].value,
+                    qpack_static_table[38].value_len))
+    {
+      *full_match = 1;
+      return 38;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[39].value,
+                    qpack_static_table[39].value_len))
+    {
+      *full_match = 1;
+      return 39;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[40].value,
+                    qpack_static_table[40].value_len))
+    {
+      *full_match = 1;
+      return 40;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[41].value,
+                    qpack_static_table[41].value_len))
+    {
+      *full_match = 1;
+      return 41;
+    }
+
+  *full_match = 0;
+  return 36;
+}
+
+static int
+qpack_lookup_content_disposition (const char *value, u32 value_len,
+                                 u8 *full_match)
+{
+  *full_match = 0;
+  return 3;
+}
+
+static int
+qpack_lookup_content_encoding (const char *value, u32 value_len,
+                              u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[42].value,
+                    qpack_static_table[42].value_len))
+    {
+      *full_match = 1;
+      return 42;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[43].value,
+                    qpack_static_table[43].value_len))
+    {
+      *full_match = 1;
+      return 43;
+    }
+
+  *full_match = 0;
+  return 42;
+}
+
+static int
+qpack_lookup_content_security_policy (const char *value, u32 value_len,
+                                     u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[85].value,
+                              qpack_static_table[85].value_len);
+  return 85;
+}
+
+static int
+qpack_lookup_content_type (const char *value, u32 value_len, u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[44].value,
+                    qpack_static_table[44].value_len))
+    {
+      *full_match = 1;
+      return 44;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[45].value,
+                    qpack_static_table[45].value_len))
+    {
+      *full_match = 1;
+      return 45;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[46].value,
+                    qpack_static_table[46].value_len))
+    {
+      *full_match = 1;
+      return 46;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[47].value,
+                    qpack_static_table[47].value_len))
+    {
+      *full_match = 1;
+      return 47;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[48].value,
+                    qpack_static_table[48].value_len))
+    {
+      *full_match = 1;
+      return 48;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[49].value,
+                    qpack_static_table[49].value_len))
+    {
+      *full_match = 1;
+      return 49;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[50].value,
+                    qpack_static_table[50].value_len))
+    {
+      *full_match = 1;
+      return 50;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[51].value,
+                    qpack_static_table[51].value_len))
+    {
+      *full_match = 1;
+      return 51;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[52].value,
+                    qpack_static_table[52].value_len))
+    {
+      *full_match = 1;
+      return 52;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[53].value,
+                    qpack_static_table[53].value_len))
+    {
+      *full_match = 1;
+      return 53;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[54].value,
+                    qpack_static_table[54].value_len))
+    {
+      *full_match = 1;
+      return 54;
+    }
+
+  *full_match = 0;
+  return 44;
+}
+
+static int
+qpack_lookup_cookie (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 5;
+}
+
+static int
+qpack_lookup_date (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 6;
+}
+
+static int
+qpack_lookup_early_data (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[86].value,
+                              qpack_static_table[86].value_len);
+  return 86;
+}
+
+static int
+qpack_lookup_etag (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 7;
+}
+
+static int
+qpack_lookup_expect_ct (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 87;
+}
+
+static int
+qpack_lookup_forwarded (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 88;
+}
+
+static int
+qpack_lookup_if_modified_since (const char *value, u32 value_len,
+                               u8 *full_match)
+{
+  *full_match = 0;
+  return 8;
+}
+
+static int
+qpack_lookup_if_none_match (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 9;
+}
+
+static int
+qpack_lookup_if_range (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 89;
+}
+
+static int
+qpack_lookup_last_modified (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 10;
+}
+
+static int
+qpack_lookup_link (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 11;
+}
+
+static int
+qpack_lookup_location (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 12;
+}
+
+static int
+qpack_lookup_origin (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 90;
+}
+
+static int
+qpack_lookup_purpose (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[91].value,
+                              qpack_static_table[91].value_len);
+  return 91;
+}
+
+static int
+qpack_lookup_range (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[55].value,
+                              qpack_static_table[55].value_len);
+  return 55;
+}
+
+static int
+qpack_lookup_referer (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 13;
+}
+
+static int
+qpack_lookup_server (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 92;
+}
+
+static int
+qpack_lookup_set_cookie (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 14;
+}
+
+static int
+qpack_lookup_strict_transport_security (const char *value, u32 value_len,
+                                       u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[56].value,
+                    qpack_static_table[56].value_len))
+    {
+      *full_match = 1;
+      return 56;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[57].value,
+                    qpack_static_table[57].value_len))
+    {
+      *full_match = 1;
+      return 57;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[58].value,
+                    qpack_static_table[58].value_len))
+    {
+      *full_match = 1;
+      return 58;
+    }
+
+  *full_match = 0;
+  return 56;
+}
+
+static int
+qpack_lookup_timing_allow_origin (const char *value, u32 value_len,
+                                 u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[93].value,
+                              qpack_static_table[93].value_len);
+  return 93;
+}
+
+static int
+qpack_lookup_upgrade_insecure_requests (const char *value, u32 value_len,
+                                       u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[94].value,
+                              qpack_static_table[94].value_len);
+  return 94;
+}
+
+static int
+qpack_lookup_user_agent (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 95;
+}
+
+static int
+qpack_lookup_vary (const char *value, u32 value_len, u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[59].value,
+                    qpack_static_table[59].value_len))
+    {
+      *full_match = 1;
+      return 59;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[60].value,
+                    qpack_static_table[60].value_len))
+    {
+      *full_match = 1;
+      return 60;
+    }
+
+  *full_match = 0;
+  return 59;
+}
+
+static int
+qpack_lookup_x_content_type_options (const char *value, u32 value_len,
+                                    u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[61].value,
+                              qpack_static_table[61].value_len);
+  return 61;
+}
+
+static int
+qpack_lookup_x_forwarded_for (const char *value, u32 value_len, u8 *full_match)
+{
+  *full_match = 0;
+  return 96;
+}
+
+static int
+qpack_lookup_x_frame_options (const char *value, u32 value_len, u8 *full_match)
+{
+  if (http_token_is (value, value_len, qpack_static_table[97].value,
+                    qpack_static_table[97].value_len))
+    {
+      *full_match = 1;
+      return 97;
+    }
+  if (http_token_is (value, value_len, qpack_static_table[98].value,
+                    qpack_static_table[98].value_len))
+    {
+      *full_match = 1;
+      return 98;
+    }
+
+  *full_match = 0;
+  return 97;
+}
+
+static int
+qpack_lookup_x_xss_protection (const char *value, u32 value_len,
+                              u8 *full_match)
+{
+  *full_match = http_token_is (value, value_len, qpack_static_table[62].value,
+                              qpack_static_table[62].value_len);
+  return 62;
+}
+
+static qpack_static_table_lookup_fn qpack_static_table_lookup[] = {
+  [HTTP_HEADER_ACCEPT_CHARSET] = qpack_lookup_no_match,
+  [HTTP_HEADER_ACCEPT_ENCODING] = qpack_lookup_accept_encoding,
+  [HTTP_HEADER_ACCEPT_LANGUAGE] = qpack_lookup_accept_language,
+  [HTTP_HEADER_ACCEPT_RANGES] = qpack_lookup_accept_ranges,
+  [HTTP_HEADER_ACCEPT] = qpack_lookup_accept,
+  [HTTP_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS] =
+    qpack_lookup_access_control_allow_credentials,
+  [HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS] =
+    qpack_lookup_access_control_allow_headers,
+  [HTTP_HEADER_ACCESS_CONTROL_ALLOW_METHODS] =
+    qpack_lookup_access_control_allow_methods,
+  [HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN] =
+    qpack_lookup_access_control_allow_origin,
+  [HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS] =
+    qpack_lookup_access_control_expose_headers,
+  [HTTP_HEADER_ACCESS_CONTROL_MAX_AGE] = qpack_lookup_no_match,
+  [HTTP_HEADER_ACCESS_CONTROL_REQUEST_HEADERS] = qpack_lookup_no_match,
+  [HTTP_HEADER_ACCESS_CONTROL_REQUEST_METHOD] =
+    qpack_lookup_access_control_request_method,
+  [HTTP_HEADER_AGE] = qpack_lookup_age,
+  [HTTP_HEADER_ALLOW] = qpack_lookup_no_match,
+  [HTTP_HEADER_ALPN] = qpack_lookup_no_match,
+  [HTTP_HEADER_ALT_SVC] = qpack_lookup_alt_svc,
+  [HTTP_HEADER_ALT_USED] = qpack_lookup_no_match,
+  [HTTP_HEADER_ALTERNATES] = qpack_lookup_no_match,
+  [HTTP_HEADER_AUTHENTICATION_CONTROL] = qpack_lookup_no_match,
+  [HTTP_HEADER_AUTHENTICATION_INFO] = qpack_lookup_no_match,
+  [HTTP_HEADER_AUTHORIZATION] = qpack_lookup_authorization,
+  [HTTP_HEADER_CACHE_CONTROL] = qpack_lookup_cache_control,
+  [HTTP_HEADER_CACHE_STATUS] = qpack_lookup_no_match,
+  [HTTP_HEADER_CAPSULE_PROTOCOL] = qpack_lookup_no_match,
+  [HTTP_HEADER_CDN_CACHE_CONTROL] = qpack_lookup_no_match,
+  [HTTP_HEADER_CDN_LOOP] = qpack_lookup_no_match,
+  [HTTP_HEADER_CLIENT_CERT] = qpack_lookup_no_match,
+  [HTTP_HEADER_CLIENT_CERT_CHAIN] = qpack_lookup_no_match,
+  [HTTP_HEADER_CLOSE] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONNECTION] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONTENT_DIGEST] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONTENT_DISPOSITION] = qpack_lookup_content_disposition,
+  [HTTP_HEADER_CONTENT_ENCODING] = qpack_lookup_content_encoding,
+  [HTTP_HEADER_CONTENT_LANGUAGE] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONTENT_LENGTH] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONTENT_LOCATION] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONTENT_RANGE] = qpack_lookup_no_match,
+  [HTTP_HEADER_CONTENT_SECURITY_POLICY] = qpack_lookup_content_security_policy,
+  [HTTP_HEADER_CONTENT_TYPE] = qpack_lookup_content_type,
+  [HTTP_HEADER_COOKIE] = qpack_lookup_cookie,
+  [HTTP_HEADER_DATE] = qpack_lookup_date,
+  [HTTP_HEADER_DIGEST] = qpack_lookup_no_match,
+  [HTTP_HEADER_DPOP] = qpack_lookup_no_match,
+  [HTTP_HEADER_DPOP_NONCE] = qpack_lookup_no_match,
+  [HTTP_HEADER_EARLY_DATA] = qpack_lookup_early_data,
+  [HTTP_HEADER_ETAG] = qpack_lookup_etag,
+  [HTTP_HEADER_EXPECT] = qpack_lookup_no_match,
+  [HTTP_HEADER_EXPECT_CT] = qpack_lookup_expect_ct,
+  [HTTP_HEADER_EXPIRES] = qpack_lookup_no_match,
+  [HTTP_HEADER_FORWARDED] = qpack_lookup_forwarded,
+  [HTTP_HEADER_FROM] = qpack_lookup_no_match,
+  [HTTP_HEADER_HOST] = qpack_lookup_no_match,
+  [HTTP_HEADER_IF_MATCH] = qpack_lookup_no_match,
+  [HTTP_HEADER_IF_MODIFIED_SINCE] = qpack_lookup_if_modified_since,
+  [HTTP_HEADER_IF_NONE_MATCH] = qpack_lookup_if_none_match,
+  [HTTP_HEADER_IF_RANGE] = qpack_lookup_if_range,
+  [HTTP_HEADER_IF_UNMODIFIED_SINCE] = qpack_lookup_no_match,
+  [HTTP_HEADER_KEEP_ALIVE] = qpack_lookup_no_match,
+  [HTTP_HEADER_LAST_MODIFIED] = qpack_lookup_last_modified,
+  [HTTP_HEADER_LINK] = qpack_lookup_link,
+  [HTTP_HEADER_LOCATION] = qpack_lookup_location,
+  [HTTP_HEADER_MAX_FORWARDS] = qpack_lookup_no_match,
+  [HTTP_HEADER_ORIGIN] = qpack_lookup_origin,
+  [HTTP_HEADER_PRIORITY] = qpack_lookup_no_match,
+  [HTTP_HEADER_PROXY_AUTHENTICATE] = qpack_lookup_no_match,
+  [HTTP_HEADER_PROXY_AUTHENTICATION_INFO] = qpack_lookup_no_match,
+  [HTTP_HEADER_PROXY_AUTHORIZATION] = qpack_lookup_no_match,
+  [HTTP_HEADER_PROXY_STATUS] = qpack_lookup_no_match,
+  [HTTP_HEADER_PURPOSE] = qpack_lookup_purpose,
+  [HTTP_HEADER_RANGE] = qpack_lookup_range,
+  [HTTP_HEADER_REFERER] = qpack_lookup_referer,
+  [HTTP_HEADER_REFRESH] = qpack_lookup_no_match,
+  [HTTP_HEADER_REPR_DIGEST] = qpack_lookup_no_match,
+  [HTTP_HEADER_RETRY_AFTER] = qpack_lookup_no_match,
+  [HTTP_HEADER_SERVER] = qpack_lookup_server,
+  [HTTP_HEADER_SET_COOKIE] = qpack_lookup_set_cookie,
+  [HTTP_HEADER_SIGNATURE] = qpack_lookup_no_match,
+  [HTTP_HEADER_SIGNATURE_INPUT] = qpack_lookup_no_match,
+  [HTTP_HEADER_STRICT_TRANSPORT_SECURITY] =
+    qpack_lookup_strict_transport_security,
+  [HTTP_HEADER_TE] = qpack_lookup_no_match,
+  [HTTP_HEADER_TIMING_ALLOW_ORIGIN] = qpack_lookup_timing_allow_origin,
+  [HTTP_HEADER_TRAILER] = qpack_lookup_no_match,
+  [HTTP_HEADER_TRANSFER_ENCODING] = qpack_lookup_no_match,
+  [HTTP_HEADER_UPGRADE] = qpack_lookup_no_match,
+  [HTTP_HEADER_UPGRADE_INSECURE_REQUESTS] =
+    qpack_lookup_upgrade_insecure_requests,
+  [HTTP_HEADER_USER_AGENT] = qpack_lookup_user_agent,
+  [HTTP_HEADER_VARY] = qpack_lookup_vary,
+  [HTTP_HEADER_VIA] = qpack_lookup_no_match,
+  [HTTP_HEADER_WANT_CONTENT_DIGEST] = qpack_lookup_no_match,
+  [HTTP_HEADER_WANT_REPR_DIGEST] = qpack_lookup_no_match,
+  [HTTP_HEADER_WWW_AUTHENTICATE] = qpack_lookup_no_match,
+  [HTTP_HEADER_X_CONTENT_TYPE_OPTIONS] = qpack_lookup_x_content_type_options,
+  [HTTP_HEADER_X_FORWARDED_FOR] = qpack_lookup_x_forwarded_for,
+  [HTTP_HEADER_X_FRAME_OPTIONS] = qpack_lookup_x_frame_options,
+  [HTTP_HEADER_X_XSS_PROTECTION] = qpack_lookup_x_xss_protection,
+};
+
 static hpack_error_t
 qpack_get_static_table_entry (uword index, http_token_t *name,
                              http_token_t *value, u8 value_is_indexed)
@@ -194,6 +905,34 @@ qpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len,
     }
 }
 
+static_always_inline u8 *
+qpack_encode_string (u8 *dst, const u8 *value, uword value_len, u8 prefix_len)
+{
+  uword huff_len;
+
+  ASSERT (prefix_len >= 2 && prefix_len <= 8);
+
+  huff_len = hpack_huffman_encoded_len (value, value_len);
+  /* raw bytes might take fewer bytes */
+  if (huff_len >= value_len)
+    {
+      /* clear H flag and rest of the bits */
+      *dst &= (u8) ~((1 << (prefix_len)) - 1);
+      dst = hpack_encode_int (dst, value_len, prefix_len - 1);
+      clib_memcpy (dst, value, value_len);
+      return dst + value_len;
+    }
+
+  /* set H flag */
+  *dst |= 1 << (prefix_len - 1);
+  /* clear rest of the prefix bits */
+  *dst &= (u8) ~((1 << (prefix_len - 1)) - 1);
+  dst = hpack_encode_int (dst, huff_len, prefix_len - 1);
+  dst = hpack_encode_huffman (dst, value, value_len);
+
+  return dst;
+}
+
 __clib_export hpack_error_t
 qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
                     u32 *name_len, u32 *value_len, void *decoder_ctx)
@@ -281,6 +1020,71 @@ qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
   return HPACK_ERROR_NONE;
 }
 
+__clib_export u8 *
+qpack_encode_header (u8 *dst, http_header_name_t name, const u8 *value,
+                    u32 value_len)
+{
+  int index;
+  u8 *a, *b, full_match;
+  u32 orig_len, actual_size;
+
+  orig_len = vec_len (dst);
+  index = qpack_static_table_lookup[name]((const char *) value, value_len,
+                                         &full_match);
+  if (index > -1)
+    {
+      if (full_match)
+       {
+         /* indexed field line, static table */
+         vec_add2 (dst, a, 2);
+         *a = 0xC0;
+         b = hpack_encode_int (a, index, 6);
+       }
+      else
+       {
+         /* literal field line with name reference, static table */
+         vec_add2 (dst, a, 2 + value_len + HPACK_ENCODED_INT_MAX_LEN);
+         *a = 0x50;
+         b = hpack_encode_int (a, index, 4);
+         b = qpack_encode_string (b, value, value_len, 8);
+       }
+    }
+  else
+    {
+      /* literal field line with literal name */
+      const hpack_token_t *name_token = &hpack_headers[name];
+      vec_add2 (dst, a,
+               name_token->len + value_len + HPACK_ENCODED_INT_MAX_LEN * 2 +
+                 1);
+      *a = 0x20;
+      b = qpack_encode_string (a, (const u8 *) name_token->base,
+                              name_token->len, 4);
+      b = qpack_encode_string (b, value, value_len, 8);
+    }
+
+  actual_size = b - a;
+  vec_set_len (dst, orig_len + actual_size);
+  return dst;
+}
+
+__clib_export u8 *
+qpack_encode_custom_header (u8 *dst, const u8 *name, u32 name_len,
+                           const u8 *value, u32 value_len)
+{
+  u8 *a, *b;
+  u32 orig_len, actual_size;
+
+  orig_len = vec_len (dst);
+  /* literal field line with literal name */
+  vec_add2 (dst, a, name_len + value_len + HPACK_ENCODED_INT_MAX_LEN * 2 + 1);
+  *a = 0x20;
+  b = qpack_encode_string (a, name, name_len, 4);
+  b = qpack_encode_string (b, value, value_len, 8);
+  actual_size = b - a;
+  vec_set_len (dst, orig_len + actual_size);
+  return dst;
+}
+
 static const http3_error_t hpack_error_to_http3_error[] = {
   [HPACK_ERROR_NONE] = HTTP3_ERROR_NO_ERROR,
   [HPACK_ERROR_COMPRESSION] = HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED,
index 11f5823..a4a08c2 100644 (file)
@@ -1722,6 +1722,59 @@ http_test_qpack (vlib_main_t *vm)
   vec_free (buf);
   vec_free (headers);
 
+  vlib_cli_output (vm, "qpack_encode_header");
+
+  static u8 *(*_qpack_encode_header) (u8 * dst, http_header_name_t name,
+                                     const u8 *value, u32 value_len);
+  _qpack_encode_header =
+    vlib_get_plugin_symbol ("http_plugin.so", "qpack_encode_header");
+
+  /* indexed field line, static table */
+  buf = _qpack_encode_header (buf, HTTP_HEADER_CACHE_CONTROL,
+                             (const u8 *) "no-cache", 8);
+  HTTP_TEST ((vec_len (buf) == 1 && buf[0] == 0xE7),
+            "'cache-control: no-cache' encoded as: %U", format_hex_bytes, buf,
+            vec_len (buf));
+  vec_free (buf);
+
+  /* literal field line with name reference, static table */
+  u8 expected1[] = "\x56\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95"
+                  "\x04\x0B\x81\x66\xE0\x82\xA6\x2D\x1B\xFF";
+  buf = _qpack_encode_header (
+    buf, HTTP_HEADER_DATE, (const u8 *) "Mon, 21 Oct 2013 20:13:21 GMT", 29);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected1) - 1) &&
+             !memcmp (buf, expected1, vec_len (buf))),
+            "'date: Mon, 21 Oct 2013 20:13:21 GMT' encoded as: %U",
+            format_hex_bytes, buf, vec_len (buf));
+  vec_free (buf);
+
+  /* literal field line with literal name */
+  u8 expected2[] = "\x2F\x01\x20\xC9\x39\x56\x42\x46\x9B\x51\x8D\xC1\xE4\x74"
+                  "\xD7\x41\x6F\x0C\x93\x97\xED\x49\xCC\x9F";
+  buf = _qpack_encode_header (buf, HTTP_HEADER_CACHE_STATUS,
+                             (const u8 *) "ExampleCache; hit", 17);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected2) - 1) &&
+             !memcmp (buf, expected2, vec_len (buf))),
+            "'cache-status: ExampleCache; hit' encoded as: %U",
+            format_hex_bytes, buf, vec_len (buf));
+  vec_free (buf);
+
+  vlib_cli_output (vm, "qpack_encode_custom_header");
+
+  static u8 *(*_qpack_encode_custom_header) (
+    u8 * dst, const u8 *name, u32 name_len, const u8 *value, u32 value_len);
+  _qpack_encode_custom_header =
+    vlib_get_plugin_symbol ("http_plugin.so", "qpack_encode_custom_header");
+
+  u8 expected3[] = "\x2E\x40\xEA\x93\xC1\x89\x3F\x83\x45\x63\xA7";
+  buf = _qpack_encode_custom_header (buf, (const u8 *) "sandwich", 8,
+                                    (const u8 *) "spam", 4);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected3) - 1) &&
+             !memcmp (buf, expected3, vec_len (buf))),
+            "'sandwich: spam' encoded as: %U", format_hex_bytes, buf,
+            vec_len (buf));
+  vec_free (buf);
+
   return 0;
 }