http: move generic functions to hpack_inlines.h 51/43751/4
authorMatus Fabian <[email protected]>
Thu, 25 Sep 2025 10:08:56 +0000 (06:08 -0400)
committerFlorin Coras <[email protected]>
Mon, 29 Sep 2025 16:24:59 +0000 (16:24 +0000)
Type: refactor

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

index 0dd68b6..b1461df 100644 (file)
@@ -2,12 +2,11 @@
  * Copyright(c) 2025 Cisco Systems, Inc.
  */
 
-#include <vppinfra/error.h>
 #include <vppinfra/ring.h>
 #include <http/http2/hpack.h>
-#include <http/http2/huffman_table.h>
 #include <http/http_status_codes.h>
 #include <http/http_private.h>
+#include <http/http2/hpack_inlines.h>
 
 #define HPACK_STATIC_TABLE_SIZE 61
 
@@ -109,153 +108,14 @@ static http_token_t http_methods[] = {
 
 #define http_method_token(e) http_methods[e].base, http_methods[e].len
 
-__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;
-}
-
-http2_error_t
-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 HTTP2_ERROR_INTERNAL_ERROR;
-      /* 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);
-         /* this might happen with invalid EOS (longer than 7 bits) */
-         if (hg->code_len > accumulator_len)
-           return HTTP2_ERROR_COMPRESSION_ERROR;
-         /* trim code to correct length */
-         u32 code = (accumulator >> (accumulator_len - hg->code_len)) &
-                    ((1 << hg->code_len) - 1);
-         if (!code)
-           return HTTP2_ERROR_COMPRESSION_ERROR;
-         /* 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 EOS case */
-             if (((1 << accumulator_len) - 1) ==
-                 (accumulator & ((1 << accumulator_len) - 1)))
-               break;
-
-             /* out of space?  */
-             if (*buf_len == 0)
-               return HTTP2_ERROR_INTERNAL_ERROR;
-
-             /* 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 EOS here */
-         if (((1 << accumulator_len) - 1) !=
-             (accumulator & ((1 << accumulator_len) - 1)))
-           return HTTP2_ERROR_COMPRESSION_ERROR;
-         break;
-       }
-    }
-  return HTTP2_ERROR_NO_ERROR;
-}
-
-__clib_export http2_error_t
+__clib_export hpack_error_t
 hpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len)
 {
   u8 *p, is_huffman;
   uword len;
 
   if (*src == end)
-    return HTTP2_ERROR_COMPRESSION_ERROR;
+    return HPACK_ERROR_COMPRESSION;
 
   p = *src;
   /* H flag in first bit */
@@ -264,11 +124,11 @@ hpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len)
   /* length is integer with 7 bit prefix */
   len = hpack_decode_int (&p, end, 7);
   if (PREDICT_FALSE (len == HPACK_INVALID_INT))
-    return HTTP2_ERROR_COMPRESSION_ERROR;
+    return HPACK_ERROR_COMPRESSION;
 
   /* do we have everything? */
   if (len > (end - p))
-    return HTTP2_ERROR_COMPRESSION_ERROR;
+    return HPACK_ERROR_COMPRESSION;
 
   if (is_huffman)
     {
@@ -279,108 +139,14 @@ hpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len)
     {
       /* enough space? */
       if (len > *buf_len)
-       return HTTP2_ERROR_INTERNAL_ERROR;
+       return HPACK_ERROR_UNKNOWN;
 
       clib_memcpy (*buf, p, len);
       *buf_len -= len;
       *buf += len;
       *src = (p + len);
-      return HTTP2_ERROR_NO_ERROR;
-    }
-}
-
-__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 HPACK_ERROR_NONE;
     }
-  return dst;
 }
 
 __clib_export u8 *
@@ -511,7 +277,7 @@ hpack_dynamic_table_add (hpack_dynamic_table_t *table, http_token_t *name,
            hpack_dynamic_table_entry_value_len (e));
 }
 
-static http2_error_t
+static hpack_error_t
 hpack_get_table_entry (uword index, http_token_t *name, http_token_t *value,
                       u8 value_is_indexed, hpack_dynamic_table_t *dt)
 {
@@ -527,7 +293,7 @@ hpack_get_table_entry (uword index, http_token_t *name, http_token_t *value,
        }
       HTTP_DBG (2, "[%llu] %U: %U", index, format_http_bytes, e->name,
                e->name_len, format_http_bytes, e->value, e->value_len);
-      return HTTP2_ERROR_NO_ERROR;
+      return HPACK_ERROR_NONE;
     }
   else
     {
@@ -536,7 +302,7 @@ hpack_get_table_entry (uword index, http_token_t *name, http_token_t *value,
       if (PREDICT_FALSE (!e))
        {
          HTTP_DBG (1, "index %llu not in dynamic table", index);
-         return HTTP2_ERROR_COMPRESSION_ERROR;
+         return HPACK_ERROR_COMPRESSION;
        }
       name->base = (char *) e->buf;
       name->len = e->name_len;
@@ -544,19 +310,20 @@ hpack_get_table_entry (uword index, http_token_t *name, http_token_t *value,
       value->len = hpack_dynamic_table_entry_value_len (e);
       HTTP_DBG (2, "[%llu] %U: %U", index, format_http_bytes, name->base,
                name->len, format_http_bytes, value->base, value->len);
-      return HTTP2_ERROR_NO_ERROR;
+      return HPACK_ERROR_NONE;
     }
 }
 
-__clib_export http2_error_t
+__clib_export hpack_error_t
 hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
-                    u32 *name_len, u32 *value_len, hpack_dynamic_table_t *dt)
+                    u32 *name_len, u32 *value_len, void *decoder_ctx)
 {
+  hpack_dynamic_table_t *dt = (hpack_dynamic_table_t *) decoder_ctx;
   u8 *p;
   u8 value_is_indexed = 0, add_new_entry = 0;
   uword old_len, new_max, index = 0;
   http_token_t name, value;
-  http2_error_t rv;
+  hpack_error_t rv;
 
   ASSERT (*src < end);
   p = *src;
@@ -568,7 +335,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       if (p == end || new_max > (uword) dt->max_size)
        {
          HTTP_DBG (1, "invalid dynamic table size update");
-         return HTTP2_ERROR_COMPRESSION_ERROR;
+         return HPACK_ERROR_COMPRESSION;
        }
       while (clib_ring_n_enq (dt->entries) && new_max > dt->used)
        hpack_dynamic_table_evict_one (dt);
@@ -582,7 +349,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       if (index == 0 || index == HPACK_INVALID_INT)
        {
          HTTP_DBG (1, "invalid index");
-         return HTTP2_ERROR_COMPRESSION_ERROR;
+         return HPACK_ERROR_COMPRESSION;
        }
       value_is_indexed = 1;
     }
