diff options
author | 2025-02-25 06:52:25 -0500 | |
---|---|---|
committer | 2025-02-28 00:07:55 +0000 | |
commit | a013224b9d35fcfd7c454c5222027ebb3a2a4d47 (patch) | |
tree | 3d5174037e166841a19425b3aa3129ddbf24aa8e | |
parent | beceb0d946b4c79b22d0254f673a9f9cf059f40c (diff) |
http: hpack headers encoding
1) header encoding without dynamic table
2) serialization of response header block
Type: feature
Change-Id: I7ec470310e5aec0f8055492e92682261b4af5e81
Signed-off-by: Matus Fabian <matfabia@cisco.com>
-rw-r--r-- | src/plugins/http/http.h | 187 | ||||
-rw-r--r-- | src/plugins/http/http2/hpack.c | 208 | ||||
-rw-r--r-- | src/plugins/http/http2/hpack.h | 23 | ||||
-rw-r--r-- | src/plugins/http/http_header_names.h | 3 | ||||
-rw-r--r-- | src/plugins/http/test/http_test.c | 63 |
5 files changed, 394 insertions, 90 deletions
diff --git a/src/plugins/http/http.h b/src/plugins/http/http.h index 593472a3591..1d2a949c612 100644 --- a/src/plugins/http/http.h +++ b/src/plugins/http/http.h @@ -210,97 +210,108 @@ typedef enum http_status_code_ } http_status_code_t; #define foreach_http_header_name \ - _ (ACCEPT, "Accept") \ - _ (ACCEPT_CHARSET, "Accept-Charset") \ - _ (ACCEPT_ENCODING, "Accept-Encoding") \ - _ (ACCEPT_LANGUAGE, "Accept-Language") \ - _ (ACCEPT_RANGES, "Accept-Ranges") \ - _ (ACCESS_CONTROL_ALLOW_CREDENTIALS, "Access-Control-Allow-Credentials") \ - _ (ACCESS_CONTROL_ALLOW_HEADERS, "Access-Control-Allow-Headers") \ - _ (ACCESS_CONTROL_ALLOW_METHODS, "Access-Control-Allow-Methods") \ - _ (ACCESS_CONTROL_ALLOW_ORIGIN, "Access-Control-Allow-Origin") \ - _ (ACCESS_CONTROL_EXPOSE_HEADERS, "Access-Control-Expose-Headers") \ - _ (ACCESS_CONTROL_MAX_AGE, "Access-Control-Max-Age") \ - _ (ACCESS_CONTROL_REQUEST_HEADERS, "Access-Control-Request-Headers") \ - _ (ACCESS_CONTROL_REQUEST_METHOD, "Access-Control-Request-Method") \ - _ (AGE, "Age") \ - _ (ALLOW, "Allow") \ - _ (ALPN, "ALPN") \ - _ (ALT_SVC, "Alt-Svc") \ - _ (ALT_USED, "Alt-Used") \ - _ (ALTERNATES, "Alternates") \ - _ (AUTHENTICATION_CONTROL, "Authentication-Control") \ - _ (AUTHENTICATION_INFO, "Authentication-Info") \ - _ (AUTHORIZATION, "Authorization") \ - _ (CACHE_CONTROL, "Cache-Control") \ - _ (CACHE_STATUS, "Cache-Status") \ - _ (CAPSULE_PROTOCOL, "Capsule-Protocol") \ - _ (CDN_CACHE_CONTROL, "CDN-Cache-Control") \ - _ (CDN_LOOP, "CDN-Loop") \ - _ (CLIENT_CERT, "Client-Cert") \ - _ (CLIENT_CERT_CHAIN, "Client-Cert-Chain") \ - _ (CLOSE, "Close") \ - _ (CONNECTION, "Connection") \ - _ (CONTENT_DIGEST, "Content-Digest") \ - _ (CONTENT_DISPOSITION, "Content-Disposition") \ - _ (CONTENT_ENCODING, "Content-Encoding") \ - _ (CONTENT_LANGUAGE, "Content-Language") \ - _ (CONTENT_LENGTH, "Content-Length") \ - _ (CONTENT_LOCATION, "Content-Location") \ - _ (CONTENT_RANGE, "Content-Range") \ - _ (CONTENT_TYPE, "Content-Type") \ - _ (COOKIE, "Cookie") \ - _ (DATE, "Date") \ - _ (DIGEST, "Digest") \ - _ (DPOP, "DPoP") \ - _ (DPOP_NONCE, "DPoP-Nonce") \ - _ (EARLY_DATA, "Early-Data") \ - _ (ETAG, "ETag") \ - _ (EXPECT, "Expect") \ - _ (EXPIRES, "Expires") \ - _ (FORWARDED, "Forwarded") \ - _ (FROM, "From") \ - _ (HOST, "Host") \ - _ (IF_MATCH, "If-Match") \ - _ (IF_MODIFIED_SINCE, "If-Modified-Since") \ - _ (IF_NONE_MATCH, "If-None-Match") \ - _ (IF_RANGE, "If-Range") \ - _ (IF_UNMODIFIED_SINCE, "If-Unmodified-Since") \ - _ (KEEP_ALIVE, "Keep-Alive") \ - _ (LAST_MODIFIED, "Last-Modified") \ - _ (LINK, "Link") \ - _ (LOCATION, "Location") \ - _ (MAX_FORWARDS, "Max-Forwards") \ - _ (ORIGIN, "Origin") \ - _ (PRIORITY, "Priority") \ - _ (PROXY_AUTHENTICATE, "Proxy-Authenticate") \ - _ (PROXY_AUTHENTICATION_INFO, "Proxy-Authentication-Info") \ - _ (PROXY_AUTHORIZATION, "Proxy-Authorization") \ - _ (PROXY_STATUS, "Proxy-Status") \ - _ (RANGE, "Range") \ - _ (REFERER, "Referer") \ - _ (REFRESH, "Refresh") \ - _ (REPR_DIGEST, "Repr-Digest") \ - _ (SET_COOKIE, "Set-Cookie") \ - _ (SIGNATURE, "Signature") \ - _ (SIGNATURE_INPUT, "Signature-Input") \ - _ (STRICT_TRANSPORT_SECURITY, "Strict-Transport-Security") \ - _ (RETRY_AFTER, "Retry-After") \ - _ (SERVER, "Server") \ - _ (TE, "TE") \ - _ (TRAILER, "Trailer") \ - _ (TRANSFER_ENCODING, "Transfer-Encoding") \ - _ (UPGRADE, "Upgrade") \ - _ (USER_AGENT, "User-Agent") \ - _ (VARY, "Vary") \ - _ (VIA, "Via") \ - _ (WANT_CONTENT_DIGEST, "Want-Content-Digest") \ - _ (WANT_REPR_DIGEST, "Want-Repr-Digest") \ - _ (WWW_AUTHENTICATE, "WWW-Authenticate") + _ (ACCEPT_CHARSET, "Accept-Charset", "accept-charset", 15) \ + _ (ACCEPT_ENCODING, "Accept-Encoding", "accept-encoding", 16) \ + _ (ACCEPT_LANGUAGE, "Accept-Language", "accept-language", 17) \ + _ (ACCEPT_RANGES, "Accept-Ranges", "accept-ranges", 18) \ + _ (ACCEPT, "Accept", "accept", 19) \ + _ (ACCESS_CONTROL_ALLOW_CREDENTIALS, "Access-Control-Allow-Credentials", \ + "access-control-allow-credentials", 0) \ + _ (ACCESS_CONTROL_ALLOW_HEADERS, "Access-Control-Allow-Headers", \ + "access-control-allow-headers", 0) \ + _ (ACCESS_CONTROL_ALLOW_METHODS, "Access-Control-Allow-Methods", \ + "access-control-allow-methods", 0) \ + _ (ACCESS_CONTROL_ALLOW_ORIGIN, "Access-Control-Allow-Origin", \ + "access-control-allow-origin", 20) \ + _ (ACCESS_CONTROL_EXPOSE_HEADERS, "Access-Control-Expose-Headers", \ + "access-control-expose-headers", 0) \ + _ (ACCESS_CONTROL_MAX_AGE, "Access-Control-Max-Age", \ + "access-control-max-age", 0) \ + _ (ACCESS_CONTROL_REQUEST_HEADERS, "Access-Control-Request-Headers", \ + "access-control-request-headers", 0) \ + _ (ACCESS_CONTROL_REQUEST_METHOD, "Access-Control-Request-Method", \ + "access-control-request-method", 0) \ + _ (AGE, "Age", "age", 21) \ + _ (ALLOW, "Allow", "allow", 22) \ + _ (ALPN, "ALPN", "alpn", 0) \ + _ (ALT_SVC, "Alt-Svc", "alt-svc", 0) \ + _ (ALT_USED, "Alt-Used", "alt-used", 0) \ + _ (ALTERNATES, "Alternates", "alternates", 0) \ + _ (AUTHENTICATION_CONTROL, "Authentication-Control", \ + "authentication-control", 0) \ + _ (AUTHENTICATION_INFO, "Authentication-Info", "authentication-info", 0) \ + _ (AUTHORIZATION, "Authorization", "authorization", 23) \ + _ (CACHE_CONTROL, "Cache-Control", "cache-control", 24) \ + _ (CACHE_STATUS, "Cache-Status", "cache-status", 0) \ + _ (CAPSULE_PROTOCOL, "Capsule-Protocol", "capsule-protocol", 0) \ + _ (CDN_CACHE_CONTROL, "CDN-Cache-Control", "cdn-cache-control", 0) \ + _ (CDN_LOOP, "CDN-Loop", "cdn-loop", 0) \ + _ (CLIENT_CERT, "Client-Cert", "client-cert", 0) \ + _ (CLIENT_CERT_CHAIN, "Client-Cert-Chain", "client-cert-chain", 0) \ + _ (CLOSE, "Close", "close", 0) \ + _ (CONNECTION, "Connection", "connection", 0) \ + _ (CONTENT_DIGEST, "Content-Digest", "content-digest", 0) \ + _ (CONTENT_DISPOSITION, "Content-Disposition", "content-disposition", 25) \ + _ (CONTENT_ENCODING, "Content-Encoding", "content-encoding", 26) \ + _ (CONTENT_LANGUAGE, "Content-Language", "content-language", 27) \ + _ (CONTENT_LENGTH, "Content-Length", "content-length", 28) \ + _ (CONTENT_LOCATION, "Content-Location", "content-location", 29) \ + _ (CONTENT_RANGE, "Content-Range", "content-range", 30) \ + _ (CONTENT_TYPE, "Content-Type", "content-type", 31) \ + _ (COOKIE, "Cookie", "cookie", 32) \ + _ (DATE, "Date", "date", 33) \ + _ (DIGEST, "Digest", "digest", 0) \ + _ (DPOP, "DPoP", "dpop", 0) \ + _ (DPOP_NONCE, "DPoP-Nonce", "dpop-nonce", 0) \ + _ (EARLY_DATA, "Early-Data", "early-data", 0) \ + _ (ETAG, "ETag", "etag", 34) \ + _ (EXPECT, "Expect", "expect", 35) \ + _ (EXPIRES, "Expires", "expires", 36) \ + _ (FORWARDED, "Forwarded", "forwarded", 0) \ + _ (FROM, "From", "from", 37) \ + _ (HOST, "Host", "host", 38) \ + _ (IF_MATCH, "If-Match", "if-match", 39) \ + _ (IF_MODIFIED_SINCE, "If-Modified-Since", "if-modified-since", 40) \ + _ (IF_NONE_MATCH, "If-None-Match", "if-none-match", 41) \ + _ (IF_RANGE, "If-Range", "if-range", 42) \ + _ (IF_UNMODIFIED_SINCE, "If-Unmodified-Since", "if-unmodified-since", 43) \ + _ (KEEP_ALIVE, "Keep-Alive", "keep-alive", 0) \ + _ (LAST_MODIFIED, "Last-Modified", "last-modified", 44) \ + _ (LINK, "Link", "link", 45) \ + _ (LOCATION, "Location", "location", 46) \ + _ (MAX_FORWARDS, "Max-Forwards", "max-forwards", 47) \ + _ (ORIGIN, "Origin", "origin", 0) \ + _ (PRIORITY, "Priority", "priority", 0) \ + _ (PROXY_AUTHENTICATE, "Proxy-Authenticate", "proxy-authenticate", 48) \ + _ (PROXY_AUTHENTICATION_INFO, "Proxy-Authentication-Info", \ + "proxy-authentication-info", 0) \ + _ (PROXY_AUTHORIZATION, "Proxy-Authorization", "proxy-authorization", 49) \ + _ (PROXY_STATUS, "Proxy-Status", "proxy-status", 0) \ + _ (RANGE, "Range", "range", 50) \ + _ (REFERER, "Referer", "referer", 51) \ + _ (REFRESH, "Refresh", "refresh", 52) \ + _ (REPR_DIGEST, "Repr-Digest", "repr-digest", 0) \ + _ (RETRY_AFTER, "Retry-After", "retry-after", 53) \ + _ (SERVER, "Server", "server", 54) \ + _ (SET_COOKIE, "Set-Cookie", "set-cookie", 55) \ + _ (SIGNATURE, "Signature", "signature", 0) \ + _ (SIGNATURE_INPUT, "Signature-Input", "signature-input", 0) \ + _ (STRICT_TRANSPORT_SECURITY, "Strict-Transport-Security", \ + "strict-transport-security", 56) \ + _ (TE, "TE", "te", 0) \ + _ (TRAILER, "Trailer", "trailer", 0) \ + _ (TRANSFER_ENCODING, "Transfer-Encoding", "transfer-encoding", 57) \ + _ (UPGRADE, "Upgrade", "upgrade", 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) typedef enum http_header_name_ { -#define _(sym, str) HTTP_HEADER_##sym, +#define _(sym, str_canonical, str_lower, hpack_index) HTTP_HEADER_##sym, foreach_http_header_name #undef _ } http_header_name_t; diff --git a/src/plugins/http/http2/hpack.c b/src/plugins/http/http2/hpack.c index 917f32e79a0..6dcf5f6c19b 100644 --- a/src/plugins/http/http2/hpack.c +++ b/src/plugins/http/http2/hpack.c @@ -7,6 +7,7 @@ #include <http/http.h> #include <http/http2/hpack.h> #include <http/http2/huffman_table.h> +#include <http/http_status_codes.h> #define HPACK_STATIC_TABLE_SIZE 61 @@ -86,6 +87,20 @@ 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 _ +}; + __clib_export uword hpack_decode_int (u8 **src, u8 *end, u8 prefix_len) { @@ -892,4 +907,195 @@ hpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len, HTTP_DBG (2, "%U", format_hpack_dynamic_table, dynamic_table); return HTTP2_ERROR_NO_ERROR; -}
\ No newline at end of file +} + +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]; + if (name_token->static_table_index) + { + /* static table index with 4 bit prefix is max 2 bytes */ + vec_add2 (dst, a, 2 + value_len + HPACK_ENCODED_INT_MAX_LEN); + /* Literal Header Field without Indexing — Indexed Name */ + *a = 0x00; /* zero first 4 bits */ + b = hpack_encode_int (a, name_token->static_table_index, 4); + } + else + { + /* one extra byte for 4 bit prefix */ + vec_add2 (dst, a, + name_token->len + value_len + HPACK_ENCODED_INT_MAX_LEN * 2 + + 1); + b = a; + /* Literal Header Field without Indexing — New Name */ + *b++ = 0x00; + b = hpack_encode_string (b, (const u8 *) name_token->base, + name_token->len); + } + b = hpack_encode_string (b, value, value_len); + + actual_size = b - a; + vec_set_len (dst, orig_len + actual_size); + return dst; +} + +static inline u8 * +hpack_encode_custom_header (u8 *dst, const u8 *name, u32 name_len, + const u8 *value, u32 value_len) +{ + u32 orig_len, actual_size; + u8 *a, *b; + + orig_len = vec_len (dst); + /* one extra byte for 4 bit prefix */ + vec_add2 (dst, a, name_len + value_len + HPACK_ENCODED_INT_MAX_LEN * 2 + 1); + b = a; + /* Literal Header Field without Indexing — New Name */ + *b++ = 0x00; + b = hpack_encode_string (b, name, name_len); + b = hpack_encode_string (b, value, value_len); + actual_size = b - a; + vec_set_len (dst, orig_len + actual_size); + return dst; +} + +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); + break; + case HTTP_STATUS_NO_CONTENT: + encode_common_sc (9); + break; + case HTTP_STATUS_PARTIAL_CONTENT: + encode_common_sc (10); + break; + case HTTP_STATUS_NOT_MODIFIED: + encode_common_sc (11); + break; + case HTTP_STATUS_BAD_REQUEST: + encode_common_sc (12); + break; + case HTTP_STATUS_NOT_FOUND: + encode_common_sc (13); + break; + case HTTP_STATUS_INTERNAL_ERROR: + encode_common_sc (14); + break; + default: + orig_len = vec_len (dst); + vec_add2 (dst, a, 5); + b = a; + /* Literal Header Field without Indexing — Indexed Name */ + *b++ = 8; + b = hpack_encode_string (b, (const u8 *) http_status_code_str[sc], 3); + actual_size = b - a; + vec_set_len (dst, orig_len + actual_size); + break; + } + return dst; +} + +static inline u8 * +hpack_encode_content_len (u8 *dst, u64 content_len) +{ + u8 digit_buffer[20]; + u8 *d = digit_buffer + sizeof (digit_buffer); + u32 orig_len, actual_size; + u8 *a, *b; + + orig_len = vec_len (dst); + vec_add2 (dst, a, 3 + sizeof (digit_buffer)); + b = a; + + /* static table index 28 */ + *b++ = 0x0F; + *b++ = 0x0D; + do + { + *--d = '0' + content_len % 10; + content_len /= 10; + } + while (content_len); + + b = hpack_encode_string (b, d, digit_buffer + sizeof (digit_buffer) - d); + actual_size = b - a; + vec_set_len (dst, orig_len + actual_size); + return dst; +} + +__clib_export void +hpack_serialize_response (u8 *app_headers, u32 app_headers_len, + hpack_response_control_data_t *control_data, + u8 **dst) +{ + u8 *p, *end; + + p = *dst; + + /* status code must be first since it is pseudo-header */ + p = hpack_encode_status_code (p, control_data->sc); + + /* server name */ + p = hpack_encode_header (p, HTTP_HEADER_SERVER, control_data->server_name, + control_data->server_name_len); + + /* date */ + p = hpack_encode_header (p, HTTP_HEADER_DATE, control_data->date, + control_data->date_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); + + 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 = 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 9dda1d6d49e..2a2936b7611 100644 --- a/src/plugins/http/http2/hpack.h +++ b/src/plugins/http/http2/hpack.h @@ -17,6 +17,7 @@ #define HPACK_DEFAULT_HEADER_TABLE_SIZE 4096 #define HPACK_DYNAMIC_TABLE_ENTRY_OVERHEAD 32 +#define HPACK_ENCODER_SKIP_CONTENT_LEN ((u64) ~0) typedef struct { @@ -56,6 +57,16 @@ typedef struct u16 parsed_bitmap; } hpack_request_control_data_t; +typedef struct +{ + http_status_code_t sc; + u64 content_len; + u8 *server_name; + u32 server_name_len; + u8 *date; + u32 date_len; +} hpack_response_control_data_t; + /** * Decode unsigned variable-length integer (RFC7541 section 5.1) * @@ -154,4 +165,16 @@ http2_error_t hpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len, http_field_line_t **headers, hpack_dynamic_table_t *dynamic_table); +/** + * Serialize response + * + * @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_response (u8 *app_headers, u32 app_headers_len, + hpack_response_control_data_t *control_data, + u8 **dst); + #endif /* SRC_PLUGINS_HTTP_HPACK_H_ */ diff --git a/src/plugins/http/http_header_names.h b/src/plugins/http/http_header_names.h index 99acac786db..1778daf10d9 100644 --- a/src/plugins/http/http_header_names.h +++ b/src/plugins/http/http_header_names.h @@ -8,7 +8,8 @@ #include <http/http.h> static http_token_t http_header_names[] = { -#define _(sym, str) { http_token_lit (str) }, +#define _(sym, str_canonical, str_lower, hpack_index) \ + { http_token_lit (str_canonical) }, foreach_http_header_name #undef _ }; diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c index 54d5a07855d..691efdf6138 100644 --- a/src/plugins/http/test/http_test.c +++ b/src/plugins/http/test/http_test.c @@ -954,6 +954,69 @@ http_test_hpack (vlib_main_t *vm) &table); _hpack_dynamic_table_free (&table); HTTP_TEST ((result == 0), "request with Huffman Coding (result=%d)", result); + + vlib_cli_output (vm, "hpack_serialize_response"); + + hpack_response_control_data_t resp_cd; + u8 *server_name; + u8 *date; + + static void (*_hpack_serialize_response) ( + u8 * app_headers, u32 app_headers_len, + hpack_response_control_data_t * control_data, u8 * *dst); + + _hpack_serialize_response = + vlib_get_plugin_symbol ("http_plugin.so", "hpack_serialize_response"); + + server_name = format (0, "http unit tests"); + date = format (0, "Mon, 21 Oct 2013 20:13:21 GMT"); + + vec_validate (buf, 127); + vec_reset_length (buf); + resp_cd.sc = HTTP_STATUS_GATEWAY_TIMEOUT; + resp_cd.content_len = HPACK_ENCODER_SKIP_CONTENT_LEN; + resp_cd.server_name = server_name; + resp_cd.server_name_len = vec_len (server_name); + resp_cd.date = date; + resp_cd.date_len = vec_len (date); + u8 expected1[] = + "\x08\x03\x35\x30\x34\x0F\x27\x8B\x9D\x29\xAD\x4B\x6A\x32\x54\x49\x50\x94" + "\x7F\x0F\x12\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95\x04\x0B" + "\x81\x66\xE0\x82\xA6\x2D\x1B\xFF"; + _hpack_serialize_response (0, 0, &resp_cd, &buf); + HTTP_TEST ((vec_len (buf) == (sizeof (expected1) - 1) && + !memcmp (buf, expected1, sizeof (expected1) - 1)), + "response encoded as %U", format_hex_bytes, buf, vec_len (buf)); + vec_reset_length (buf); + + resp_cd.sc = HTTP_STATUS_OK; + resp_cd.content_len = 1024; + http_headers_ctx_t headers; + u8 *headers_buf = 0; + vec_validate (headers_buf, 127); + http_init_headers_ctx (&headers, headers_buf, vec_len (headers_buf)); + http_add_header (&headers, HTTP_HEADER_CONTENT_TYPE, + http_token_lit ("text/plain")); + http_add_header (&headers, HTTP_HEADER_CACHE_STATUS, + http_token_lit ("ExampleCache; hit")); + http_add_custom_header (&headers, http_token_lit ("sandwich"), + http_token_lit ("spam")); + u8 expected2[] = + "\x88\x0F\x27\x8B\x9D\x29\xAD\x4B\x6A\x32\x54\x49\x50\x94\x7F\x0F\x12\x96" + "\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95\x04\x0B\x81\x66\xE0\x82" + "\xA6\x2D\x1B\xFF\x0F\x0D\x83\x08\x04\xD7\x0F\x10\x87\x49\x7C\xA5\x8A\xE8" + "\x19\xAA\x00\x88\x20\xC9\x39\x56\x42\x46\x9B\x51\x8D\xC1\xE4\x74\xD7\x41" + "\x6F\x0C\x93\x97\xED\x49\xCC\x9F\x00\x86\x40\xEA\x93\xC1\x89\x3F\x83\x45" + "\x63\xA7"; + _hpack_serialize_response (headers_buf, headers.tail_offset, &resp_cd, &buf); + 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_free (headers_buf); + vec_free (server_name); + vec_free (date); + return 0; } |