aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatus Fabian <matfabia@cisco.com>2025-02-25 06:52:25 -0500
committerFlorin Coras <florin.coras@gmail.com>2025-02-28 00:07:55 +0000
commita013224b9d35fcfd7c454c5222027ebb3a2a4d47 (patch)
tree3d5174037e166841a19425b3aa3129ddbf24aa8e
parentbeceb0d946b4c79b22d0254f673a9f9cf059f40c (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.h187
-rw-r--r--src/plugins/http/http2/hpack.c208
-rw-r--r--src/plugins/http/http2/hpack.h23
-rw-r--r--src/plugins/http/http_header_names.h3
-rw-r--r--src/plugins/http/test/http_test.c63
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;
}