http: QPACK encoding request 16/43816/2
authorMatus Fabian <[email protected]>
Tue, 30 Sep 2025 10:02:20 +0000 (06:02 -0400)
committerFlorin Coras <[email protected]>
Tue, 30 Sep 2025 19:23:27 +0000 (19:23 +0000)
Type: feature

Change-Id: Ic823d9db4f594b66fff14aefd5658350d5621fda
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 2fd5347..bb14a6b 100644 (file)
@@ -1196,6 +1196,92 @@ qpack_encode_content_len (u8 *dst, u64 content_len)
   return dst;
 }
 
+static u8 *
+qpack_encode_method (u8 *dst, http_req_method_t method)
+{
+  u8 *a;
+
+  switch (method)
+    {
+    case HTTP_REQ_CONNECT:
+      encode_static_entry (15);
+      break;
+    case HTTP_REQ_GET:
+      encode_static_entry (17);
+      break;
+    case HTTP_REQ_POST:
+      encode_static_entry (20);
+      break;
+    case HTTP_REQ_PUT:
+      encode_static_entry (21);
+    default:
+      ASSERT (0);
+      break;
+    }
+  return dst;
+}
+
+static u8 *
+qpack_encode_scheme (u8 *dst, http_url_scheme_t scheme)
+{
+  u8 *a;
+
+  switch (scheme)
+    {
+    case HTTP_URL_SCHEME_HTTP:
+      encode_static_entry (22);
+      break;
+    case HTTP_URL_SCHEME_HTTPS:
+      encode_static_entry (23);
+      break;
+    default:
+      ASSERT (0);
+      break;
+    }
+  return dst;
+}
+
+static u8 *
+qpack_encode_path (u8 *dst, u8 *path, u32 path_len)
+{
+  u8 *a, *b;
+  u32 orig_len, actual_size;
+
+  if (path_len == 1 && path[0] == '/')
+    {
+      encode_static_entry (1);
+    }
+  else
+    {
+      orig_len = vec_len (dst);
+      vec_add2 (dst, a, path_len + 1 + HPACK_ENCODED_INT_MAX_LEN);
+      b = a;
+      *b++ = 0x81;
+      b = qpack_encode_string (b, path, path_len, 8);
+      actual_size = b - a;
+      vec_set_len (dst, orig_len + actual_size);
+    }
+
+  return dst;
+}
+
+static u8 *
+qpack_encode_authority (u8 *dst, u8 *authority, u32 authority_len)
+{
+  u8 *a, *b;
+  u32 orig_len, actual_size;
+
+  orig_len = vec_len (dst);
+  vec_add2 (dst, a, authority_len + 1 + HPACK_ENCODED_INT_MAX_LEN);
+  b = a;
+  *b++ = 0x80;
+  b = qpack_encode_string (b, authority, authority_len, 8);
+  actual_size = b - a;
+  vec_set_len (dst, orig_len + actual_size);
+
+  return dst;
+}
+
 static inline hpack_error_t
 qpack_parse_headers_prefix (u8 **src, u8 *end, qpack_decoder_ctx_t *ctx)
 {
@@ -1330,3 +1416,78 @@ qpack_serialize_response (u8 *app_headers, u32 app_headers_len,
 
   *dst = p;
 }
+
+__clib_export void
+qpack_serialize_request (u8 *app_headers, u32 app_headers_len,
+                        hpack_request_control_data_t *control_data, u8 **dst)
+{
+  u8 *a, *p, *end;
+
+  p = *dst;
+  /* encoded field section prefix, two zero bytes because we don't use dynamic
+   * table */
+  vec_add2 (p, a, 2);
+  a[0] = 0;
+  a[1] = 0;
+
+  /* pseudo-headers must go first */
+  p = qpack_encode_method (p, control_data->method);
+
+  if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_SCHEME_PARSED)
+    p = qpack_encode_scheme (p, control_data->scheme);
+
+  if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_PATH_PARSED)
+    p = qpack_encode_path (p, control_data->path, control_data->path_len);
+
+  if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_PROTOCOL_PARSED)
+    p = qpack_encode_custom_header (p, (u8 *) ":protocol", 9,
+                                   control_data->protocol,
+                                   control_data->protocol_len);
+
+  p = qpack_encode_authority (p, control_data->authority,
+                             control_data->authority_len);
+
+  /* user agent */
+  if (control_data->user_agent_len)
+    p =
+      qpack_encode_header (p, HTTP_HEADER_USER_AGENT, control_data->user_agent,
+                          control_data->user_agent_len);
+
+  /* content length if any */
+  if (control_data->content_len != HPACK_ENCODER_SKIP_CONTENT_LEN)
+    p = qpack_encode_content_len (p, control_data->content_len);
+
+  if (!app_headers_len)
+    {
+      *dst = p;
+      return;
+    }
+
+  end = app_headers + app_headers_len;
+  while (app_headers < end)
+    {
+      /* custom header name? */
+      u32 *tmp = (u32 *) app_headers;
+      if (PREDICT_FALSE (*tmp & HTTP_CUSTOM_HEADER_NAME_BIT))
+       {
+         http_custom_token_t *name, *value;
+         name = (http_custom_token_t *) app_headers;
+         u32 name_len = name->len & ~HTTP_CUSTOM_HEADER_NAME_BIT;
+         app_headers += sizeof (http_custom_token_t) + name_len;
+         value = (http_custom_token_t *) app_headers;
+         app_headers += sizeof (http_custom_token_t) + value->len;
+         p = qpack_encode_custom_header (p, name->token, name_len,
+                                         value->token, value->len);
+       }
+      else
+       {
+         http_app_header_t *header;
+         header = (http_app_header_t *) app_headers;
+         app_headers += sizeof (http_app_header_t) + header->value.len;
+         p = qpack_encode_header (p, header->name, header->value.token,
+                                  header->value.len);
+       }
+    }
+
+  *dst = p;
+}
index da36e46..d0abc32 100644 (file)
@@ -65,4 +65,16 @@ void qpack_serialize_response (u8 *app_headers, u32 app_headers_len,
                               hpack_response_control_data_t *control_data,
                               u8 **dst);
 
