http: h2 encoding request 32/43232/3
authorMatus Fabian <[email protected]>
Fri, 20 Jun 2025 17:03:59 +0000 (17:03 +0000)
committerFlorin Coras <[email protected]>
Mon, 23 Jun 2025 18:45:15 +0000 (18:45 +0000)
Type: improvement

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

index bd9e40e..0e028a7 100644 (file)
@@ -51,13 +51,18 @@ typedef struct
 
 #define http_token_lit(s) (s), sizeof (s) - 1
 
+#define foreach_http_method                                                   \
+  _ (GET, "GET")                                                              \
+  _ (POST, "POST")                                                            \
+  _ (PUT, "PUT")                                                              \
+  _ (CONNECT, "CONNECT")                                                      \
+  _ (UNKNOWN, "UNKNOWN") /* for internal use */
+
 typedef enum http_req_method_
 {
-  HTTP_REQ_GET = 0,
-  HTTP_REQ_POST,
-  HTTP_REQ_PUT,
-  HTTP_REQ_CONNECT,
-  HTTP_REQ_UNKNOWN, /* for internal use */
+#define _(s, str) HTTP_REQ_##s,
+  foreach_http_method
+#undef _
 } http_req_method_t;
 
 typedef enum http_msg_type_
index 324602c..15e92fe 100644 (file)
@@ -100,6 +100,14 @@ static hpack_token_t hpack_headers[] = {
 #undef _
 };
 
