From: Matus Fabian Date: Tue, 30 Sep 2025 10:02:20 +0000 (-0400) Subject: http: QPACK encoding request X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F16%2F43816%2F2;p=vpp.git http: QPACK encoding request Type: feature Change-Id: Ic823d9db4f594b66fff14aefd5658350d5621fda Signed-off-by: Matus Fabian --- diff --git a/src/plugins/http/http3/qpack.c b/src/plugins/http/http3/qpack.c index 2fd53473c48..bb14a6bf236 100644 --- a/src/plugins/http/http3/qpack.c +++ b/src/plugins/http/http3/qpack.c @@ -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; +} diff --git a/src/plugins/http/http3/qpack.h b/src/plugins/http/http3/qpack.h index da36e46429e..d0abc326a03 100644 --- a/src/plugins/http/http3/qpack.h +++ b/src/plugins/http/http3/qpack.h @@ -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_ */ diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c index 6ce3a153785..3d61b0b7a31 100644 --- a/src/plugins/http/test/http_test.c +++ b/src/plugins/http/test/http_test.c @@ -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; }