http: QPACK decoding request and response 53/43753/3
authorMatus Fabian <[email protected]>
Thu, 25 Sep 2025 15:14:30 +0000 (11:14 -0400)
committerFlorin Coras <[email protected]>
Mon, 29 Sep 2025 16:24:59 +0000 (16:24 +0000)
Type: feature

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

index ae7b442..e0c9377 100644 (file)
@@ -280,3 +280,81 @@ qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len,
   *src = p;
   return HPACK_ERROR_NONE;
 }
+
+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,
+  [HPACK_ERROR_PROTOCOL] = HTTP3_ERROR_GENERAL_PROTOCOL_ERROR,
+  [HPACK_ERROR_UNKNOWN] = HTTP3_ERROR_INTERNAL_ERROR,
+};
+
+static inline hpack_error_t
+qpack_parse_headers_prefix (u8 **src, u8 *end, qpack_decoder_ctx_t *ctx)
+{
+  u8 *p;
+
+  ASSERT (*src < end);
+  p = *src;
+
+  ctx->req_insert_count = hpack_decode_int (&p, end, 8);
+  if (ctx->req_insert_count == HPACK_INVALID_INT || p == end)
+    return HPACK_ERROR_COMPRESSION;
+
+  ctx->delta_base_sign = *p & 0x80;
+  ctx->delta_base = hpack_decode_int (&p, end, 7);
+  if (ctx->req_insert_count == HPACK_INVALID_INT)
+    return HPACK_ERROR_COMPRESSION;
+
+  *src = p;
+  return HPACK_ERROR_NONE;
+}
+
+__clib_export http3_error_t
+qpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
+                    hpack_request_control_data_t *control_data,
+                    http_field_line_t **headers,
+                    qpack_decoder_ctx_t *decoder_ctx)
+{
+  hpack_error_t rv;
+  u8 *p, *end;
+
+  if (src_len < 3)
+    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+
+  p = src;
+  end = src + src_len;
+
+  /* encoded field section prefix */
+  rv = qpack_parse_headers_prefix (&p, end, decoder_ctx);
+  if (rv || p == end)
+    return hpack_error_to_http3_error[rv];
+
+  rv = hpack_decode_request (p, end, dst, dst_len, control_data, headers,
+                            decoder_ctx, (void *) qpack_decode_header);
+  return hpack_error_to_http3_error[rv];
+}
+
+__clib_export http3_error_t
+qpack_parse_response (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
+                     hpack_response_control_data_t *control_data,
+                     http_field_line_t **headers,
+                     qpack_decoder_ctx_t *decoder_ctx)
+{
+  hpack_error_t rv;
+  u8 *p, *end;
+
+  if (src_len < 3)
+    return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED;
+
+  p = src;
+  end = src + src_len;
+
+  /* encoded field section prefix */
+  rv = qpack_parse_headers_prefix (&p, end, decoder_ctx);
+  if (rv || p == end)
+    return hpack_error_to_http3_error[rv];
+
+  rv = hpack_decode_response (p, end, dst, dst_len, control_data, headers,
+                             decoder_ctx, qpack_decode_header);
+  return hpack_error_to_http3_error[rv];
+}
index c24e933..32c1cea 100644 (file)
@@ -5,7 +5,52 @@
 #ifndef SRC_PLUGINS_HTTP_QPACK_H_
 #define SRC_PLUGINS_HTTP_QPACK_H_
 
-#include <http/http3/http3.h>
 #include <http/http.h>
+#include <http/http2/hpack.h>
+#include <http/http3/http3.h>
+
+typedef struct
+{
+  uword req_insert_count;
+  uword delta_base;
+  u8 delta_base_sign;
+} qpack_decoder_ctx_t;
+
+/**
+ * Request parser
+ *
+ * @param src          Header block to parse
+ * @param src_len      Length of header block
+ * @param dst          Buffer where headers will be decoded
+ * @param dst_len      Length of buffer for decoded headers
+ * @param control_data Preparsed pseudo-headers
+ * @param headers      List of regular headers
+ * @param decoder_ctx  Decoder context
+ *
+ * @return @c HTTP3_ERROR_NO_ERROR on success
+ */
+http3_error_t qpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
+                                  hpack_request_control_data_t *control_data,
+                                  http_field_line_t **headers,
+                                  qpack_decoder_ctx_t *decoder_ctx);
+
+/**
+ * Response parser
+ *
+ * @param src          Header block to parse
+ * @param src_len      Length of header block
+ * @param dst          Buffer where headers will be decoded
+ * @param dst_len      Length of buffer for decoded headers
+ * @param control_data Preparsed pseudo-headers
+ * @param headers      List of regular headers
+ * @param decoder_ctx  Decoder context
+ *
+ * @return @c HTTP3_ERROR_NO_ERROR on success
+ */
+http3_error_t
+qpack_parse_response (u8 *src, u32 src_len, u8 *dst, u32 dst_len,
+                     hpack_response_control_data_t *control_data,
+                     http_field_line_t **headers,
+                     qpack_decoder_ctx_t *decoder_ctx);
 
 #endif /* SRC_PLUGINS_HTTP_QPACK_H_ */
