#define foreach_http_status_code \
_ (100, CONTINUE, "100 Continue") \
_ (101, SWITCHING_PROTOCOLS, "101 Switching Protocols") \
+ _ (102, PROCESSING, "102 Processing") \
+ _ (103, EARLY_HINTS, "103 Early Hints") \
_ (200, OK, "200 OK") \
_ (201, CREATED, "201 Created") \
_ (202, ACCEPTED, "202 Accepted") \
_ (417, EXPECTATION_FAILED, "417 Expectation Failed") \
_ (421, MISDIRECTED_REQUEST, "421 Misdirected Request") \
_ (422, UNPROCESSABLE_CONTENT, "422 Unprocessable_Content") \
+ _ (423, LOCKED, "423 Locked") \
+ _ (424, FAILED_DEPENDENCY, "424 Failed Dependency") \
+ _ (425, TOO_EARLY, "425 Too Early") \
_ (426, UPGRADE_REQUIRED, "426 Upgrade Required") \
+ _ (428, PRECONDITION_REQUIRED, "428 Precondition Required") \
+ _ (429, TOO_MANY_REQUESTS, "429 Too Many Requests") \
+ _ (431, REQUEST_HEADER_FIELDS_TOO_LARGE, \
+ "431 Request Header Fields Too Large") \
+ _ (451, UNAVAILABLE_FOR_LEGAL_REASONS, "451 Unavailable For Legal Reasons") \
_ (500, INTERNAL_ERROR, "500 Internal Server Error") \
_ (501, NOT_IMPLEMENTED, "501 Not Implemented") \
_ (502, BAD_GATEWAY, "502 Bad Gateway") \
_ (503, SERVICE_UNAVAILABLE, "503 Service Unavailable") \
_ (504, GATEWAY_TIMEOUT, "504 Gateway Timeout") \
- _ (505, HTTP_VERSION_NOT_SUPPORTED, "505 HTTP Version Not Supported")
+ _ (505, HTTP_VERSION_NOT_SUPPORTED, "505 HTTP Version Not Supported") \
+ _ (506, VARIANT_ALSO_NEGOTIATES, "506 Variant Also Negotiates") \
+ _ (507, INSUFFICIENT_STORAGE, "507 Insufficient Storage") \
+ _ (508, LOOP_DETECTED, "508 Loop Detected") \
+ _ (511, NETWORK_AUTHENTICATION_REQUIRED, \
+ "511 Network Authentication Required")
typedef enum http_status_code_
{
#include <http/http3/qpack.h>
#include <http/http2/hpack_inlines.h>
+#include <http/http_status_codes.h>
typedef struct
{
return 42;
}
+static int
+qpack_lookup_content_length (const char *value, u32 value_len, u8 *full_match)
+{
+ /* "content-length: 0" is encoded directly in qpack_encode_content_len */
+ *full_match = 0;
+ return 4;
+}
+
static int
qpack_lookup_content_security_policy (const char *value, u32 value_len,
u8 *full_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_LENGTH] = qpack_lookup_content_length,
[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,
[HPACK_ERROR_UNKNOWN] = HTTP3_ERROR_INTERNAL_ERROR,
};
+#define encode_static_entry(_index) \
+ vec_add2 (dst, a, 1); \
+ *a++ = 0xC0 | _index;
+
+static u8 *
+qpack_encode_status_code (u8 *dst, http_status_code_t sc)
+{
+ u32 orig_len, actual_size;
+ u8 *a, *b;
+
+ switch (sc)
+ {
+ case HTTP_STATUS_EARLY_HINTS:
+ encode_static_entry (24);
+ break;
+ case HTTP_STATUS_OK:
+ encode_static_entry (25);
+ break;
+ case HTTP_STATUS_NOT_MODIFIED:
+ encode_static_entry (26);
+ break;
+ case HTTP_STATUS_NOT_FOUND:
+ encode_static_entry (27);
+ break;
+ case HTTP_STATUS_SERVICE_UNAVAILABLE:
+ encode_static_entry (28);
+ break;
+ case HTTP_STATUS_CONTINUE:
+ encode_static_entry (63);
+ break;
+ case HTTP_STATUS_NO_CONTENT:
+ encode_static_entry (64);
+ break;
+ case HTTP_STATUS_PARTIAL_CONTENT:
+ encode_static_entry (65);
+ break;
+ case HTTP_STATUS_FOUND:
+ encode_static_entry (66);
+ break;
+ case HTTP_STATUS_BAD_REQUEST:
+ encode_static_entry (67);
+ break;
+ case HTTP_STATUS_FORBIDDEN:
+ encode_static_entry (68);
+ break;
+ case HTTP_STATUS_MISDIRECTED_REQUEST:
+ encode_static_entry (69);
+ break;
+ case HTTP_STATUS_TOO_EARLY:
+ encode_static_entry (70);
+ break;
+ case HTTP_STATUS_INTERNAL_ERROR:
+ encode_static_entry (71);
+ break;
+ default:
+ orig_len = vec_len (dst);
+ vec_add2 (dst, a, 5);
+ *a = 0x50;
+ b = hpack_encode_int (a, 24, 4);
+ b = qpack_encode_string (b, (const u8 *) http_status_code_str[sc], 3, 8);
+ actual_size = b - a;
+ vec_set_len (dst, orig_len + actual_size);
+ break;
+ }
+ return dst;
+}
+
+static u8 *
+qpack_encode_content_len (u8 *dst, u64 content_len)
+{
+ u8 digit_buffer[20];
+ u8 *d = digit_buffer + sizeof (digit_buffer);
+ u8 *a;
+
+ /* save some cycles and encode "content-length: 0" directly */
+ if (content_len == 0)
+ {
+ vec_add2 (dst, a, 1);
+ /* static table index 4 */
+ *a = 0xC4;
+ return dst;
+ }
+
+ do
+ {
+ *--d = '0' + content_len % 10;
+ content_len /= 10;
+ }
+ while (content_len);
+
+ dst = qpack_encode_header (dst, HTTP_HEADER_CONTENT_LENGTH, d,
+ digit_buffer + sizeof (digit_buffer) - d);
+ return dst;
+}
+
static inline hpack_error_t
qpack_parse_headers_prefix (u8 **src, u8 *end, qpack_decoder_ctx_t *ctx)
{
decoder_ctx, qpack_decode_header);
return hpack_error_to_http3_error[rv];
}
+
+__clib_export void
+qpack_serialize_response (u8 *app_headers, u32 app_headers_len,
+ hpack_response_control_data_t *control_data,
+ u8 **dst)
+{
+ u8 *a, *p, *end;
+
+ p = *dst;
+ /* encoded field section prefix, two zero bytes because we don't use dynamic
+ * table */
+ vec_add2 (p, a, 2);
+ a[0] = 0;
+ a[1] = 0;
+
+ /* status code must be first since it is pseudo-header */
+ p = qpack_encode_status_code (p, control_data->sc);
+
+ /* server name */
+ p = qpack_encode_header (p, HTTP_HEADER_SERVER, control_data->server_name,
+ control_data->server_name_len);
+
+ /* date */
+ p = qpack_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 = qpack_encode_content_len (p, control_data->content_len);
+
+ if (!app_headers_len)
+ {
+ *dst = p;
+ return;
+ }
+
+ end = app_headers + app_headers_len;
+ while (app_headers < end)
+ {
+ /* custom header name? */
+ u32 *tmp = (u32 *) app_headers;
+ if (PREDICT_FALSE (*tmp & HTTP_CUSTOM_HEADER_NAME_BIT))
+ {
+ http_custom_token_t *name, *value;
+ name = (http_custom_token_t *) app_headers;
+ u32 name_len = name->len & ~HTTP_CUSTOM_HEADER_NAME_BIT;
+ app_headers += sizeof (http_custom_token_t) + name_len;
+ value = (http_custom_token_t *) app_headers;
+ app_headers += sizeof (http_custom_token_t) + value->len;
+ p = qpack_encode_custom_header (p, name->token, name_len,
+ value->token, value->len);
+ }
+ else
+ {
+ http_app_header_t *header;
+ header = (http_app_header_t *) app_headers;
+ app_headers += sizeof (http_app_header_t) + header->value.len;
+ p = qpack_encode_header (p, header->name, header->value.token,
+ header->value.len);
+ }
+ }
+
+ *dst = p;
+}
http_field_line_t **headers,
qpack_decoder_ctx_t *decoder_ctx);
+/**
+ * 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 qpack_serialize_response (u8 *app_headers, u32 app_headers_len,
+ hpack_response_control_data_t *control_data,
+ u8 **dst);
+
#endif /* SRC_PLUGINS_HTTP_QPACK_H_ */
vec_len (buf));
vec_free (buf);
+ vlib_cli_output (vm, "qpack_serialize_response");
+
+ static void (*_qpack_serialize_response) (
+ u8 * app_headers, u32 app_headers_len,
+ hpack_response_control_data_t * control_data, u8 * *dst);
+ _qpack_serialize_response =
+ vlib_get_plugin_symbol ("http_plugin.so", "qpack_serialize_response");
+
+ u8 *server_name = format (0, "http unit tests");
+ u8 *date = format (0, "Mon, 21 Oct 2013 20:13:21 GMT");
+
+ memset (&resp_control_data, 0, sizeof (resp_control_data));
+ vec_validate_init_empty (buf, 127, 0xFF);
+ vec_reset_length (buf);
+ resp_control_data.sc = HTTP_STATUS_GATEWAY_TIMEOUT;
+ resp_control_data.server_name = server_name;
+ resp_control_data.server_name_len = vec_len (server_name);
+ resp_control_data.date = date;
+ resp_control_data.date_len = vec_len (date);
+ u8 expected4[] =
+ "\x00\x00\x5F\x09\x03\x35\x30\x34\x5F\x4D\x8B\x9D\x29\xAD\x4B\x6A\x32\x54"
+ "\x49\x50\x94\x7F\x56\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95"
+ "\x04\x0B\x81\x66\xE0\x82\xA6\x2D\x1B\xFF\xC4";
+ _qpack_serialize_response (0, 0, &resp_control_data, &buf);
+ HTTP_TEST ((vec_len (buf) == (sizeof (expected4) - 1) &&
+ !memcmp (buf, expected4, vec_len (buf))),
+ "response encoded as: %U", format_hex_bytes, buf, vec_len (buf));
+ vec_free (buf);
+
+ resp_control_data.sc = HTTP_STATUS_OK;
+ resp_control_data.content_len = 1024;
+ http_headers_ctx_t headers_ctx;
+ u8 *headers_buf = 0;
+ vec_validate_init_empty (headers_buf, 127, 0xFF);
+ http_init_headers_ctx (&headers_ctx, headers_buf, vec_len (headers_buf));
+ http_add_header (&headers_ctx, HTTP_HEADER_CONTENT_TYPE,
+ http_token_lit ("text/plain"));
+ http_add_header (&headers_ctx, HTTP_HEADER_CACHE_STATUS,
+ http_token_lit ("ExampleCache; hit"));
+ http_add_custom_header (&headers_ctx, http_token_lit ("sandwich"),
+ http_token_lit ("spam"));
+ u8 expected5[] =
+ "\x00\x00\xD9\x5F\x4D\x8B\x9D\x29\xAD\x4B\x6A\x32\x54\x49\x50\x94\x7F\x56"
+ "\x96\xD0\x7A\xBE\x94\x10\x54\xD4\x44\xA8\x20\x05\x95\x04\x0B\x81\x66\xE0"
+ "\x82\xA6\x2D\x1B\xFF\x54\x83\x08\x04\xD7\xF5\x2F\x01\x20\xC9\x39\x56\x42"
+ "\x46\x9B\x51\x8D\xC1\xE4\x74\xD7\x41\x6F\x0C\x93\x97\xED\x49\xCC\x9F\x2E"
+ "\x40\xEA\x93\xC1\x89\x3F\x83\x45\x63\xA7";
+ _qpack_serialize_response (headers_buf, headers_ctx.tail_offset,
+ &resp_control_data, &buf);
+ HTTP_TEST ((vec_len (buf) == (sizeof (expected5) - 1) &&
+ !memcmp (buf, expected5, vec_len (buf))),
+ "response encoded as: %U", format_hex_bytes, buf, vec_len (buf));
+ vec_free (headers_buf);
+ vec_free (buf);
+ vec_free (server_name);
+ vec_free (date);
+
return 0;
}