+/**
+ * Serialize request
+ *
+ * @param app_headers     App header list
+ * @param app_headers_len App header list length
+ * @param control_data    Header values set by protocol layer
+ * @param dst             Vector where serialized headers will be added
+ */
+void qpack_serialize_request (u8 *app_headers, u32 app_headers_len,
+                             hpack_request_control_data_t *control_data,
+                             u8 **dst);
+
 #endif /* SRC_PLUGINS_HTTP_QPACK_H_ */
index 6ce3a15..3d61b0b 100644 (file)
@@ -1829,9 +1829,69 @@ http_test_qpack (vlib_main_t *vm)
             "response encoded as: %U", format_hex_bytes, buf, vec_len (buf));
   vec_free (headers_buf);
   vec_free (buf);
-  vec_free (server_name);
   vec_free (date);
 
+  vlib_cli_output (vm, "qpack_serialize_request");
+
+  static void (*_qpack_serialize_request) (
+    u8 * app_headers, u32 app_headers_len,
+    hpack_request_control_data_t * control_data, u8 * *dst);
+  _qpack_serialize_request =
+    vlib_get_plugin_symbol ("http_plugin.so", "qpack_serialize_request");
+
+  u8 *authority = format (0, "www.example.com");
+  u8 *path = format (0, "/");
+  memset (&req_control_data, 0, sizeof (req_control_data));
+  vec_validate_init_empty (headers_buf, 127, 0xFF);
+  req_control_data.method = HTTP_REQ_GET;
+  req_control_data.parsed_bitmap = HPACK_PSEUDO_HEADER_SCHEME_PARSED;
+  req_control_data.scheme = HTTP_URL_SCHEME_HTTPS;
+  req_control_data.parsed_bitmap |= HPACK_PSEUDO_HEADER_PATH_PARSED;
+  req_control_data.path = path;
+  req_control_data.path_len = vec_len (path);
+  req_control_data.parsed_bitmap |= HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
+  req_control_data.authority = authority;
+  req_control_data.authority_len = vec_len (authority);
+  req_control_data.user_agent_len = 0;
+  req_control_data.content_len = HPACK_ENCODER_SKIP_CONTENT_LEN;
+  u8 expected6[] = "\x00\x00\xD1\xD7\xC1\x80\x8C\xF1\xE3\xC2\xE5\xF2\x3A\x6B"
+                  "\xA0\xAB\x90\xF4\xFF";
+  _qpack_serialize_request (0, 0, &req_control_data, &buf);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected6) - 1) &&
+             !memcmp (buf, expected6, vec_len (buf))),
+            "request encoded as: %U", format_hex_bytes, buf, vec_len (buf));
+  vec_free (buf);
+  vec_free (authority);
+  vec_free (path);
+
+  authority = format (0, "example.org:123");
+  memset (&req_control_data, 0, sizeof (req_control_data));
+  vec_validate_init_empty (headers_buf, 127, 0xFF);
+  req_control_data.method = HTTP_REQ_CONNECT;
+  req_control_data.parsed_bitmap |= HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
+  req_control_data.authority = authority;
+  req_control_data.authority_len = vec_len (authority);
+  req_control_data.user_agent = server_name;
+  req_control_data.user_agent_len = vec_len (server_name);
+  req_control_data.content_len = HPACK_ENCODER_SKIP_CONTENT_LEN;
+  vec_validate (headers_buf, 127);
+  http_init_headers_ctx (&headers_ctx, headers_buf, vec_len (headers_buf));
+  http_add_custom_header (&headers_ctx, http_token_lit ("sandwich"),
+                         http_token_lit ("spam"));
+  u8 expected7[] =
+    "\x00\x00\xCF\x80\x8B\x2F\x91\xD3\x5D\x05\x5C\xF6\x4D\x70\x22\x67\x5F\x50"
+    "\x8B\x9D\x29\xAD\x4B\x6A\x32\x54\x49\x50\x94\x7f\x2E\x40\xEA\x93\xC1\x89"
+    "\x3F\x83\x45\x63\xA7";
+  _qpack_serialize_request (headers_buf, headers_ctx.tail_offset,
+                           &req_control_data, &buf);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected7) - 1) &&
+             !memcmp (buf, expected7, vec_len (buf))),
+            "request encoded as: %U", format_hex_bytes, buf, vec_len (buf));
+
+  vec_free (server_name);
+  vec_free (buf);
+  vec_free (authority);
+
   return 0;
 }