From: Matus Fabian Date: Fri, 26 Sep 2025 15:59:29 +0000 (-0400) Subject: http: QPACK header encoding w/o dynamic table X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F80%2F43780%2F6;p=vpp.git http: QPACK header encoding w/o dynamic table Type: feature Change-Id: I10eeb9b7b5a5e8fc8733ea33463cffaece7bd162 Signed-off-by: Matus Fabian --- diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h index c106038806f..9eb31990892 100644 --- a/src/plugins/http/http.h +++ b/src/plugins/http/http.h @@ -290,6 +290,8 @@ typedef enum http_status_code_ _ (CONTENT_LENGTH, "Content-Length", "content-length", 28) \ _ (CONTENT_LOCATION, "Content-Location", "content-location", 29) \ _ (CONTENT_RANGE, "Content-Range", "content-range", 30) \ + _ (CONTENT_SECURITY_POLICY, "Content-Security-Policy", \ + "content-security-policy", 0) \ _ (CONTENT_TYPE, "Content-Type", "content-type", 31) \ _ (COOKIE, "Cookie", "cookie", 32) \ _ (DATE, "Date", "date", 33) \ @@ -299,6 +301,7 @@ typedef enum http_status_code_ _ (EARLY_DATA, "Early-Data", "early-data", 0) \ _ (ETAG, "ETag", "etag", 34) \ _ (EXPECT, "Expect", "expect", 35) \ + _ (EXPECT_CT, "Expect-CT", "expect-ct", 0) \ _ (EXPIRES, "Expires", "expires", 36) \ _ (FORWARDED, "Forwarded", "forwarded", 0) \ _ (FROM, "From", "from", 37) \ @@ -320,6 +323,7 @@ typedef enum http_status_code_ "proxy-authentication-info", 0) \ _ (PROXY_AUTHORIZATION, "Proxy-Authorization", "proxy-authorization", 49) \ _ (PROXY_STATUS, "Proxy-Status", "proxy-status", 0) \ + _ (PURPOSE, "Purpose", "purpose", 0) \ _ (RANGE, "Range", "range", 50) \ _ (REFERER, "Referer", "referer", 51) \ _ (REFRESH, "Refresh", "refresh", 52) \ @@ -332,15 +336,23 @@ typedef enum http_status_code_ _ (STRICT_TRANSPORT_SECURITY, "Strict-Transport-Security", \ "strict-transport-security", 56) \ _ (TE, "TE", "te", 0) \ + _ (TIMING_ALLOW_ORIGIN, "Timing-Allow-Origin", "timing-allow-origin", 0) \ _ (TRAILER, "Trailer", "trailer", 0) \ _ (TRANSFER_ENCODING, "Transfer-Encoding", "transfer-encoding", 57) \ _ (UPGRADE, "Upgrade", "upgrade", 0) \ + _ (UPGRADE_INSECURE_REQUESTS, "Upgrade-Insecure-Requests", \ + "upgrade-insecure-requests", 0) \ _ (USER_AGENT, "User-Agent", "user-agent", 58) \ _ (VARY, "Vary", "vary", 59) \ _ (VIA, "Via", "via", 60) \ _ (WANT_CONTENT_DIGEST, "Want-Content-Digest", "want-content-digest", 0) \ _ (WANT_REPR_DIGEST, "Want-Repr-Digest", "want-repr-digest", 0) \ - _ (WWW_AUTHENTICATE, "WWW-Authenticate", "www-authenticate", 61) + _ (WWW_AUTHENTICATE, "WWW-Authenticate", "www-authenticate", 61) \ + _ (X_CONTENT_TYPE_OPTIONS, "X-Content-Type-Options", \ + "x-content-type-options", 0) \ + _ (X_FORWARDED_FOR, "X-Forwarded-For", "x-forwarded-for", 0) \ + _ (X_FRAME_OPTIONS, "X-Frame-Options", "x-frame-options", 0) \ + _ (X_XSS_PROTECTION, "X-XSS-Protection", "x-xss-protection", 0) typedef enum http_header_name_ { diff --git a/src/plugins/http/http2/hpack.c b/src/plugins/http/http2/hpack.c index b1461dfb638..5fd049efe35 100644 --- a/src/plugins/http/http2/hpack.c +++ b/src/plugins/http/http2/hpack.c @@ -86,20 +86,6 @@ static hpack_static_table_entry_t { name_val_token_lit ("www-authenticate", "") }, }; -typedef struct -{ - char *base; - uword len; - u8 static_table_index; -} hpack_token_t; - -static hpack_token_t hpack_headers[] = { -#define _(sym, str_canonical, str_lower, hpack_index) \ - { http_token_lit (str_lower), hpack_index }, - foreach_http_header_name -#undef _ -}; - static http_token_t http_methods[] = { #define _(s, str) { http_token_lit (str) }, foreach_http_method @@ -489,12 +475,11 @@ static inline u8 * hpack_encode_header (u8 *dst, http_header_name_t name, const u8 *value, u32 value_len) { - hpack_token_t *name_token; u8 *a, *b; u32 orig_len, actual_size; orig_len = vec_len (dst); - name_token = &hpack_headers[name]; + const hpack_token_t *name_token = &hpack_headers[name]; if (name_token->static_table_index) { /* static table index with 4 bit prefix is max 2 bytes */ diff --git a/src/plugins/http/http2/hpack_inlines.h b/src/plugins/http/http2/hpack_inlines.h index 50a0825e777..804f9bda5a1 100644 --- a/src/plugins/http/http2/hpack_inlines.h +++ b/src/plugins/http/http2/hpack_inlines.h @@ -23,6 +23,20 @@ typedef hpack_error_t (hpack_header_decoder_fn) (u8 **src, u8 *end, u8 **buf, u32 *value_len, void *decoder_ctx); +typedef struct +{ + char *base; + uword len; + u8 static_table_index; +} hpack_token_t; + +static const hpack_token_t hpack_headers[] = { +#define _(sym, str_canonical, str_lower, hpack_index) \ + { http_token_lit (str_lower), hpack_index }, + foreach_http_header_name +#undef _ +}; + /** * Decode unsigned variable-length integer (RFC7541 section 5.1) * diff --git a/src/plugins/http/http3/qpack.c b/src/plugins/http/http3/qpack.c index e0c9377e9b7..d2fa4aedaba 100644 --- a/src/plugins/http/http3/qpack.c +++ b/src/plugins/http/http3/qpack.c @@ -130,6 +130,717 @@ static qpack_static_table_entry_t qpack_static_table[] = { STATIC_ASSERT (QPACK_STATIC_TABLE_SIZE == 99, "static table must have 99 entries"); +typedef int (*qpack_static_table_lookup_fn) (const char *value, u32 value_len, + u8 *full_match); + +static int +qpack_lookup_no_match (const char *value, u32 value_len, u8 *full_match) +{ + return -1; +} + +static int +qpack_lookup_accept_encoding (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[31].value, + qpack_static_table[31].value_len); + return 31; +} + +static int +qpack_lookup_accept_language (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 72; +} + +static int +qpack_lookup_accept_ranges (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[32].value, + qpack_static_table[32].value_len); + return 32; +} + +static int +qpack_lookup_accept (const char *value, u32 value_len, u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[29].value, + qpack_static_table[29].value_len)) + { + *full_match = 1; + return 29; + } + if (http_token_is (value, value_len, qpack_static_table[30].value, + qpack_static_table[30].value_len)) + { + *full_match = 1; + return 30; + } + + *full_match = 0; + return 29; +} + +static int +qpack_lookup_access_control_allow_credentials (const char *value, + u32 value_len, u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[73].value, + qpack_static_table[73].value_len)) + { + *full_match = 1; + return 73; + } + if (http_token_is (value, value_len, qpack_static_table[74].value, + qpack_static_table[74].value_len)) + { + *full_match = 1; + return 74; + } + + *full_match = 0; + return 73; +} + +static int +qpack_lookup_access_control_allow_headers (const char *value, u32 value_len, + u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[33].value, + qpack_static_table[33].value_len)) + { + *full_match = 1; + return 33; + } + if (http_token_is (value, value_len, qpack_static_table[34].value, + qpack_static_table[34].value_len)) + { + *full_match = 1; + return 34; + } + if (http_token_is (value, value_len, qpack_static_table[75].value, + qpack_static_table[75].value_len)) + { + *full_match = 1; + return 75; + } + + *full_match = 0; + return 33; +} + +static int +qpack_lookup_access_control_allow_methods (const char *value, u32 value_len, + u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[76].value, + qpack_static_table[76].value_len)) + { + *full_match = 1; + return 76; + } + if (http_token_is (value, value_len, qpack_static_table[77].value, + qpack_static_table[77].value_len)) + { + *full_match = 1; + return 77; + } + if (http_token_is (value, value_len, qpack_static_table[78].value, + qpack_static_table[78].value_len)) + { + *full_match = 1; + return 78; + } + + *full_match = 0; + return 76; +} + +static int +qpack_lookup_access_control_allow_origin (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[35].value, + qpack_static_table[35].value_len); + return 35; +} + +static int +qpack_lookup_access_control_expose_headers (const char *value, u32 value_len, + u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[79].value, + qpack_static_table[79].value_len)) + { + *full_match = 1; + return 79; + } + if (http_token_is (value, value_len, qpack_static_table[80].value, + qpack_static_table[80].value_len)) + { + *full_match = 1; + return 80; + } + + *full_match = 0; + return 79; +} + +static int +qpack_lookup_access_control_request_method (const char *value, u32 value_len, + u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[81].value, + qpack_static_table[81].value_len)) + { + *full_match = 1; + return 81; + } + if (http_token_is (value, value_len, qpack_static_table[82].value, + qpack_static_table[82].value_len)) + { + *full_match = 1; + return 82; + } + + *full_match = 0; + return 81; +} + +static int +qpack_lookup_age (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[2].value, + qpack_static_table[2].value_len); + return 2; +} + +static int +qpack_lookup_alt_svc (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[83].value, + qpack_static_table[83].value_len); + return 83; +} + +static int +qpack_lookup_authorization (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 84; +} + +static int +qpack_lookup_cache_control (const char *value, u32 value_len, u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[36].value, + qpack_static_table[36].value_len)) + { + *full_match = 1; + return 36; + } + if (http_token_is (value, value_len, qpack_static_table[37].value, + qpack_static_table[37].value_len)) + { + *full_match = 1; + return 37; + } + if (http_token_is (value, value_len, qpack_static_table[38].value, + qpack_static_table[38].value_len)) + { + *full_match = 1; + return 38; + } + if (http_token_is (value, value_len, qpack_static_table[39].value, + qpack_static_table[39].value_len)) + { + *full_match = 1; + return 39; + } + if (http_token_is (value, value_len, qpack_static_table[40].value, + qpack_static_table[40].value_len)) + { + *full_match = 1; + return 40; + } + if (http_token_is (value, value_len, qpack_static_table[41].value, + qpack_static_table[41].value_len)) + { + *full_match = 1; + return 41; + } + + *full_match = 0; + return 36; +} + +static int +qpack_lookup_content_disposition (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = 0; + return 3; +} + +static int +qpack_lookup_content_encoding (const char *value, u32 value_len, + u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[42].value, + qpack_static_table[42].value_len)) + { + *full_match = 1; + return 42; + } + if (http_token_is (value, value_len, qpack_static_table[43].value, + qpack_static_table[43].value_len)) + { + *full_match = 1; + return 43; + } + + *full_match = 0; + return 42; +} + +static int +qpack_lookup_content_security_policy (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[85].value, + qpack_static_table[85].value_len); + return 85; +} + +static int +qpack_lookup_content_type (const char *value, u32 value_len, u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[44].value, + qpack_static_table[44].value_len)) + { + *full_match = 1; + return 44; + } + if (http_token_is (value, value_len, qpack_static_table[45].value, + qpack_static_table[45].value_len)) + { + *full_match = 1; + return 45; + } + if (http_token_is (value, value_len, qpack_static_table[46].value, + qpack_static_table[46].value_len)) + { + *full_match = 1; + return 46; + } + if (http_token_is (value, value_len, qpack_static_table[47].value, + qpack_static_table[47].value_len)) + { + *full_match = 1; + return 47; + } + if (http_token_is (value, value_len, qpack_static_table[48].value, + qpack_static_table[48].value_len)) + { + *full_match = 1; + return 48; + } + if (http_token_is (value, value_len, qpack_static_table[49].value, + qpack_static_table[49].value_len)) + { + *full_match = 1; + return 49; + } + if (http_token_is (value, value_len, qpack_static_table[50].value, + qpack_static_table[50].value_len)) + { + *full_match = 1; + return 50; + } + if (http_token_is (value, value_len, qpack_static_table[51].value, + qpack_static_table[51].value_len)) + { + *full_match = 1; + return 51; + } + if (http_token_is (value, value_len, qpack_static_table[52].value, + qpack_static_table[52].value_len)) + { + *full_match = 1; + return 52; + } + if (http_token_is (value, value_len, qpack_static_table[53].value, + qpack_static_table[53].value_len)) + { + *full_match = 1; + return 53; + } + if (http_token_is (value, value_len, qpack_static_table[54].value, + qpack_static_table[54].value_len)) + { + *full_match = 1; + return 54; + } + + *full_match = 0; + return 44; +} + +static int +qpack_lookup_cookie (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 5; +} + +static int +qpack_lookup_date (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 6; +} + +static int +qpack_lookup_early_data (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[86].value, + qpack_static_table[86].value_len); + return 86; +} + +static int +qpack_lookup_etag (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 7; +} + +static int +qpack_lookup_expect_ct (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 87; +} + +static int +qpack_lookup_forwarded (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 88; +} + +static int +qpack_lookup_if_modified_since (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = 0; + return 8; +} + +static int +qpack_lookup_if_none_match (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 9; +} + +static int +qpack_lookup_if_range (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 89; +} + +static int +qpack_lookup_last_modified (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 10; +} + +static int +qpack_lookup_link (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 11; +} + +static int +qpack_lookup_location (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 12; +} + +static int +qpack_lookup_origin (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 90; +} + +static int +qpack_lookup_purpose (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[91].value, + qpack_static_table[91].value_len); + return 91; +} + +static int +qpack_lookup_range (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[55].value, + qpack_static_table[55].value_len); + return 55; +} + +static int +qpack_lookup_referer (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 13; +} + +static int +qpack_lookup_server (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 92; +} + +static int +qpack_lookup_set_cookie (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 14; +} + +static int +qpack_lookup_strict_transport_security (const char *value, u32 value_len, + u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[56].value, + qpack_static_table[56].value_len)) + { + *full_match = 1; + return 56; + } + if (http_token_is (value, value_len, qpack_static_table[57].value, + qpack_static_table[57].value_len)) + { + *full_match = 1; + return 57; + } + if (http_token_is (value, value_len, qpack_static_table[58].value, + qpack_static_table[58].value_len)) + { + *full_match = 1; + return 58; + } + + *full_match = 0; + return 56; +} + +static int +qpack_lookup_timing_allow_origin (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[93].value, + qpack_static_table[93].value_len); + return 93; +} + +static int +qpack_lookup_upgrade_insecure_requests (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[94].value, + qpack_static_table[94].value_len); + return 94; +} + +static int +qpack_lookup_user_agent (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 95; +} + +static int +qpack_lookup_vary (const char *value, u32 value_len, u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[59].value, + qpack_static_table[59].value_len)) + { + *full_match = 1; + return 59; + } + if (http_token_is (value, value_len, qpack_static_table[60].value, + qpack_static_table[60].value_len)) + { + *full_match = 1; + return 60; + } + + *full_match = 0; + return 59; +} + +static int +qpack_lookup_x_content_type_options (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[61].value, + qpack_static_table[61].value_len); + return 61; +} + +static int +qpack_lookup_x_forwarded_for (const char *value, u32 value_len, u8 *full_match) +{ + *full_match = 0; + return 96; +} + +static int +qpack_lookup_x_frame_options (const char *value, u32 value_len, u8 *full_match) +{ + if (http_token_is (value, value_len, qpack_static_table[97].value, + qpack_static_table[97].value_len)) + { + *full_match = 1; + return 97; + } + if (http_token_is (value, value_len, qpack_static_table[98].value, + qpack_static_table[98].value_len)) + { + *full_match = 1; + return 98; + } + + *full_match = 0; + return 97; +} + +static int +qpack_lookup_x_xss_protection (const char *value, u32 value_len, + u8 *full_match) +{ + *full_match = http_token_is (value, value_len, qpack_static_table[62].value, + qpack_static_table[62].value_len); + return 62; +} + +static qpack_static_table_lookup_fn qpack_static_table_lookup[] = { + [HTTP_HEADER_ACCEPT_CHARSET] = qpack_lookup_no_match, + [HTTP_HEADER_ACCEPT_ENCODING] = qpack_lookup_accept_encoding, + [HTTP_HEADER_ACCEPT_LANGUAGE] = qpack_lookup_accept_language, + [HTTP_HEADER_ACCEPT_RANGES] = qpack_lookup_accept_ranges, + [HTTP_HEADER_ACCEPT] = qpack_lookup_accept, + [HTTP_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS] = + qpack_lookup_access_control_allow_credentials, + [HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS] = + qpack_lookup_access_control_allow_headers, + [HTTP_HEADER_ACCESS_CONTROL_ALLOW_METHODS] = + qpack_lookup_access_control_allow_methods, + [HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN] = + qpack_lookup_access_control_allow_origin, + [HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS] = + qpack_lookup_access_control_expose_headers, + [HTTP_HEADER_ACCESS_CONTROL_MAX_AGE] = qpack_lookup_no_match, + [HTTP_HEADER_ACCESS_CONTROL_REQUEST_HEADERS] = qpack_lookup_no_match, + [HTTP_HEADER_ACCESS_CONTROL_REQUEST_METHOD] = + qpack_lookup_access_control_request_method, + [HTTP_HEADER_AGE] = qpack_lookup_age, + [HTTP_HEADER_ALLOW] = qpack_lookup_no_match, + [HTTP_HEADER_ALPN] = qpack_lookup_no_match, + [HTTP_HEADER_ALT_SVC] = qpack_lookup_alt_svc, + [HTTP_HEADER_ALT_USED] = qpack_lookup_no_match, + [HTTP_HEADER_ALTERNATES] = qpack_lookup_no_match, + [HTTP_HEADER_AUTHENTICATION_CONTROL] = qpack_lookup_no_match, + [HTTP_HEADER_AUTHENTICATION_INFO] = qpack_lookup_no_match, + [HTTP_HEADER_AUTHORIZATION] = qpack_lookup_authorization, + [HTTP_HEADER_CACHE_CONTROL] = qpack_lookup_cache_control, + [HTTP_HEADER_CACHE_STATUS] = qpack_lookup_no_match, + [HTTP_HEADER_CAPSULE_PROTOCOL] = qpack_lookup_no_match, + [HTTP_HEADER_CDN_CACHE_CONTROL] = qpack_lookup_no_match, + [HTTP_HEADER_CDN_LOOP] = qpack_lookup_no_match, + [HTTP_HEADER_CLIENT_CERT] = qpack_lookup_no_match, + [HTTP_HEADER_CLIENT_CERT_CHAIN] = qpack_lookup_no_match, + [HTTP_HEADER_CLOSE] = qpack_lookup_no_match, + [HTTP_HEADER_CONNECTION] = qpack_lookup_no_match, + [HTTP_HEADER_CONTENT_DIGEST] = qpack_lookup_no_match, + [HTTP_HEADER_CONTENT_DISPOSITION] = qpack_lookup_content_disposition, + [HTTP_HEADER_CONTENT_ENCODING] = qpack_lookup_content_encoding, + [HTTP_HEADER_CONTENT_LANGUAGE] = qpack_lookup_no_match, + [HTTP_HEADER_CONTENT_LENGTH] = qpack_lookup_no_match, + [HTTP_HEADER_CONTENT_LOCATION] = qpack_lookup_no_match, + [HTTP_HEADER_CONTENT_RANGE] = qpack_lookup_no_match, + [HTTP_HEADER_CONTENT_SECURITY_POLICY] = qpack_lookup_content_security_policy, + [HTTP_HEADER_CONTENT_TYPE] = qpack_lookup_content_type, + [HTTP_HEADER_COOKIE] = qpack_lookup_cookie, + [HTTP_HEADER_DATE] = qpack_lookup_date, + [HTTP_HEADER_DIGEST] = qpack_lookup_no_match, + [HTTP_HEADER_DPOP] = qpack_lookup_no_match, + [HTTP_HEADER_DPOP_NONCE] = qpack_lookup_no_match, + [HTTP_HEADER_EARLY_DATA] = qpack_lookup_early_data, + [HTTP_HEADER_ETAG] = qpack_lookup_etag, + [HTTP_HEADER_EXPECT] = qpack_lookup_no_match, + [HTTP_HEADER_EXPECT_CT] = qpack_lookup_expect_ct, + [HTTP_HEADER_EXPIRES] = qpack_lookup_no_match, + [HTTP_HEADER_FORWARDED] = qpack_lookup_forwarded, + [HTTP_HEADER_FROM] = qpack_lookup_no_match, + [HTTP_HEADER_HOST] = qpack_lookup_no_match, + [HTTP_HEADER_IF_MATCH] = qpack_lookup_no_match, + [HTTP_HEADER_IF_MODIFIED_SINCE] = qpack_lookup_if_modified_since, + [HTTP_HEADER_IF_NONE_MATCH] = qpack_lookup_if_none_match, + [HTTP_HEADER_IF_RANGE] = qpack_lookup_if_range, + [HTTP_HEADER_IF_UNMODIFIED_SINCE] = qpack_lookup_no_match, + [HTTP_HEADER_KEEP_ALIVE] = qpack_lookup_no_match, + [HTTP_HEADER_LAST_MODIFIED] = qpack_lookup_last_modified, + [HTTP_HEADER_LINK] = qpack_lookup_link, + [HTTP_HEADER_LOCATION] = qpack_lookup_location, + [HTTP_HEADER_MAX_FORWARDS] = qpack_lookup_no_match, + [HTTP_HEADER_ORIGIN] = qpack_lookup_origin, + [HTTP_HEADER_PRIORITY] = qpack_lookup_no_match, + [HTTP_HEADER_PROXY_AUTHENTICATE] = qpack_lookup_no_match, + [HTTP_HEADER_PROXY_AUTHENTICATION_INFO] = qpack_lookup_no_match, + [HTTP_HEADER_PROXY_AUTHORIZATION] = qpack_lookup_no_match, + [HTTP_HEADER_PROXY_STATUS] = qpack_lookup_no_match, + [HTTP_HEADER_PURPOSE] = qpack_lookup_purpose, + [HTTP_HEADER_RANGE] = qpack_lookup_range, + [HTTP_HEADER_REFERER] = qpack_lookup_referer, + [HTTP_HEADER_REFRESH] = qpack_lookup_no_match, + [HTTP_HEADER_REPR_DIGEST] = qpack_lookup_no_match, + [HTTP_HEADER_RETRY_AFTER] = qpack_lookup_no_match, + [HTTP_HEADER_SERVER] = qpack_lookup_server, + [HTTP_HEADER_SET_COOKIE] = qpack_lookup_set_cookie, + [HTTP_HEADER_SIGNATURE] = qpack_lookup_no_match, + [HTTP_HEADER_SIGNATURE_INPUT] = qpack_lookup_no_match, + [HTTP_HEADER_STRICT_TRANSPORT_SECURITY] = + qpack_lookup_strict_transport_security, + [HTTP_HEADER_TE] = qpack_lookup_no_match, + [HTTP_HEADER_TIMING_ALLOW_ORIGIN] = qpack_lookup_timing_allow_origin, + [HTTP_HEADER_TRAILER] = qpack_lookup_no_match, + [HTTP_HEADER_TRANSFER_ENCODING] = qpack_lookup_no_match, + [HTTP_HEADER_UPGRADE] = qpack_lookup_no_match, + [HTTP_HEADER_UPGRADE_INSECURE_REQUESTS] = + qpack_lookup_upgrade_insecure_requests, + [HTTP_HEADER_USER_AGENT] = qpack_lookup_user_agent, + [HTTP_HEADER_VARY] = qpack_lookup_vary, + [HTTP_HEADER_VIA] = qpack_lookup_no_match, + [HTTP_HEADER_WANT_CONTENT_DIGEST] = qpack_lookup_no_match, + [HTTP_HEADER_WANT_REPR_DIGEST] = qpack_lookup_no_match, + [HTTP_HEADER_WWW_AUTHENTICATE] = qpack_lookup_no_match, + [HTTP_HEADER_X_CONTENT_TYPE_OPTIONS] = qpack_lookup_x_content_type_options, + [HTTP_HEADER_X_FORWARDED_FOR] = qpack_lookup_x_forwarded_for, + [HTTP_HEADER_X_FRAME_OPTIONS] = qpack_lookup_x_frame_options, + [HTTP_HEADER_X_XSS_PROTECTION] = qpack_lookup_x_xss_protection, +}; + static hpack_error_t qpack_get_static_table_entry (uword index, http_token_t *name, http_token_t *value, u8 value_is_indexed) @@ -194,6 +905,34 @@ qpack_decode_string (u8 **src, u8 *end, u8 **buf, uword *buf_len, } } +static_always_inline u8 * +qpack_encode_string (u8 *dst, const u8 *value, uword value_len, u8 prefix_len) +{ + uword huff_len; + + ASSERT (prefix_len >= 2 && prefix_len <= 8); + + huff_len = hpack_huffman_encoded_len (value, value_len); + /* raw bytes might take fewer bytes */ + if (huff_len >= value_len) + { + /* clear H flag and rest of the bits */ + *dst &= (u8) ~((1 << (prefix_len)) - 1); + dst = hpack_encode_int (dst, value_len, prefix_len - 1); + clib_memcpy (dst, value, value_len); + return dst + value_len; + } + + /* set H flag */ + *dst |= 1 << (prefix_len - 1); + /* clear rest of the prefix bits */ + *dst &= (u8) ~((1 << (prefix_len - 1)) - 1); + dst = hpack_encode_int (dst, huff_len, prefix_len - 1); + dst = hpack_encode_huffman (dst, value, value_len); + + return dst; +} + __clib_export hpack_error_t qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len, u32 *name_len, u32 *value_len, void *decoder_ctx) @@ -281,6 +1020,71 @@ qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len, return HPACK_ERROR_NONE; } +__clib_export u8 * +qpack_encode_header (u8 *dst, http_header_name_t name, const u8 *value, + u32 value_len) +{ + int index; + u8 *a, *b, full_match; + u32 orig_len, actual_size; + + orig_len = vec_len (dst); + index = qpack_static_table_lookup[name]((const char *) value, value_len, + &full_match); + if (index > -1) + { + if (full_match) + { + /* indexed field line, static table */ + vec_add2 (dst, a, 2); + *a = 0xC0; + b = hpack_encode_int (a, index, 6); + } + else + { + /* literal field line with name reference, static table */ + vec_add2 (dst, a, 2 + value_len + HPACK_ENCODED_INT_MAX_LEN); + *a = 0x50; + b = hpack_encode_int (a, index, 4); + b = qpack_encode_string (b, value, value_len, 8); + } + } + else + { + /* literal field line with literal name */ + const hpack_token_t *name_token = &hpack_headers[name]; + vec_add2 (dst, a, + name_token->len + value_len + HPACK_ENCODED_INT_MAX_LEN * 2 + + 1); + *a = 0x20; + b = qpack_encode_string (a, (const u8 *) name_token->base, + name_token->len, 4); + b = qpack_encode_string (b, value, value_len, 8); + } + + actual_size = b - a; + vec_set_len (dst, orig_len + actual_size); + return dst; +} + +__clib_export u8 * +qpack_encode_custom_header (u8 *dst, const u8 *name, u32 name_len, + const u8 *value, u32 value_len) +{ + u8 *a, *b; + u32 orig_len, actual_size; + + orig_len = vec_len (dst); + /* literal field line with literal name */ + vec_add2 (dst, a, name_len + value_len + HPACK_ENCODED_INT_MAX_LEN * 2 + 1); + *a = 0x20; + b = qpack_encode_string (a, name, name_len, 4); + b = qpack_encode_string (b, value, value_len, 8); + actual_size = b - a; + vec_set_len (dst, orig_len + actual_size); + return dst; +} + 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, diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c index 11f5823c589..a4a08c2a020 100644 --- a/src/plugins/http/test/http_test.c +++ b/src/plugins/http/test/http_test.c @@ -1722,6 +1722,59 @@ http_test_qpack (vlib_main_t *vm) vec_free (buf); vec_free (headers); + vlib_cli_output (vm, "qpack_encode_header"); + + static u8 *(*_qpack_encode_header) (u8 * dst, http_header_name_t name, + const u8 *value, u32 value_len); + _qpack_encode_header = + vlib_get_plugin_symbol ("http_plugin.so", "qpack_encode_header"); + + /* indexed field line, static table */ + buf = _qpack_encode_header (buf, HTTP_HEADER_CACHE_CONTROL, + (const u8 *) "no-cache", 8); + HTTP_TEST ((vec_len (buf) == 1 && buf[0] == 0xE7), + "'cache-control: no-cache' encoded as: %U", format_hex_bytes, buf, + vec_len (buf)); + vec_free (buf); + + /* literal field line with name reference, static table */ + u8 expected1[] = "\x56\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95" + "\x04\x0B\x81\x66\xE0\x82\xA6\x2D\x1B\xFF"; + buf = _qpack_encode_header ( + buf, HTTP_HEADER_DATE, (const u8 *) "Mon, 21 Oct 2013 20:13:21 GMT", 29); + HTTP_TEST ((vec_len (buf) == (sizeof (expected1) - 1) && + !memcmp (buf, expected1, vec_len (buf))), + "'date: Mon, 21 Oct 2013 20:13:21 GMT' encoded as: %U", + format_hex_bytes, buf, vec_len (buf)); + vec_free (buf); + + /* literal field line with literal name */ + u8 expected2[] = "\x2F\x01\x20\xC9\x39\x56\x42\x46\x9B\x51\x8D\xC1\xE4\x74" + "\xD7\x41\x6F\x0C\x93\x97\xED\x49\xCC\x9F"; + buf = _qpack_encode_header (buf, HTTP_HEADER_CACHE_STATUS, + (const u8 *) "ExampleCache; hit", 17); + HTTP_TEST ((vec_len (buf) == (sizeof (expected2) - 1) && + !memcmp (buf, expected2, vec_len (buf))), + "'cache-status: ExampleCache; hit' encoded as: %U", + format_hex_bytes, buf, vec_len (buf)); + vec_free (buf); + + vlib_cli_output (vm, "qpack_encode_custom_header"); + + static u8 *(*_qpack_encode_custom_header) ( + u8 * dst, const u8 *name, u32 name_len, const u8 *value, u32 value_len); + _qpack_encode_custom_header = + vlib_get_plugin_symbol ("http_plugin.so", "qpack_encode_custom_header"); + + u8 expected3[] = "\x2E\x40\xEA\x93\xC1\x89\x3F\x83\x45\x63\xA7"; + buf = _qpack_encode_custom_header (buf, (const u8 *) "sandwich", 8, + (const u8 *) "spam", 4); + HTTP_TEST ((vec_len (buf) == (sizeof (expected3) - 1) && + !memcmp (buf, expected3, vec_len (buf))), + "'sandwich: spam' encoded as: %U", format_hex_bytes, buf, + vec_len (buf)); + vec_free (buf); + return 0; }