index 406662d..11f5823 100644 (file)
@@ -1657,6 +1657,71 @@ http_test_qpack (vlib_main_t *vm)
   /* literal field line with literal name */
   TEST ("\x23\x61\x62\x63\x01\x5A", "abc", "Z");
 
+  vlib_cli_output (vm, "qpack_parse_request");
+
+  static http3_error_t (*_qpack_parse_request) (
+    u8 * src, u32 src_len, u8 * dst, u32 dst_len,
+    hpack_request_control_data_t * control_data, http_field_line_t * *headers,
+    qpack_decoder_ctx_t * decoder_ctx);
+
+  _qpack_parse_request =
+    vlib_get_plugin_symbol ("http_plugin.so", "qpack_parse_request");
+
+  http3_error_t err;
+  http_field_line_t *headers = 0;
+  qpack_decoder_ctx_t decoder_ctx = {};
+  hpack_request_control_data_t req_control_data = {};
+  u8 req[] = { 0x00, 0x00, 0xD1, 0xD7, 0x50, 0x01, 0x61,
+              0xC1, 0x23, 0x61, 0x62, 0x63, 0x01, 0x5A };
+  u16 parsed_bitmap =
+    HPACK_PSEUDO_HEADER_METHOD_PARSED | HPACK_PSEUDO_HEADER_SCHEME_PARSED |
+    HPACK_PSEUDO_HEADER_PATH_PARSED | HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
+
+  vec_validate_init_empty (buf, 254, 0);
+  err = _qpack_parse_request (req, 14, buf, vec_len (buf), &req_control_data,
+                             &headers, &decoder_ctx);
+  HTTP_TEST (
+    (err == HTTP3_ERROR_NO_ERROR &&
+     req_control_data.parsed_bitmap == parsed_bitmap &&
+     req_control_data.method == HTTP_REQ_GET &&
+     req_control_data.scheme == HTTP_URL_SCHEME_HTTPS &&
+     req_control_data.path_len == 1 && req_control_data.path[0] == '/' &&
+     req_control_data.authority_len == 1 &&
+     req_control_data.authority[0] == 'a' && vec_len (headers) == 1 &&
+     req_control_data.headers_len == 4 && headers[0].name_len == 3 &&
+     headers[0].value_len == 1 &&
+     !memcmp (req_control_data.headers + headers[0].name_offset, "abc", 3) &&
+     !memcmp (req_control_data.headers + headers[0].value_offset, "Z", 1)),
+    "decoder result: %U", format_http3_error, err);
+
+  vec_free (buf);
+  vec_free (headers);
+  memset (&decoder_ctx, 0, sizeof (decoder_ctx));
+
+  vlib_cli_output (vm, "qpack_parse_response");
+
+  static http3_error_t (*_qpack_parse_response) (
+    u8 * src, u32 src_len, u8 * dst, u32 dst_len,
+    hpack_response_control_data_t * control_data, http_field_line_t * *headers,
+    qpack_decoder_ctx_t * decoder_ctx);
+
+  _qpack_parse_response =
+    vlib_get_plugin_symbol ("http_plugin.so", "qpack_parse_response");
+
+  hpack_response_control_data_t resp_control_data = {};
+  u8 resp[] = { 0x00, 0x00, 0xD9 };
+
+  vec_validate_init_empty (buf, 63, 0);
+  err = _qpack_parse_response (resp, 3, buf, vec_len (buf), &resp_control_data,
+                              &headers, &decoder_ctx);
+  HTTP_TEST (
+    (err == HTTP3_ERROR_NO_ERROR &&
+     resp_control_data.parsed_bitmap == HPACK_PSEUDO_HEADER_STATUS_PARSED &&
+     resp_control_data.sc == HTTP_STATUS_OK && vec_len (headers) == 0),
+    "decoder result: %U", format_http3_error, err);
+  vec_free (buf);
+  vec_free (headers);
+
   return 0;
 }