@@ -593,7 +360,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       if (index == 0 || index == HPACK_INVALID_INT)
        {
          HTTP_DBG (1, "invalid index");
-         return HTTP2_ERROR_COMPRESSION_ERROR;
+         return HPACK_ERROR_COMPRESSION;
        }
       add_new_entry = 1;
     }
@@ -613,7 +380,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
          if (index == 0 || index == HPACK_INVALID_INT)
            {
              HTTP_DBG (1, "invalid index");
-             return HTTP2_ERROR_COMPRESSION_ERROR;
+             return HPACK_ERROR_COMPRESSION;
            }
        }
     }
@@ -621,7 +388,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
   if (index)
     {
       rv = hpack_get_table_entry (index, &name, &value, value_is_indexed, dt);
-      if (rv != HTTP2_ERROR_NO_ERROR)
+      if (rv)
        {
          HTTP_DBG (1, "entry index %llu error", index);
          return rv;
@@ -629,7 +396,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       if (name.len > *buf_len)
        {
          HTTP_DBG (1, "not enough space");
-         return HTTP2_ERROR_INTERNAL_ERROR;
+         return HPACK_ERROR_UNKNOWN;
        }
       clib_memcpy (*buf, name.base, name.len);
       *buf_len -= name.len;
@@ -640,7 +407,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
          if (value.len > *buf_len)
            {
              HTTP_DBG (1, "not enough space");
-             return HTTP2_ERROR_INTERNAL_ERROR;
+             return HPACK_ERROR_UNKNOWN;
            }
          clib_memcpy (*buf, value.base, value.len);
          *buf_len -= value.len;
@@ -653,7 +420,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       old_len = *buf_len;
       name.base = (char *) *buf;
       rv = hpack_decode_string (&p, end, buf, buf_len);
-      if (rv != HTTP2_ERROR_NO_ERROR)
+      if (rv)
        {
          HTTP_DBG (1, "invalid header name");
          return rv;
@@ -667,7 +434,7 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       old_len = *buf_len;
       value.base = (char *) *buf;
       rv = hpack_decode_string (&p, end, buf, buf_len);
-      if (rv != HTTP2_ERROR_NO_ERROR)
+      if (rv)
        {
          HTTP_DBG (1, "invalid header value");
          return rv;
@@ -680,290 +447,15 @@ hpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
     hpack_dynamic_table_add (dt, &name, &value);
 
   *src = p;
-  return HTTP2_ERROR_NO_ERROR;
+  return HPACK_ERROR_NONE;
 }
 
-static inline u8
-hpack_header_name_is_valid (u8 *name, u32 name_len)
-{
-  u32 i;
-  static uword tchar[4] = {
-    /* !#$%'*+-.0123456789 */
-    0x03ff6cba00000000,
-    /* ^_`abcdefghijklmnopqrstuvwxyz|~ */
-    0x57ffffffc0000000,
-    0x0000000000000000,
-    0x0000000000000000,
-  };
-  for (i = 0; i < name_len; i++)
-    {
-      if (!clib_bitmap_get_no_check (tchar, name[i]))
-       return 0;
-    }
-  return 1;
-}
-
-static inline u8
-hpack_header_value_is_valid (u8 *value, u32 value_len)
-{
-  u32 i;
-  /* VCHAR / SP / HTAB / %x80-FF */
-  static uword tchar[4] = {
-    0xffffffff00000200,
-    0x7fffffffffffffff,
-    0xffffffffffffffff,
-    0xffffffffffffffff,
-  };
-
-  if (value_len == 0)
-    return 1;
-
-  /* must not start or end with SP or HTAB */
-  if ((value[0] == 0x20 || value[0] == 0x09 || value[value_len - 1] == 0x20 ||
-       value[value_len - 1] == 0x09))
-    return 0;
-
-  for (i = 0; i < value_len; i++)
-    {
-      if (!clib_bitmap_get_no_check (tchar, value[i]))
-       return 0;
-    }
-  return 1;
-}
-
-static inline http_req_method_t
-hpack_parse_method (u8 *value, u32 value_len)
-{
-  switch (value_len)
-    {
-    case 3:
-      if (!memcmp (value, "GET", 3))
-       return HTTP_REQ_GET;
-      break;
-    case 4:
-      if (!memcmp (value, "POST", 4))
-       return HTTP_REQ_POST;
-      break;
-    case 7:
-      if (!memcmp (value, "CONNECT", 7))
-       return HTTP_REQ_CONNECT;
-      break;
-    default:
-      break;
-    }
-  /* HPACK should return only connection errors, this one is stream error */
-  return HTTP_REQ_UNKNOWN;
-}
-
-static inline http_url_scheme_t
-hpack_parse_scheme (u8 *value, u32 value_len)
-{
-  switch (value_len)
-    {
-    case 4:
-      if (!memcmp (value, "http", 4))
-       return HTTP_URL_SCHEME_HTTP;
-      break;
-    case 5:
-      if (!memcmp (value, "https", 5))
-       return HTTP_URL_SCHEME_HTTPS;
-      break;
-    default:
-      break;
-    }
-  /* HPACK should return only connection errors, this one is stream error */
-  return HTTP_URL_SCHEME_UNKNOWN;
-}
-
-static inline int
-hpack_parse_status_code (u8 *value, u32 value_len, http_status_code_t *sc)
-{
-  u16 status_code = 0;
-  u8 *p;
-
-  if (value_len != 3)
-    return HTTP2_ERROR_PROTOCOL_ERROR;
-
-  p = value;
-  parse_int (status_code, 100);
-  parse_int (status_code, 10);
-  parse_int (status_code, 1);
-  if (status_code < 100 || status_code > 599)
-    {
-      HTTP_DBG (1, "invalid status code %d", status_code);
-      return -1;
-    }
-  HTTP_DBG (1, "status code: %d", status_code);
-  *sc = http_sc_by_u16 (status_code);
-
-  return 0;
-}
-
-static http2_error_t
-hpack_parse_req_pseudo_header (u8 *name, u32 name_len, u8 *value,
-                              u32 value_len,
-                              hpack_request_control_data_t *control_data)
-{
-  HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len, format_http_bytes,
-           value, value_len);
-  switch (name_len)
-    {
-    case 5:
-      if (!memcmp (name + 1, "path", 4))
-       {
-         if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_PATH_PARSED ||
-             value_len == 0)
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_PATH_PARSED;
-         control_data->path = value;
-         control_data->path_len = value_len;
-         break;
-       }
-      return HTTP2_ERROR_PROTOCOL_ERROR;
-    case 7:
-      switch (name[1])
-       {
-       case 'm':
-         if (!memcmp (name + 2, "ethod", 5))
-           {
-             if (control_data->parsed_bitmap &
-                 HPACK_PSEUDO_HEADER_METHOD_PARSED)
-               return HTTP2_ERROR_PROTOCOL_ERROR;
-             control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_METHOD_PARSED;
-             control_data->method = hpack_parse_method (value, value_len);
-             break;
-           }
-         return HTTP2_ERROR_PROTOCOL_ERROR;
-       case 's':
-         if (!memcmp (name + 2, "cheme", 5))
-           {
-             if (control_data->parsed_bitmap &
-                 HPACK_PSEUDO_HEADER_SCHEME_PARSED)
-               return HTTP2_ERROR_PROTOCOL_ERROR;
-             control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_SCHEME_PARSED;
-             control_data->scheme = hpack_parse_scheme (value, value_len);
-             break;
-           }
-         return HTTP2_ERROR_PROTOCOL_ERROR;
-       default:
-         return HTTP2_ERROR_PROTOCOL_ERROR;
-       }
-      break;
-    case 9:
-      if (!memcmp (name + 1, "protocol", 8))
-       {
-         if (control_data->parsed_bitmap &
-             HPACK_PSEUDO_HEADER_PROTOCOL_PARSED)
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_PROTOCOL_PARSED;
-         control_data->protocol = value;
-         control_data->protocol_len = value_len;
-         break;
-       }
-      break;
-    case 10:
-      if (!memcmp (name + 1, "authority", 9))
-       {
-         if (control_data->parsed_bitmap &
-             HPACK_PSEUDO_HEADER_AUTHORITY_PARSED)
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
-         control_data->authority = value;
-         control_data->authority_len = value_len;
-         break;
-       }
-      return HTTP2_ERROR_PROTOCOL_ERROR;
-    default:
-      return HTTP2_ERROR_PROTOCOL_ERROR;
-    }
-
-  return HTTP2_ERROR_NO_ERROR;
-}
-
-static http2_error_t
-hpack_parse_resp_pseudo_header (u8 *name, u32 name_len, u8 *value,
-                               u32 value_len,
-                               hpack_response_control_data_t *control_data)
-{
-  HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len, format_http_bytes,
-           value, value_len);
-  switch (name_len)
-    {
-    case 7:
-      if (!memcmp (name + 1, "status", 6))
-       {
-         if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_STATUS_PARSED)
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_STATUS_PARSED;
-         if (hpack_parse_status_code (value, value_len, &control_data->sc))
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-       }
-      break;
-    default:
-      return HTTP2_ERROR_PROTOCOL_ERROR;
-    }
-
-  return HTTP2_ERROR_NO_ERROR;
-}
-
-/* Special treatment for headers like:
- *
- * RFC9113 8.2.2: any message containing connection-specific header
- * fields MUST be treated as malformed (connection, upgrade, keep-alive,
- * proxy-connection, transfer-encoding), TE header MUST NOT contain any value
- * other than "trailers"
- *
- * find headers that will be used later in preprocessing (content-length)
- */
-always_inline http2_error_t
-hpack_preprocess_header (u8 *name, u32 name_len, u8 *value, u32 value_len,
-                        uword index, uword *content_len_header_index)
-{
-  switch (name_len)
-    {
-    case 2:
-      if (name[0] == 't' && name[1] == 'e' &&
-         !http_token_is_case ((const char *) value, value_len,
-                              http_token_lit ("trailers")))
-       return HTTP2_ERROR_PROTOCOL_ERROR;
-      break;
-    case 7:
-      if (!memcmp (name, "upgrade", 7))
-       return HTTP2_ERROR_PROTOCOL_ERROR;
-      break;
-    case 10:
-      switch (name[0])
-       {
-       case 'c':
-         if (!memcmp (name + 1, "onnection", 9))
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         break;
-       case 'k':
-         if (!memcmp (name + 1, "eep-alive", 9))
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         break;
-       default:
-         break;
-       }
-      break;
-    case 14:
-      if (!memcmp (name, "content-length", 14) &&
-         *content_len_header_index == ~0)
-       *content_len_header_index = index;
-      break;
-    case 16:
-      if (!memcmp (name, "proxy-connection", 16))
-       return HTTP2_ERROR_PROTOCOL_ERROR;
-      break;
-    case 17:
-      if (!memcmp (name, "transfer-encoding", 17))
-       return HTTP2_ERROR_PROTOCOL_ERROR;
-      break;
-    default:
-      break;
-    }
-  return HTTP2_ERROR_NO_ERROR;
-}
+static const http2_error_t hpack_error_to_http2_error[] = {
+  [HPACK_ERROR_NONE] = HTTP2_ERROR_NO_ERROR,
+  [HPACK_ERROR_COMPRESSION] = HTTP2_ERROR_COMPRESSION_ERROR,
+  [HPACK_ERROR_PROTOCOL] = HTTP2_ERROR_PROTOCOL_ERROR,
+  [HPACK_ERROR_UNKNOWN] = HTTP2_ERROR_INTERNAL_ERROR,
+};
 
 __clib_export http2_error_t
 hpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