+static http_token_t http_methods[] = {
+#define _(s, str) { http_token_lit (str) },
+  foreach_http_method
+#undef _
+};
+
+#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)
 {
@@ -1050,38 +1058,38 @@ hpack_encode_custom_header (u8 *dst, const u8 *name, u32 name_len,
   return dst;
 }
 
+#define encode_indexed_static_entry(_index)                                   \
+  vec_add2 (dst, a, 1);                                                       \
+  *a++ = 0x80 | _index;
+
 static inline u8 *
 hpack_encode_status_code (u8 *dst, http_status_code_t sc)
 {
   u32 orig_len, actual_size;
   u8 *a, *b;
 
-#define encode_common_sc(_index)                                              \
-  vec_add2 (dst, a, 1);                                                       \
-  *a++ = 0x80 | _index;
-
   switch (sc)
     {
     case HTTP_STATUS_OK:
-      encode_common_sc (8);
+      encode_indexed_static_entry (8);
       break;
     case HTTP_STATUS_NO_CONTENT:
-      encode_common_sc (9);
+      encode_indexed_static_entry (9);
       break;
     case HTTP_STATUS_PARTIAL_CONTENT:
-      encode_common_sc (10);
+      encode_indexed_static_entry (10);
       break;
     case HTTP_STATUS_NOT_MODIFIED:
-      encode_common_sc (11);
+      encode_indexed_static_entry (11);
       break;
     case HTTP_STATUS_BAD_REQUEST:
-      encode_common_sc (12);
+      encode_indexed_static_entry (12);
       break;
     case HTTP_STATUS_NOT_FOUND:
-      encode_common_sc (13);
+      encode_indexed_static_entry (13);
       break;
     case HTTP_STATUS_INTERNAL_ERROR:
-      encode_common_sc (14);
+      encode_indexed_static_entry (14);
       break;
     default:
       orig_len = vec_len (dst);
@@ -1097,6 +1105,110 @@ hpack_encode_status_code (u8 *dst, http_status_code_t sc)
   return dst;
 }
 
+static inline u8 *
+hpack_encode_method (u8 *dst, http_req_method_t method)
+{
+  u32 orig_len, actual_size;
+  u8 *a, *b;
+
+  switch (method)
+    {
+    case HTTP_REQ_GET:
+      encode_indexed_static_entry (2);
+      break;
+    case HTTP_REQ_POST:
+      encode_indexed_static_entry (3);
+      break;
+    default:
+      orig_len = vec_len (dst);
+      vec_add2 (dst, a, 9);
+      b = a;
+      /* Literal Header Field without Indexing — Indexed Name */
+      *b++ = 2;
+      b = hpack_encode_string (b, (const u8 *) http_method_token (method));
+      actual_size = b - a;
+      vec_set_len (dst, orig_len + actual_size);
+      break;
+    }
+  return dst;
+}
+
+static inline u8 *
+hpack_encode_scheme (u8 *dst, http_url_scheme_t scheme)
+{
+  u8 *a;
+
+  switch (scheme)
+    {
+    case HTTP_URL_SCHEME_HTTP:
+      encode_indexed_static_entry (6);
+      break;
+    case HTTP_URL_SCHEME_HTTPS:
+      encode_indexed_static_entry (7);
+      break;
+    default:
+      ASSERT (0);
+      break;
+    }
+  return dst;
+}
+
+static inline u8 *
+hpack_encode_path (u8 *dst, u8 *path, u32 path_len)
+{
+  u32 orig_len, actual_size;
+  u8 *a, *b;
+
+  switch (path_len)
+    {
+    case 1:
+      if (path[0] == '/')
+       {
+         encode_indexed_static_entry (4);
+         return dst;
+       }
+      break;
+    case 11:
+      if (!memcmp (path, "/index.html", 11))
+       {
+         encode_indexed_static_entry (5);
+         return dst;
+       }
+      break;
+    default:
+      break;
+    }
+
+  orig_len = vec_len (dst);
+  vec_add2 (dst, a, path_len + 2);
+  b = a;
+  /* Literal Header Field without Indexing — Indexed Name */
+  *b++ = 4;
+  b = hpack_encode_string (b, path, path_len);
+  actual_size = b - a;
+  vec_set_len (dst, orig_len + actual_size);
+
+  return dst;
+}
+
+static inline u8 *
+hpack_encode_authority (u8 *dst, u8 *authority, u32 authority_len)
+{
+  u32 orig_len, actual_size;
+  u8 *a, *b;
+
+  orig_len = vec_len (dst);
+  vec_add2 (dst, a, authority_len + 2);
+  b = a;
+  /* Literal Header Field without Indexing — Indexed Name */
+  *b++ = 1;
+  b = hpack_encode_string (b, authority, authority_len);
+  actual_size = b - a;
+  vec_set_len (dst, orig_len + actual_size);
+
+  return dst;
+}
+
 static inline u8 *
 hpack_encode_content_len (u8 *dst, u64 content_len)
 {
@@ -1183,3 +1295,62 @@ hpack_serialize_response (u8 *app_headers, u32 app_headers_len,
 
   *dst = p;
 }
+
+__clib_export void
+hpack_serialize_request (u8 *app_headers, u32 app_headers_len,
+                        hpack_request_control_data_t *control_data, u8 **dst)
+{
+  u8 *p, *end;
+
+  p = *dst;
+
+  /* pseudo-headers must go first */
+  p = hpack_encode_method (p, control_data->method);
+
+  if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_SCHEME_PARSED)
+    p = hpack_encode_scheme (p, control_data->scheme);
+
+  if (control_data->parsed_bitmap & HPACK_PSEUDO_HEADER_PATH_PARSED)
+    p = hpack_encode_path (p, control_data->path, control_data->path_len);
+
+  p = hpack_encode_authority (p, control_data->authority,
+                             control_data->authority_len);
+
+  /* user agent */
+  if (control_data->user_agent_len)
+    p =
+      hpack_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 = hpack_encode_content_len (p, control_data->content_len);
+
+  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 = hpack_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 = hpack_encode_header (p, header->name, header->value.token,
+                                  header->value.len);
+       }
+    }
+
+  *dst = p;
+}
index fef2a96..a2fd5fa 100644 (file)
@@ -56,10 +56,13 @@ typedef struct
   u8 *headers;
   u8 *protocol;
   u32 protocol_len;
+  u8 *user_agent;
+  u32 user_agent_len;
   uword content_len_header_index;
   u32 headers_len;
   u32 control_data_len;
   u16 parsed_bitmap;
+  u64 content_len;
 } hpack_request_control_data_t;
 
 typedef struct
@@ -182,4 +185,16 @@ void hpack_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 hpack_serialize_request (u8 *app_headers, u32 app_headers_len,
+                             hpack_request_control_data_t *control_data,
+                             u8 **dst);
+
 #endif /* SRC_PLUGINS_HTTP_HPACK_H_ */
