From aef76197ef6b13dbdc5df3f865f36e0f1e05f32f Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Tue, 8 Apr 2025 13:49:11 -0400 Subject: [PATCH] http: PING frame support Type: improvement Change-Id: I932c97e4b9fc8d614ae77c3c949f04687a4f29ad Signed-off-by: Matus Fabian --- src/plugins/http/http2/frame.c | 20 ++++++++++++++++++-- src/plugins/http/http2/frame.h | 11 +++++++++++ src/plugins/http/http2/hpack.c | 2 +- src/plugins/http/http2/hpack.h | 1 + src/plugins/http/http2/http2.c | 36 ++++++++++++++++++++++++++++-------- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/plugins/http/http2/frame.c b/src/plugins/http/http2/frame.c index a2028b87e95..c9c1931f02a 100644 --- a/src/plugins/http/http2/frame.c +++ b/src/plugins/http/http2/frame.c @@ -249,6 +249,22 @@ http2_frame_write_goaway (http2_error_t error_code, u32 last_stream_id, /* TODO: Additional Debug Data */ } +void +http2_frame_write_ping (u8 is_resp, u8 *payload, u8 **dst) +{ + u8 *p; + http2_frame_header_t fh = { + .type = HTTP2_FRAME_TYPE_PING, + .length = HTTP2_PING_PAYLOAD_LEN, + .flags = is_resp ? HTTP2_FRAME_FLAG_ACK : 0, + }; + + p = http2_frame_header_alloc (dst); + http2_frame_header_write (&fh, p); + vec_add2 (*dst, p, HTTP2_PING_PAYLOAD_LEN); + clib_memcpy_fast (p, payload, HTTP2_PING_PAYLOAD_LEN); +} + #define PRIORITY_DATA_LEN 5 __clib_export http2_error_t @@ -262,7 +278,7 @@ http2_frame_read_headers (u8 **headers, u32 *headers_len, u8 *payload, u8 pad_len = *payload++; if ((u32) pad_len >= payload_len) return HTTP2_ERROR_PROTOCOL_ERROR; - *headers_len -= pad_len; + *headers_len -= (pad_len + 1); } if (flags & HTTP2_FRAME_FLAG_PRIORITY) @@ -303,7 +319,7 @@ http2_frame_read_data (u8 **data, u32 *data_len, u8 *payload, u32 payload_len, u8 pad_len = *payload++; if ((u32) pad_len >= payload_len) return HTTP2_ERROR_PROTOCOL_ERROR; - *data_len -= pad_len; + *data_len -= (pad_len + 1); } *data = payload; diff --git a/src/plugins/http/http2/frame.h b/src/plugins/http/http2/frame.h index bfe4e122f0d..53a37c1aa0a 100644 --- a/src/plugins/http/http2/frame.h +++ b/src/plugins/http/http2/frame.h @@ -10,6 +10,7 @@ #include #define HTTP2_FRAME_HEADER_SIZE 9 +#define HTTP2_PING_PAYLOAD_LEN 8 #define foreach_http2_frame_type \ _ (0x00, DATA, "DATA") \ @@ -172,6 +173,7 @@ http2_error_t http2_frame_read_goaway (u32 *last_stream_id, u32 *error_code, /** * Write GOAWAY frame to the end of given vector + * * @param error_code Error code * @param last_stream_id Last stream ID * @param dst Vector where GOAWAY frame will be written @@ -179,6 +181,15 @@ http2_error_t http2_frame_read_goaway (u32 *last_stream_id, u32 *error_code, void http2_frame_write_goaway (http2_error_t error_code, u32 last_stream_id, u8 **dst); +/** + * Write PING frame to the end of given vector + * + * @param is_resp Indicate that this is PING response + * @param payload Payload to parse + * @param dst Vector where GOAWAY frame will be written + */ +void http2_frame_write_ping (u8 is_resp, u8 *payload, u8 **dst); + /** * Parse HEADERS frame payload * diff --git a/src/plugins/http/http2/hpack.c b/src/plugins/http/http2/hpack.c index f722f67c3f8..1a1ecd43cf3 100644 --- a/src/plugins/http/http2/hpack.c +++ b/src/plugins/http/http2/hpack.c @@ -913,7 +913,7 @@ hpack_parse_request (u8 *src, u32 src_len, u8 *dst, u32 dst_len, control_data->content_len_header_index = header - *headers; } } - + control_data->control_data_len = dst_len - b_left; HTTP_DBG (2, "%U", format_hpack_dynamic_table, dynamic_table); return HTTP2_ERROR_NO_ERROR; } diff --git a/src/plugins/http/http2/hpack.h b/src/plugins/http/http2/hpack.h index 4e254be69a7..69144de133a 100644 --- a/src/plugins/http/http2/hpack.h +++ b/src/plugins/http/http2/hpack.h @@ -56,6 +56,7 @@ typedef struct u8 *headers; uword content_len_header_index; u32 headers_len; + u32 control_data_len; u16 parsed_bitmap; } hpack_request_control_data_t; diff --git a/src/plugins/http/http2/http2.c b/src/plugins/http/http2/http2.c index b155a69a2b0..9e0bb06f477 100644 --- a/src/plugins/http/http2/http2.c +++ b/src/plugins/http/http2/http2.c @@ -125,8 +125,9 @@ http2_conn_ctx_free (http_conn_t *hc) h2c = http2_conn_ctx_get_w_thread (hc); HTTP_DBG (1, "h2c [%u]%x", hc->c_thread_index, h2c - h2m->conn_pool[hc->c_thread_index]); - hpack_dynamic_table_free (&h2c->decoder_dynamic_table); hash_free (h2c->req_by_stream_id); + if (hc->flags & HTTP_CONN_F_HAS_REQUEST) + hpack_dynamic_table_free (&h2c->decoder_dynamic_table); if (CLIB_DEBUG) memset (h2c, 0xba, sizeof (*h2c)); pool_put (h2m->conn_pool[hc->c_thread_index], h2c); @@ -314,6 +315,7 @@ http2_req_state_wait_transport_method (http_conn_t *hc, http2_req_t *req, h2c = http2_conn_ctx_get_w_thread (hc); + /* TODO: configurable buf size with bigger default value */ vec_validate_init_empty (buf, 1023, 0); *error = hpack_parse_request (req->payload, req->payload_len, buf, 1023, &control_data, &req->base.headers, @@ -365,6 +367,7 @@ http2_req_state_wait_transport_method (http_conn_t *hc, http2_req_t *req, return HTTP_SM_STOP; } + req->base.control_data_len = control_data.control_data_len; req->base.headers_offset = control_data.headers - buf; req->base.headers_len = control_data.headers_len; if (control_data.content_len_header_index != ~0) @@ -379,6 +382,8 @@ http2_req_state_wait_transport_method (http_conn_t *hc, http2_req_t *req, } new_state = HTTP_REQ_STATE_TRANSPORT_IO_MORE_DATA; } + /* TODO: handle following case (for now we just discard data frames) + * req->base.body_len == 0 && req->stream_state == HTTP2_STREAM_STATE_OPEN */ req->base.to_recv = req->base.body_len; req->base.target_path_len = control_data.path_len; @@ -390,9 +395,6 @@ http2_req_state_wait_transport_method (http_conn_t *hc, http2_req_t *req, req->base.target_query_len = 0; http_identify_optional_query (&req->base, buf); - req->base.control_data_len = - req->base.headers_offset + control_data.headers_len; - msg.type = HTTP_MSG_REQUEST; msg.method_type = control_data.method; msg.data.type = HTTP_MSG_DATA_INLINE; @@ -709,9 +711,7 @@ http2_handle_data_frame (http_conn_t *hc, http2_frame_header_t *fh) h2c = http2_conn_ctx_get_w_thread (hc); if (fh->stream_id <= h2c->last_opened_stream_id) { - /* we reset stream, but peer might send something meanwhile */ HTTP_DBG (1, "stream closed, ignoring frame"); - http2_stream_error (hc, req, HTTP2_ERROR_STREAM_CLOSED, 0); return HTTP2_ERROR_NO_ERROR; } else @@ -885,6 +885,27 @@ http2_handle_goaway_frame (http_conn_t *hc, http2_frame_header_t *fh) return HTTP2_ERROR_NO_ERROR; } +static http2_error_t +http2_handle_ping_frame (http_conn_t *hc, http2_frame_header_t *fh) +{ + u8 *rx_buf, *resp = 0; + + if (fh->stream_id != 0 || fh->length != HTTP2_PING_PAYLOAD_LEN || + fh->flags & HTTP2_FRAME_FLAG_ACK) + return HTTP2_ERROR_PROTOCOL_ERROR; + + rx_buf = http_get_rx_buf (hc); + vec_validate (rx_buf, fh->length - 1); + http_io_ts_read (hc, rx_buf, fh->length, 0); + + http2_frame_write_ping (1, rx_buf, &resp); + http_io_ts_write (hc, resp, vec_len (resp), 0); + vec_free (resp); + http_io_ts_after_write (hc, 0, 1, 1); + + return HTTP2_ERROR_NO_ERROR; +} + static_always_inline int http2_expect_preface (http_conn_t *hc, http2_conn_ctx_t *h2c) { @@ -1150,8 +1171,7 @@ http2_transport_rx_callback (http_conn_t *hc) rv = http2_handle_goaway_frame (hc, &fh); break; case HTTP2_FRAME_TYPE_PING: - /* TODO */ - rv = HTTP2_ERROR_INTERNAL_ERROR; + rv = http2_handle_ping_frame (hc, &fh); break; case HTTP2_FRAME_TYPE_CONTINUATION: /* TODO */ -- 2.16.6