@@ -971,88 +463,12 @@ hpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
                     http_field_line_t **headers,
                     hpack_dynamic_table_t *dynamic_table)
 {
-  u8 *p, *end, *b, *name, *value;
-  u8 regular_header_parsed = 0;
-  u32 name_len, value_len;
-  uword b_left;
-  http_field_line_t *header;
-  http2_error_t rv;
-
-  p = src;
-  end = src + src_len;
-  b = dst;
-  b_left = dst_len;
-  control_data->parsed_bitmap = 0;
-  control_data->headers_len = 0;
-  control_data->content_len_header_index = ~0;
-
-  while (p != end)
-    {
-      name = b;
-      rv = hpack_decode_header (&p, end, &b, &b_left, &name_len, &value_len,
-                               dynamic_table);
-      if (rv != HTTP2_ERROR_NO_ERROR)
-       {
-         HTTP_DBG (1, "hpack_decode_header: %U", format_http2_error, rv);
-         return rv;
-       }
-      value = name + name_len;
-
-      /* pseudo header */
-      if (name[0] == ':')
-       {
-         /* all pseudo-headers must be before regular headers */
-         if (regular_header_parsed)
-           {
-             HTTP_DBG (1, "pseudo-headers after regular header");
-             return HTTP2_ERROR_PROTOCOL_ERROR;
-           }
-         rv = hpack_parse_req_pseudo_header (name, name_len, value, value_len,
-                                             control_data);
-         if (rv != HTTP2_ERROR_NO_ERROR)
-           {
-             HTTP_DBG (1, "hpack_parse_req_pseudo_header: %U",
-                       format_http2_error, rv);
-             return rv;
-           }
-         continue;
-       }
-      else
-       {
-         if (!hpack_header_name_is_valid (name, name_len))
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         if (!regular_header_parsed)
-           {
-             regular_header_parsed = 1;
-             control_data->headers = name;
-           }
-       }
-      if (!hpack_header_value_is_valid (value, value_len))
-       return HTTP2_ERROR_PROTOCOL_ERROR;
-      vec_add2 (*headers, header, 1);
-      HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len,
-               format_http_bytes, value, value_len);
-      header->name_offset = name - control_data->headers;
-      header->name_len = name_len;
-      header->value_offset = value - control_data->headers;
-      header->value_len = value_len;
-      control_data->headers_len += name_len;
-      control_data->headers_len += value_len;
-      if (regular_header_parsed)
-       {
-         rv = hpack_preprocess_header (
-           name, name_len, value, value_len, header - *headers,
-           &control_data->content_len_header_index);
-         if (rv != HTTP2_ERROR_NO_ERROR)
-           {
-             HTTP_DBG (1, "connection-specific header present");
-             return rv;
-           }
-       }
-    }
-  control_data->control_data_len = dst_len - b_left;
-  HTTP_DBG (2, "%U", format_hpack_dynamic_table, dynamic_table);
-  return HTTP2_ERROR_NO_ERROR;
+  hpack_error_t rv;
+  rv = hpack_decode_request (src, src + src_len, dst, dst_len, control_data,
+                            headers, (void *) dynamic_table,
+                            hpack_decode_header);
+  HTTP_DBG (3, "%U", format_hpack_dynamic_table, dynamic_table);
+  return hpack_error_to_http2_error[rv];
 }
 
 __clib_export http2_error_t
