From: Matus Fabian Date: Thu, 25 Sep 2025 15:14:30 +0000 (-0400) Subject: http: QPACK decoding request and response X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F53%2F43753%2F3;p=vpp.git http: QPACK decoding request and response Type: feature Change-Id: Ica48ee1bf432a88529c41b166f5a883e2a432677 Signed-off-by: Matus Fabian --- diff --git a/src/plugins/http/http3/qpack.c b/src/plugins/http/http3/qpack.c index ae7b4427e2d..e0c9377e9b7 100644 --- a/src/plugins/http/http3/qpack.c +++ b/src/plugins/http/http3/qpack.c @@ -280,3 +280,81 @@ qpack_decode_header (u8 **src, u8 *end, u8 **buf, uword *buf_len, *src = p; return HPACK_ERROR_NONE; } + +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, + [HPACK_ERROR_PROTOCOL] = HTTP3_ERROR_GENERAL_PROTOCOL_ERROR, + [HPACK_ERROR_UNKNOWN] = HTTP3_ERROR_INTERNAL_ERROR, +}; + +static inline hpack_error_t +qpack_parse_headers_prefix (u8 **src, u8 *end, qpack_decoder_ctx_t *ctx) +{ + u8 *p; + + ASSERT (*src < end); + p = *src; + + ctx->req_insert_count = hpack_decode_int (&p, end, 8); + if (ctx->req_insert_count == HPACK_INVALID_INT || p == end) + return HPACK_ERROR_COMPRESSION; + + ctx->delta_base_sign = *p & 0x80; + ctx->delta_base = hpack_decode_int (&p, end, 7); + if (ctx->req_insert_count == HPACK_INVALID_INT) + return HPACK_ERROR_COMPRESSION; + + *src = p; + return HPACK_ERROR_NONE; +} + +__clib_export http3_error_t +qpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len, + hpack_request_control_data_t *control_data, + http_field_line_t **headers, + qpack_decoder_ctx_t *decoder_ctx) +{ + hpack_error_t rv; + u8 *p, *end; + + if (src_len < 3) + return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED; + + p = src; + end = src + src_len; + + /* encoded field section prefix */ + rv = qpack_parse_headers_prefix (&p, end, decoder_ctx); + if (rv || p == end) + return hpack_error_to_http3_error[rv]; + + rv = hpack_decode_request (p, end, dst, dst_len, control_data, headers, + decoder_ctx, (void *) qpack_decode_header); + return hpack_error_to_http3_error[rv]; +} + +__clib_export http3_error_t +qpack_parse_response (u8 *src, u32 src_len, u8 *dst, u32 dst_len, + hpack_response_control_data_t *control_data, + http_field_line_t **headers, + qpack_decoder_ctx_t *decoder_ctx) +{ + hpack_error_t rv; + u8 *p, *end; + + if (src_len < 3) + return HTTP3_ERROR_QPACK_DECOMPRESSION_FAILED; + + p = src; + end = src + src_len; + + /* encoded field section prefix */ + rv = qpack_parse_headers_prefix (&p, end, decoder_ctx); + if (rv || p == end) + return hpack_error_to_http3_error[rv]; + + rv = hpack_decode_response (p, end, dst, dst_len, control_data, headers, + decoder_ctx, qpack_decode_header); + return hpack_error_to_http3_error[rv]; +} diff --git a/src/plugins/http/http3/qpack.h b/src/plugins/http/http3/qpack.h index c24e933a6b0..32c1ceaff6a 100644 --- a/src/plugins/http/http3/qpack.h +++ b/src/plugins/http/http3/qpack.h @@ -5,7 +5,52 @@ #ifndef SRC_PLUGINS_HTTP_QPACK_H_ #define SRC_PLUGINS_HTTP_QPACK_H_ -#include #include +#include +#include + +typedef struct +{ + uword req_insert_count; + uword delta_base; + u8 delta_base_sign; +} qpack_decoder_ctx_t; + +/** + * Request parser + * + * @param src Header block to parse + * @param src_len Length of header block + * @param dst Buffer where headers will be decoded + * @param dst_len Length of buffer for decoded headers + * @param control_data Preparsed pseudo-headers + * @param headers List of regular headers + * @param decoder_ctx Decoder context + * + * @return @c HTTP3_ERROR_NO_ERROR on success + */ +http3_error_t qpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len, + hpack_request_control_data_t *control_data, + http_field_line_t **headers, + qpack_decoder_ctx_t *decoder_ctx); + +/** + * Response parser + * + * @param src Header block to parse + * @param src_len Length of header block + * @param dst Buffer where headers will be decoded + * @param dst_len Length of buffer for decoded headers + * @param control_data Preparsed pseudo-headers + * @param headers List of regular headers + * @param decoder_ctx Decoder context + * + * @return @c HTTP3_ERROR_NO_ERROR on success + */ +http3_error_t +qpack_parse_response (u8 *src, u32 src_len, u8 *dst, u32 dst_len, + hpack_response_control_data_t *control_data, + http_field_line_t **headers, + qpack_decoder_ctx_t *decoder_ctx); #endif /* SRC_PLUGINS_HTTP_QPACK_H_ */ diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c index 406662dbe9a..11f5823c589 100644 --- a/src/plugins/http/test/http_test.c +++ b/src/plugins/http/test/http_test.c @@ -1657,6 +1657,71 @@ http_test_qpack (vlib_main_t *vm) /* literal field line with literal name */ TEST ("\x23\x61\x62\x63\x01\x5A", "abc", "Z"); + vlib_cli_output (vm, "qpack_parse_request"); + + static http3_error_t (*_qpack_parse_request) ( + u8 * src, u32 src_len, u8 * dst, u32 dst_len, + hpack_request_control_data_t * control_data, http_field_line_t * *headers, + qpack_decoder_ctx_t * decoder_ctx); + + _qpack_parse_request = + vlib_get_plugin_symbol ("http_plugin.so", "qpack_parse_request"); + + http3_error_t err; + http_field_line_t *headers = 0; + qpack_decoder_ctx_t decoder_ctx = {}; + hpack_request_control_data_t req_control_data = {}; + u8 req[] = { 0x00, 0x00, 0xD1, 0xD7, 0x50, 0x01, 0x61, + 0xC1, 0x23, 0x61, 0x62, 0x63, 0x01, 0x5A }; + u16 parsed_bitmap = + HPACK_PSEUDO_HEADER_METHOD_PARSED | HPACK_PSEUDO_HEADER_SCHEME_PARSED | + HPACK_PSEUDO_HEADER_PATH_PARSED | HPACK_PSEUDO_HEADER_AUTHORITY_PARSED; + + vec_validate_init_empty (buf, 254, 0); + err = _qpack_parse_request (req, 14, buf, vec_len (buf), &req_control_data, + &headers, &decoder_ctx); + HTTP_TEST ( + (err == HTTP3_ERROR_NO_ERROR && + req_control_data.parsed_bitmap == parsed_bitmap && + req_control_data.method == HTTP_REQ_GET && + req_control_data.scheme == HTTP_URL_SCHEME_HTTPS && + req_control_data.path_len == 1 && req_control_data.path[0] == '/' && + req_control_data.authority_len == 1 && + req_control_data.authority[0] == 'a' && vec_len (headers) == 1 && + req_control_data.headers_len == 4 && headers[0].name_len == 3 && + headers[0].value_len == 1 && + !memcmp (req_control_data.headers + headers[0].name_offset, "abc", 3) && + !memcmp (req_control_data.headers + headers[0].value_offset, "Z", 1)), + "decoder result: %U", format_http3_error, err); + + vec_free (buf); + vec_free (headers); + memset (&decoder_ctx, 0, sizeof (decoder_ctx)); + + vlib_cli_output (vm, "qpack_parse_response"); + + static http3_error_t (*_qpack_parse_response) ( + u8 * src, u32 src_len, u8 * dst, u32 dst_len, + hpack_response_control_data_t * control_data, http_field_line_t * *headers, + qpack_decoder_ctx_t * decoder_ctx); + + _qpack_parse_response = + vlib_get_plugin_symbol ("http_plugin.so", "qpack_parse_response"); + + hpack_response_control_data_t resp_control_data = {}; + u8 resp[] = { 0x00, 0x00, 0xD9 }; + + vec_validate_init_empty (buf, 63, 0); + err = _qpack_parse_response (resp, 3, buf, vec_len (buf), &resp_control_data, + &headers, &decoder_ctx); + HTTP_TEST ( + (err == HTTP3_ERROR_NO_ERROR && + resp_control_data.parsed_bitmap == HPACK_PSEUDO_HEADER_STATUS_PARSED && + resp_control_data.sc == HTTP_STATUS_OK && vec_len (headers) == 0), + "decoder result: %U", format_http3_error, err); + vec_free (buf); + vec_free (headers); + return 0; }