index f44d3cb..73a397a 100644 (file)
@@ -1013,11 +1013,77 @@ http_test_hpack (vlib_main_t *vm)
   HTTP_TEST ((vec_len (buf) == (sizeof (expected2) - 1) &&
              !memcmp (buf, expected2, sizeof (expected2) - 1)),
             "response encoded as %U", format_hex_bytes, buf, vec_len (buf));
-  vec_free (buf);
+  vec_reset_length (buf);
   vec_free (headers_buf);
-  vec_free (server_name);
   vec_free (date);
 
+  vlib_cli_output (vm, "hpack_serialize_request");
+
+  hpack_request_control_data_t req_cd;
+  u8 *authority, *path;
+
+  static void (*_hpack_serialize_request) (
+    u8 * app_headers, u32 app_headers_len,
+    hpack_request_control_data_t * control_data, u8 * *dst);
+
+  _hpack_serialize_request =
+    vlib_get_plugin_symbol ("http_plugin.so", "hpack_serialize_request");
+
+  authority = format (0, "www.example.com");
+  path = format (0, "/");
+
+  vec_validate (buf, 127);
+  vec_reset_length (buf);
+
+  req_cd.method = HTTP_REQ_GET;
+  req_cd.parsed_bitmap = HPACK_PSEUDO_HEADER_SCHEME_PARSED;
+  req_cd.scheme = HTTP_URL_SCHEME_HTTP;
+  req_cd.parsed_bitmap |= HPACK_PSEUDO_HEADER_PATH_PARSED;
+  req_cd.path = path;
+  req_cd.path_len = vec_len (path);
+  req_cd.parsed_bitmap |= HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
+  req_cd.authority = authority;
+  req_cd.authority_len = vec_len (authority);
+  req_cd.user_agent_len = 0;
+  req_cd.content_len = HPACK_ENCODER_SKIP_CONTENT_LEN;
+  u8 expected3[] =
+    "\x82\x86\x84\x01\x8C\xF1\xE3\xC2\xE5\xF2\x3A\x6B\xA0\xAB\x90\xF4\xFF";
+  _hpack_serialize_request (0, 0, &req_cd, &buf);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected3) - 1) &&
+             !memcmp (buf, expected3, sizeof (expected3) - 1)),
+            "request encoded as %U", format_hex_bytes, buf, vec_len (buf));
+  vec_reset_length (buf);
+  vec_free (authority);
+  vec_free (path);
+  memset (&req_cd, 0, sizeof (req_cd));
+
+  authority = format (0, "example.org:123");
+
+  req_cd.method = HTTP_REQ_CONNECT;
+  req_cd.parsed_bitmap |= HPACK_PSEUDO_HEADER_AUTHORITY_PARSED;
+  req_cd.authority = authority;
+  req_cd.authority_len = vec_len (authority);
+  req_cd.user_agent = server_name;
+  req_cd.user_agent_len = vec_len (server_name);
+  req_cd.content_len = HPACK_ENCODER_SKIP_CONTENT_LEN;
+
+  vec_validate (headers_buf, 127);
+  http_init_headers_ctx (&headers, headers_buf, vec_len (headers_buf));
+  http_add_custom_header (&headers, http_token_lit ("sandwich"),
+                         http_token_lit ("spam"));
+
+  u8 expected4[] =
+    "\x02\x07\x43\x4F\x4E\x4E\x45\x43\x54\x01\x8B\x2F\x91\xD3\x5D\x05\x5C\xF6"
+    "\x4D\x70\x22\x67\x0F\x2B\x8B\x9D\x29\xAD\x4B\x6A\x32\x54\x49\x50\x94\x7f"
+    "\x00\x86\x40\xEA\x93\xC1\x89\x3F\x83\x45\x63\xA7";
+  _hpack_serialize_request (headers_buf, headers.tail_offset, &req_cd, &buf);
+  HTTP_TEST ((vec_len (buf) == (sizeof (expected4) - 1) &&
+             !memcmp (buf, expected4, sizeof (expected4) - 1)),
+            "request encoded as %U", format_hex_bytes, buf, vec_len (buf));
+  vec_free (buf);
+  vec_free (server_name);
+  vec_free (authority);
+
   return 0;
 }