@@ -1061,88 +477,12 @@ hpack_parse_response (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
                      http_field_line_t **headers,
                      hpack_dynamic_table_t *dynamic_table)
 {
-  u8 *p, *end, *b, *name, *value;
-  u8 regular_header_parsed = 0;
-  u32 name_len, value_len;
-  uword b_left;
-  http_field_line_t *header;
-  http2_error_t rv;
-
-  p = src;
-  end = src + src_len;
-  b = dst;
-  b_left = dst_len;
-  control_data->parsed_bitmap = 0;
-  control_data->headers_len = 0;
-  control_data->content_len_header_index = ~0;
-
-  while (p != end)
-    {
-      name = b;
-      rv = hpack_decode_header (&p, end, &b, &b_left, &name_len, &value_len,
-                               dynamic_table);
-      if (rv != HTTP2_ERROR_NO_ERROR)
-       {
-         HTTP_DBG (1, "hpack_decode_header: %U", format_http2_error, rv);
-         return rv;
-       }
-      value = name + name_len;
-
-      /* pseudo header */
-      if (name[0] == ':')
-       {
-         /* all pseudo-headers must be before regular headers */
-         if (regular_header_parsed)
-           {
-             HTTP_DBG (1, "pseudo-headers after regular header");
-             return HTTP2_ERROR_PROTOCOL_ERROR;
-           }
-         rv = hpack_parse_resp_pseudo_header (name, name_len, value,
-                                              value_len, control_data);
-         if (rv != HTTP2_ERROR_NO_ERROR)
-           {
-             HTTP_DBG (1, "hpack_parse_resp_pseudo_header: %U",
-                       format_http2_error, rv);
-             return rv;
-           }
-         continue;
-       }
-      else
-       {
-         if (!hpack_header_name_is_valid (name, name_len))
-           return HTTP2_ERROR_PROTOCOL_ERROR;
-         if (!regular_header_parsed)
-           {
-             regular_header_parsed = 1;
-             control_data->headers = name;
-           }
-       }
-      if (!hpack_header_value_is_valid (value, value_len))
-       return HTTP2_ERROR_PROTOCOL_ERROR;
-      vec_add2 (*headers, header, 1);
-      HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len,
-               format_http_bytes, value, value_len);
-      header->name_offset = name - control_data->headers;
-      header->name_len = name_len;
-      header->value_offset = value - control_data->headers;
-      header->value_len = value_len;
-      control_data->headers_len += name_len;
-      control_data->headers_len += value_len;
-      if (regular_header_parsed)
-       {
-         rv = hpack_preprocess_header (
-           name, name_len, value, value_len, header - *headers,
-           &control_data->content_len_header_index);
-         if (rv != HTTP2_ERROR_NO_ERROR)
-           {
-             HTTP_DBG (1, "connection-specific header present");
-             return rv;
-           }
-       }
-    }
-  control_data->control_data_len = dst_len - b_left;
-  HTTP_DBG (2, "%U", format_hpack_dynamic_table, dynamic_table);
-  return HTTP2_ERROR_NO_ERROR;
+  hpack_error_t rv;
+  rv = hpack_decode_response (src, src + src_len, dst, dst_len, control_data,
+                             headers, (void *) dynamic_table,
+                             hpack_decode_header);
+  HTTP_DBG (3, "%U", format_hpack_dynamic_table, dynamic_table);
+  return hpack_error_to_http2_error[rv];
 }
 
 static inline u8 *
index 72b9f2b..2a84c55 100644 (file)
@@ -80,69 +80,6 @@ typedef struct
   u32 control_data_len;
 } hpack_response_control_data_t;
 
