From 1523b51e8a8a7b4f36e4d57ae4eceac81153c630 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Fri, 20 Jun 2025 17:03:59 +0000 Subject: [PATCH] http: h2 encoding request Type: improvement Change-Id: I4609c3a89c4df0883aa25f07623dad68c539d70d Signed-off-by: Matus Fabian --- src/plugins/http/http.h | 15 ++- src/plugins/http/http2/hpack.c | 193 +++++++++++++++++++++++++++++++++++--- src/plugins/http/http2/hpack.h | 15 +++ src/plugins/http/test/http_test.c | 70 +++++++++++++- 4 files changed, 275 insertions(+), 18 deletions(-) diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h index bd9e40e23aa..0e028a77608 100644 --- a/src/plugins/http/http.h +++ b/src/plugins/http/http.h @@ -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_ diff --git a/src/plugins/http/http2/hpack.c b/src/plugins/http/http2/hpack.c index 324602ce346..15e92fe6831 100644 --- a/src/plugins/http/http2/hpack.c +++ b/src/plugins/http/http2/hpack.c @@ -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; +} diff --git a/src/plugins/http/http2/hpack.h b/src/plugins/http/http2/hpack.h index fef2a96c2df..a2fd5faed58 100644 --- a/src/plugins/http/http2/hpack.h +++ b/src/plugins/http/http2/hpack.h @@ -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_ */ diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c index f44d3cbd31b..73a397aef2f 100644 --- a/src/plugins/http/test/http_test.c +++ b/src/plugins/http/test/http_test.c @@ -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; } -- 2.16.6