http: hpack headers encoding 88/42388/3
authorMatus Fabian <[email protected]>
Tue, 25 Feb 2025 11:52:25 +0000 (06:52 -0500)
committerFlorin Coras <[email protected]>
Fri, 28 Feb 2025 00:07:55 +0000 (00:07 +0000)
1) header encoding without dynamic table
2) serialization of response header block

Type: feature

Change-Id: I7ec470310e5aec0f8055492e92682261b4af5e81
Signed-off-by: Matus Fabian <[email protected]>
src/plugins/http/http.h
src/plugins/http/http2/hpack.c
src/plugins/http/http2/hpack.h
src/plugins/http/http_header_names.h
src/plugins/http/test/http_test.c

index 593472a..1d2a949 100644 (file)
@@ -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;
index 917f32e..6dcf5f6 100644 (file)
@@ -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;
+}
index 9dda1d6..2a2936b 100644 (file)
@@ -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_ */
index 99acac7..1778daf 100644 (file)
@@ -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 _
 };
index 54d5a07..691efdf 100644 (file)
@@ -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;
 }