-/**
- * 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 HTTP2_ERROR_NO_ERROR on success
- *
- * @note Caller is responsible to check if there is somthing left in source
- * buffer first
- */
-http2_error_t 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);
-
 /**
  * Initialize HPACK dynamic table
  *
diff --git a/src/plugins/http/http2/hpack_inlines.h b/src/plugins/http/http2/hpack_inlines.h
new file mode 100644 (file)
index 0000000..50a0825
--- /dev/null
@@ -0,0 +1,765 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_PLUGINS_HTTP_HPACK_INLINES_H_
+#define SRC_PLUGINS_HTTP_HPACK_INLINES_H_
+
+#include <vppinfra/error.h>
+#include <http/http_private.h>
+#include <http/http2/hpack.h>
+#include <http/http2/huffman_table.h>
+
+typedef enum
+{
+  HPACK_ERROR_NONE,
+  HPACK_ERROR_COMPRESSION,
+  HPACK_ERROR_PROTOCOL,
+  HPACK_ERROR_UNKNOWN,
+} hpack_error_t;
+
+typedef hpack_error_t (hpack_header_decoder_fn) (u8 **src, u8 *end, u8 **buf,
+                                                uword *buf_len, u32 *name_len,
+                                                u32 *value_len,
+                                                void *decoder_ctx);
+
+/**
+ * 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
+ */
+always_inline 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;
+}
+
+/**
+ * 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
+ */
+always_inline 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;
+}
+
+/**
+ * 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 HPACK_ERROR_NONE on success
+ *
+ * @note Caller is responsible to check if there is somthing left in source
+ * buffer first
+ */
+always_inline hpack_error_t
+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 HPACK_ERROR_UNKNOWN;
+      /* 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);
+         /* this might happen with invalid EOS (longer than 7 bits) */
+         if (hg->code_len > accumulator_len)
+           return HPACK_ERROR_COMPRESSION;
+         /* trim code to correct length */
+         u32 code = (accumulator >> (accumulator_len - hg->code_len)) &
+                    ((1 << hg->code_len) - 1);
+         if (!code)
+           return HPACK_ERROR_COMPRESSION;
+         /* 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 EOS case */
+             if (((1 << accumulator_len) - 1) ==
+                 (accumulator & ((1 << accumulator_len) - 1)))
+               break;
+
+             /* out of space?  */
+             if (*buf_len == 0)
+               return HPACK_ERROR_UNKNOWN;
+
+             /* 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 EOS here */
+         if (((1 << accumulator_len) - 1) !=
+             (accumulator & ((1 << accumulator_len) - 1)))
+           return HPACK_ERROR_COMPRESSION;
+         break;
+       }
+    }
+  return HPACK_ERROR_NONE;
+}
+
+/**
+ * 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
+ */
+always_inline 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;
+}
+
+/**
+ * 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
+ */
+always_inline 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;
+}
+
+always_inline u8
+hpack_header_name_is_valid (u8 *name, u32 name_len)
+{
+  u32 i;
+  static uword tchar[4] = {
+    /* !#$%'*+-.0123456789 */
+    0x03ff6cba00000000,
+    /* ^_`abcdefghijklmnopqrstuvwxyz|~ */
+    0x57ffffffc0000000,
+    0x0000000000000000,
+    0x0000000000000000,
+  };
+  for (i = 0; i < name_len; i++)
+    {
+      if (!clib_bitmap_get_no_check (tchar, name[i]))
+       return 0;
+    }
+  return 1;
+}
+
+always_inline u8
+hpack_header_value_is_valid (u8 *value, u32 value_len)
+{
+  u32 i;
+  /* VCHAR / SP / HTAB / %x80-FF */
+  static uword tchar[4] = {
+    0xffffffff00000200,
+    0x7fffffffffffffff,
+    0xffffffffffffffff,
+    0xffffffffffffffff,
+  };
+
+  if (value_len == 0)
+    return 1;
+
+  /* must not start or end with SP or HTAB */
+  if ((value[0] == 0x20 || value[0] == 0x09 || value[value_len - 1] == 0x20 ||
+       value[value_len - 1] == 0x09))
+    return 0;
+
+  for (i = 0; i < value_len; i++)
+    {
+      if (!clib_bitmap_get_no_check (tchar, value[i]))
+       return 0;
+    }
+  return 1;
+}
+
+always_inline http_req_method_t
+hpack_parse_method (u8 *value, u32 value_len)
+{
+  switch (value_len)
+    {
+    case 3:
+      if (!memcmp (value, "GET", 3))
+       return HTTP_REQ_GET;
+      break;
+    case 4:
+      if (!memcmp (value, "POST", 4))
+       return HTTP_REQ_POST;
+      break;
+    case 7:
+      if (!memcmp (value, "CONNECT", 7))
+       return HTTP_REQ_CONNECT;
+      break;
+    default:
+      break;
+    }
+  /* HPACK should return only connection errors, this one is stream error */
+  return HTTP_REQ_UNKNOWN;
+}
+
+always_inline http_url_scheme_t
+hpack_parse_scheme (u8 *value, u32 value_len)
+{
+  switch (value_len)
+    {
+    case 4:
+      if (!memcmp (value, "http", 4))
+       return HTTP_URL_SCHEME_HTTP;
+      break;
+    case 5:
+      if (!memcmp (value, "https", 5))
+       return HTTP_URL_SCHEME_HTTPS;
+      break;
+    default:
+      break;
+    }
+  /* HPACK should return only connection errors, this one is stream error */
+  return HTTP_URL_SCHEME_UNKNOWN;
+}
+
+always_inline hpack_error_t
+hpack_parse_status_code (u8 *value, u32 value_len, http_status_code_t *sc)
+{
+  u16 status_code = 0;
+  u8 *p;
+
+  if (value_len != 3)
+    return HPACK_ERROR_PROTOCOL;
+
+  p = value;
+  parse_int (status_code, 100);
+  parse_int (status_code, 10);
+  parse_int (status_code, 1);
+  if (status_code < 100 || status_code > 599)
+    {
+      HTTP_DBG (1, "invalid status code %d", status_code);
+      return HPACK_ERROR_PROTOCOL;
+    }
+  HTTP_DBG (1, "status code: %d", status_code);
+  *sc = http_sc_by_u16 (status_code);
+
+  return HPACK_ERROR_NONE;
+}
+
+always_inline hpack_error_t
+hpack_parse_req_pseudo_header (u8 *name, u32 name_len, u8 *value,
+                              u32 value_len,
+                              hpack_request_control_data_t *control_data)
+{
+  HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len, format_http_bytes,
+           value, value_len);
+  switch (name_len)
+    {
+    case 5:
+      if (!memcmp (name + 1, "path", 4))
+       {
+         if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_PATH_PARSED ||
+             value_len == 0)
+           return HPACK_ERROR_PROTOCOL;
+         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_PATH_PARSED;
+         control_data->path = value;
+         control_data->path_len = value_len;
+         break;
+       }
+      return HPACK_ERROR_PROTOCOL;
+    case 7:
+      switch (name[1])
+       {
+       case 'm':
+         if (!memcmp (name + 2, "ethod", 5))
+           {
+             if (control_data->parsed_bitmap &
+                 HPACK_PSEUDO_HEADER_METHOD_PARSED)
+               return HPACK_ERROR_PROTOCOL;
+             control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_METHOD_PARSED;
+             control_data->method = hpack_parse_method (value, value_len);
+             break;
+           }
+         return HPACK_ERROR_PROTOCOL;
+       case 's':
+         if (!memcmp (name + 2, "cheme", 5))
+           {
+             if (control_data->parsed_bitmap &
+                 HPACK_PSEUDO_HEADER_SCHEME_PARSED)
+               return HPACK_ERROR_PROTOCOL;
+             control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_SCHEME_PARSED;
+             control_data->scheme = hpack_parse_scheme (value, value_len);
+             break;
+           }
+         return HPACK_ERROR_PROTOCOL;
+       default:
+         return HPACK_ERROR_PROTOCOL;
+       }
+      break;
+    case 9:
+      if (!memcmp (name + 1, "protocol", 8))
+       {
+         if (control_data->parsed_bitmap &
+             HPACK_PSEUDO_HEADER_PROTOCOL_PARSED)
+           return HPACK_ERROR_PROTOCOL;
+         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_PROTOCOL_PARSED;
+         control_data->protocol = value;
+         control_data->protocol_len = value_len;
+         break;
+       }
+      break;
+    case 10:
+      if (!memcmp (name + 1, "authority", 9))
+       {
+         if (control_data->parsed_bitmap &
+             HPACK_PSEUDO_HEADER_AUTHORITY_PARSED)
+           return HPACK_ERROR_PROTOCOL;
+         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
+         control_data->authority = value;
+         control_data->authority_len = value_len;
+         break;
+       }
+      return HPACK_ERROR_PROTOCOL;
+    default:
+      return HPACK_ERROR_PROTOCOL;
+    }
+
+  return HPACK_ERROR_NONE;
+}
+
+always_inline hpack_error_t
+hpack_parse_resp_pseudo_header (u8 *name, u32 name_len, u8 *value,
+                               u32 value_len,
+                               hpack_response_control_data_t *control_data)
+{
+  HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len, format_http_bytes,
+           value, value_len);
+  switch (name_len)
+    {
+    case 7:
+      if (!memcmp (name + 1, "status", 6))
+       {
+         if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_STATUS_PARSED)
+           return HPACK_ERROR_PROTOCOL;
+         control_data->parsed_bitmap |= HPACK_PSEUDO_HEADER_STATUS_PARSED;
+         return hpack_parse_status_code (value, value_len, &control_data->sc);
+       }
+      break;
+    default:
+      return HPACK_ERROR_PROTOCOL;
+    }
+  return HPACK_ERROR_NONE;
+}
+
+/* Special treatment for headers like:
+ *
+ * RFC9113 8.2.2: any message containing connection-specific header
+ * fields MUST be treated as malformed (connection, upgrade, keep-alive,
+ * proxy-connection, transfer-encoding), TE header MUST NOT contain any value
+ * other than "trailers"
+ *
+ * find headers that will be used later in preprocessing (content-length)
+ */
+always_inline hpack_error_t
+hpack_preprocess_header (u8 *name, u32 name_len, u8 *value, u32 value_len,
+                        uword index, uword *content_len_header_index)
+{
+  switch (name_len)
+    {
+    case 2:
+      if (name[0] == 't' && name[1] == 'e' &&
+         !http_token_is_case ((const char *) value, value_len,
+                              http_token_lit ("trailers")))
+       return HPACK_ERROR_PROTOCOL;
+      break;
+    case 7:
+      if (!memcmp (name, "upgrade", 7))
+       return HPACK_ERROR_PROTOCOL;
+      break;
+    case 10:
+      switch (name[0])
+       {
+       case 'c':
+         if (!memcmp (name + 1, "onnection", 9))
+           return HPACK_ERROR_PROTOCOL;
+         break;
+       case 'k':
+         if (!memcmp (name + 1, "eep-alive", 9))
+           return HPACK_ERROR_PROTOCOL;
+         break;
+       default:
+         break;
+       }
+      break;
+    case 14:
+      if (!memcmp (name, "content-length", 14) &&
+         *content_len_header_index == ~0)
+       *content_len_header_index = index;
+      break;
+    case 16:
+      if (!memcmp (name, "proxy-connection", 16))
+       return HPACK_ERROR_PROTOCOL;
+      break;
+    case 17:
+      if (!memcmp (name, "transfer-encoding", 17))
+       return HPACK_ERROR_PROTOCOL;
+      break;
+    default:
+      break;
+    }
+  return HPACK_ERROR_NONE;
+}
+
+always_inline hpack_error_t
+hpack_decode_request (u8 *src, u8 *end, u8 *dst, u32 dst_len,
+                     hpack_request_control_data_t *control_data,
+                     http_field_line_t **headers, void *decoder_ctx,
+                     hpack_header_decoder_fn *decoder_fn)
+{
+  u8 *p, *b, *name, *value;
+  u8 regular_header_parsed = 0;
+  u32 name_len, value_len;
+  uword b_left;
+  http_field_line_t *header;
+  hpack_error_t rv;
+
+  p = src;
+  b = dst;
+  b_left = dst_len;
+  control_data->parsed_bitmap = 0;
+  control_data->headers_len = 0;
+  control_data->content_len_header_index = ~0;
+
+  while (p != end)
+    {
+      name = b;
+      rv =
+       decoder_fn (&p, end, &b, &b_left, &name_len, &value_len, decoder_ctx);
+      if (rv)
+       {
+         HTTP_DBG (1, "decode_header: %d", rv);
+         return rv;
+       }
+      value = name + name_len;
+
+      /* pseudo header */
+      if (name[0] == ':')
+       {
+         /* all pseudo-headers must be before regular headers */
+         if (regular_header_parsed)
+           {
+             HTTP_DBG (1, "pseudo-headers after regular header");
+             return HPACK_ERROR_PROTOCOL;
+           }
+         rv = hpack_parse_req_pseudo_header (name, name_len, value, value_len,
+                                             control_data);
+         if (rv)
+           {
+             HTTP_DBG (1, "hpack_parse_req_pseudo_header: %d", rv);
+             return rv;
+           }
+         continue;
+       }
+      else
+       {
+         if (!hpack_header_name_is_valid (name, name_len))
+           return HPACK_ERROR_PROTOCOL;
+         if (!regular_header_parsed)
+           {
+             regular_header_parsed = 1;
+             control_data->headers = name;
+           }
+       }
+      if (!hpack_header_value_is_valid (value, value_len))
+       return HPACK_ERROR_PROTOCOL;
+      vec_add2 (*headers, header, 1);
+      HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len,
+               format_http_bytes, value, value_len);
+      header->name_offset = name - control_data->headers;
+      header->name_len = name_len;
+      header->value_offset = value - control_data->headers;
+      header->value_len = value_len;
+      control_data->headers_len += name_len;
+      control_data->headers_len += value_len;
+      if (regular_header_parsed)
+       {
+         rv = hpack_preprocess_header (
+           name, name_len, value, value_len, header - *headers,
+           &control_data->content_len_header_index);
+         if (rv)
+           {
+             HTTP_DBG (1, "connection-specific header present");
+             return rv;
+           }
+       }
+    }
+  control_data->control_data_len = dst_len - b_left;
+  return HPACK_ERROR_NONE;
+}
+
+always_inline hpack_error_t
+hpack_decode_response (u8 *src, u8 *end, u8 *dst, u32 dst_len,
+                      hpack_response_control_data_t *control_data,
+                      http_field_line_t **headers, void *decoder_ctx,
+                      hpack_header_decoder_fn *decoder_fn)
+{
+  u8 *p, *b, *name, *value;
+  u8 regular_header_parsed = 0;
+  u32 name_len, value_len;
+  uword b_left;
+  http_field_line_t *header;
+  hpack_error_t rv;
+
+  p = src;
+  b = dst;
+  b_left = dst_len;
+  control_data->parsed_bitmap = 0;
+  control_data->headers_len = 0;
+  control_data->content_len_header_index = ~0;
+
+  while (p != end)
+    {
+      name = b;
+      rv =
+       decoder_fn (&p, end, &b, &b_left, &name_len, &value_len, decoder_ctx);
+      if (rv)
+       {
+         HTTP_DBG (1, "decode_header: %d", rv);
+         return rv;
+       }
+      value = name + name_len;
+
+      /* pseudo header */
+      if (name[0] == ':')
+       {
+         /* all pseudo-headers must be before regular headers */
+         if (regular_header_parsed)
+           {
+             HTTP_DBG (1, "pseudo-headers after regular header");
+             return HPACK_ERROR_PROTOCOL;
+           }
+         rv = hpack_parse_resp_pseudo_header (name, name_len, value,
+                                              value_len, control_data);
+         if (rv)
+           {
+             HTTP_DBG (1, "hpack_parse_resp_pseudo_header: %d", rv);
+             return rv;
+           }
+         continue;
+       }
+      else
+       {
+         if (!hpack_header_name_is_valid (name, name_len))
+           return HPACK_ERROR_PROTOCOL;
+         if (!regular_header_parsed)
+           {
+             regular_header_parsed = 1;
+             control_data->headers = name;
+           }
+       }
+      if (!hpack_header_value_is_valid (value, value_len))
+       return HPACK_ERROR_PROTOCOL;
+      vec_add2 (*headers, header, 1);
+      HTTP_DBG (2, "%U: %U", format_http_bytes, name, name_len,
+               format_http_bytes, value, value_len);
+      header->name_offset = name - control_data->headers;
+      header->name_len = name_len;
+      header->value_offset = value - control_data->headers;
+      header->value_len = value_len;
+      control_data->headers_len += name_len;
+      control_data->headers_len += value_len;
+      if (regular_header_parsed)
+       {
+         rv = hpack_preprocess_header (
+           name, name_len, value, value_len, header - *headers,
+           &control_data->content_len_header_index);
+         if (rv)
+           {
+             HTTP_DBG (1, "connection-specific header present");
+             return rv;
+           }
+       }
+    }
+  control_data->control_data_len = dst_len - b_left;
+  return HPACK_ERROR_NONE;
+}
+
+#endif /* SRC_PLUGINS_HTTP_HPACK_INLINES_H_ */
index 6118c7b..ae7b442 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 #include <http/http3/qpack.h>
-#include <http/http2/hpack.h>
+#include <http/http2/hpack_inlines.h>
 
 typedef struct
 {
@@ -130,12 +130,12 @@ static qpack_static_table_entry_t qpack_static_table[] = {
 STATIC_ASSERT (QPACK_STATIC_TABLE_SIZE == 99,
               "static table must have 99 entries");
 
-static http3_error_t
+static hpack_error_t
 qpack_get_static_table_entry (uword index, http_token_t *name,
                              http_token_t *value, u8 value_is_indexed)
 {
   if (index >= QPACK_STATIC_TABLE_SIZE)
-    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+    return HPACK_ERROR_COMPRESSION;
 
   qpack_static_table_entry_t *e = &qpack_static_table[index];
   name->base = e->name;
@@ -143,25 +143,24 @@ qpack_get_static_table_entry (uword index, http_token_t *name,
   if (value_is_indexed)
     {
       if (e->value_len == 0)
-       return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+       return HPACK_ERROR_COMPRESSION;
       value->base = e->value;
       value->len = e->value_len;
     }
 
-  return HTTP3_ERROR_NO_ERROR;
+  return HPACK_ERROR_NONE;
 }
 
-static http3_error_t
+static hpack_error_t
 qpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len,
                     u8 prefix_len)
 {
   u8 *p, is_huffman;
   uword len;
-  int rv;
 
   ASSERT (prefix_len >= 2 && prefix_len <= 8);
   if (*src == end)
-    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+    return HPACK_ERROR_COMPRESSION;
 
   p = *src;
   /* first bit for H flag */
@@ -170,48 +169,46 @@ qpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len,
   /* length is integer with (N-1) bit prefix */
   len = hpack_decode_int (&p, end, prefix_len - 1);
   if (PREDICT_FALSE (len == HPACK_INVALID_INT))
-    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+    return HPACK_ERROR_COMPRESSION;
 
   /* do we have everything? */
   if (len > (end - p))
-    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+    return HPACK_ERROR_COMPRESSION;
 
   if (is_huffman)
     {
       *src = (p + len);
-      rv = hpack_decode_huffman (&p, p + len, buf, buf_len);
-      return rv ? HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED :
-                 HTTP3_ERROR_NO_ERROR;
+      return hpack_decode_huffman (&p, p + len, buf, buf_len);
     }
   else
     {
       /* enough space? */
       if (len > *buf_len)
-       return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+       return HPACK_ERROR_UNKNOWN;
 
       clib_memcpy (*buf, p, len);
       *buf_len -= len;
       *buf += len;
       *src = (p + len);
-      return HTTP3_ERROR_NO_ERROR;
+      return HPACK_ERROR_NONE;
     }
 }
 
-__clib_export http3_error_t
+__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)
 {
   u8 *p;
   uword index, old_len;
   http_token_t name, value;
-  http3_error_t rv;
+  hpack_error_t rv;
 
   ASSERT (*src < end);
   p = *src;
 
 #define COPY_TOKEN(_token)                                                    \
   if (_token.len > *buf_len)                                                  \
-    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;                            \
+    return HPACK_ERROR_COMPRESSION;                                           \
   clib_memcpy (*buf, _token.base, _token.len);                                \
   *buf_len -= _token.len;                                                     \
   *buf += _token.len;
@@ -222,9 +219,9 @@ qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       /* indexed field line, static table */
       index = hpack_decode_int (&p, end, 6);
       if (index == HPACK_INVALID_INT)
-       return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+       return HPACK_ERROR_COMPRESSION;
       rv = qpack_get_static_table_entry (index, &name, &value, 1);
-      if (rv != HTTP3_ERROR_NO_ERROR)
+      if (rv)
        return rv;
       COPY_TOKEN (name);
       *name_len = name.len;
@@ -233,53 +230,53 @@ qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
       break;
     case 8 ... 11:
       /* TODO: indexed field line, dynamic table */
-      return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+      return HPACK_ERROR_COMPRESSION;
     case 7:
     case 5:
       /* literal field line with name reference, static table */
       index = hpack_decode_int (&p, end, 4);
       if (index == HPACK_INVALID_INT)
-       return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+       return HPACK_ERROR_COMPRESSION;
       rv = qpack_get_static_table_entry (index, &name, &value, 0);
-      if (rv != HTTP3_ERROR_NO_ERROR)
+      if (rv)
        return rv;
       COPY_TOKEN (name);
       *name_len = name.len;
       old_len = *buf_len;
       rv = qpack_decode_string (&p, end, buf, buf_len, 8);
-      if (rv != HTTP3_ERROR_NO_ERROR)
+      if (rv)
        return rv;
       *value_len = old_len - *buf_len;
       break;
     case 6:
     case 4:
       /* TODO: literal field line with name reference, dynamic table */
-      return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+      return HPACK_ERROR_COMPRESSION;
     case 3:
     case 2:
       /* literal field line with literal name */
       old_len = *buf_len;
       rv = qpack_decode_string (&p, end, buf, buf_len, 4);
-      if (rv != HTTP3_ERROR_NO_ERROR)
+      if (rv)
        return rv;
       *name_len = old_len - *buf_len;
       old_len = *buf_len;
       rv = qpack_decode_string (&p, end, buf, buf_len, 8);
-      if (rv != HTTP3_ERROR_NO_ERROR)
+      if (rv)
        return rv;
       *value_len = old_len - *buf_len;
       break;
     case 1:
       /* TODO: indexed field line with post-base index */
-      return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+      return HPACK_ERROR_COMPRESSION;
     case 0:
       /* TODO: literal field line with post-base name reference */
-      return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+      return HPACK_ERROR_COMPRESSION;
     default:
       ASSERT (0);
       break;
     }
 
   *src = p;
-  return HTTP3_ERROR_NO_ERROR;
+  return HPACK_ERROR_NONE;
 }
index a4ecc7a..406662d 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_inlines.h>
 #include <http/http2/hpack.h>
 #include <http/http2/frame.h>
 #include <http/http3/qpack.h>
@@ -846,10 +847,6 @@ 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)                                                        \
@@ -857,7 +854,7 @@ http_test_hpack (vlib_main_t *vm)
   memcpy (input, i, sizeof (i) - 1);                                          \
   pos = input;                                                                \
   end = vec_end (input);                                                      \
-  value = _hpack_decode_int (&pos, end, (u8) pl);                             \
+  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);                                \
@@ -880,7 +877,7 @@ http_test_hpack (vlib_main_t *vm)
   memcpy (input, i, sizeof (i) - 1);                                          \
   pos = input;                                                                \
   end = vec_end (input);                                                      \
-  value = _hpack_decode_int (&pos, end, (u8) pl);                             \
+  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);                                \
@@ -897,16 +894,12 @@ http_test_hpack (vlib_main_t *vm)
 
   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);                                    \
+  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);                                 \
@@ -924,14 +917,14 @@ http_test_hpack (vlib_main_t *vm)
 
   vlib_cli_output (vm, "hpack_decode_string");
 
-  static http2_error_t (*_hpack_decode_string) (u8 * *src, u8 * end, u8 * *buf,
+  static hpack_error_t (*_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;
-  http2_error_t rv;
+  hpack_error_t rv;
 
 #define TEST(i, e)                                                            \
   vec_validate (input, sizeof (i) - 2);                                       \
@@ -944,7 +937,7 @@ http_test_hpack (vlib_main_t *vm)
   len = vec_len (buf) - blen;                                                 \
   HTTP_TEST ((len == strlen (e) && !memcmp (buf, e, len) &&                   \
              pos == vec_end (input) && bp == buf + len &&                    \
-             rv == HTTP2_ERROR_NO_ERROR),                                    \
+             rv == HPACK_ERROR_NONE),                                        \
             "%U is decoded as %U", format_hex_bytes, input, vec_len (input), \
             format_http_bytes, buf, len);                                    \
   vec_free (input);                                                           \
@@ -989,20 +982,20 @@ http_test_hpack (vlib_main_t *vm)
   vec_free (buf);
 
   /* incomplete */
-  N_TEST ("\x87", HTTP2_ERROR_COMPRESSION_ERROR);
-  N_TEST ("\x07priv", HTTP2_ERROR_COMPRESSION_ERROR);
+  N_TEST ("\x87", HPACK_ERROR_COMPRESSION);
+  N_TEST ("\x07priv", HPACK_ERROR_COMPRESSION);
   /* invalid length */
-  N_TEST ("\x7Fprivate", HTTP2_ERROR_COMPRESSION_ERROR);
+  N_TEST ("\x7Fprivate", HPACK_ERROR_COMPRESSION);
   /* invalid EOF */
-  N_TEST ("\x81\x8C", HTTP2_ERROR_COMPRESSION_ERROR);
+  N_TEST ("\x81\x8C", HPACK_ERROR_COMPRESSION);
   N_TEST ("\x98\xDC\x53\xFF\xFF\xFF\xDF\xFF\xFF\xFF\x14\xFF\xFF\xFF\xF7\xFF"
          "\xFF\xFF\xC5\x3F\xFF\xFF\xFD\xFF\xFF",
-         HTTP2_ERROR_COMPRESSION_ERROR);
+         HPACK_ERROR_COMPRESSION);
   /* 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",
-    HTTP2_ERROR_INTERNAL_ERROR);
+    HPACK_ERROR_UNKNOWN);
 #undef N_TEST
 
   vlib_cli_output (vm, "hpack_encode_string");
@@ -1043,7 +1036,7 @@ http_test_hpack (vlib_main_t *vm)
 
   vlib_cli_output (vm, "hpack_decode_header");
 
-  static http2_error_t (*_hpack_decode_header) (
+  static hpack_error_t (*_hpack_decode_header) (
     u8 * *src, u8 * end, u8 * *buf, uword * buf_len, u32 * name_len,
     u32 * value_len, hpack_dynamic_table_t * dt);
 
@@ -1076,7 +1069,7 @@ http_test_hpack (vlib_main_t *vm)
   rv = _hpack_decode_header (&pos, vec_end (input), &bp, &blen, &name_len,    \
                             &value_len, &table);                             \
   len = vec_len (buf) - blen;                                                 \
-  HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && table.used == dt_size &&          \
+  HTTP_TEST ((rv == HPACK_ERROR_NONE && table.used == dt_size &&              \
              name_len == strlen (e_name) && value_len == strlen (e_value) && \
              !memcmp (buf, e_name, name_len) &&                              \
              !memcmp (buf + name_len, e_value, value_len) &&                 \
@@ -1623,7 +1616,7 @@ http_test_qpack (vlib_main_t *vm)
 {
   vlib_cli_output (vm, "qpack_decode_header");
 
-  static http3_error_t (*_qpack_decode_header) (
+  static hpack_error_t (*_qpack_decode_header) (
     u8 * *src, u8 * end, u8 * *buf, uword * buf_len, u32 * name_len,
     u32 * value_len);
 
@@ -1633,7 +1626,7 @@ http_test_qpack (vlib_main_t *vm)
   u8 *pos, *bp, *buf = 0, *input = 0;
   uword blen;
   u32 name_len, value_len;
-  http3_error_t rv;
+  hpack_error_t rv;
 
 #define TEST(i, e_name, e_value)                                              \
   vec_validate (input, sizeof (i) - 2);                                       \
@@ -1644,7 +1637,7 @@ http_test_qpack (vlib_main_t *vm)
   blen = vec_len (buf);                                                       \
   rv = _qpack_decode_header (&pos, vec_end (input), &bp, &blen, &name_len,    \
                             &value_len);                                     \
-  HTTP_TEST ((rv == HTTP3_ERROR_NO_ERROR && name_len == strlen (e_name) &&    \
+  HTTP_TEST ((rv == HPACK_ERROR_NONE && name_len == strlen (e_name) &&        \
              value_len == strlen (e_value) &&                                \
              !memcmp (buf, e_name, name_len) &&                              \
              !memcmp (buf + name_len, e_value, value_len) &&                 \