http: PING frame support 40/42740/2
authorMatus Fabian <matfabia@cisco.com>
Tue, 8 Apr 2025 17:49:11 +0000 (13:49 -0400)
committerFlorin Coras <florin.coras@gmail.com>
Wed, 9 Apr 2025 05:58:45 +0000 (05:58 +0000)
Type: improvement

Change-Id: I932c97e4b9fc8d614ae77c3c949f04687a4f29ad
Signed-off-by: Matus Fabian <matfabia@cisco.com>
src/plugins/http/http2/frame.c
src/plugins/http/http2/frame.h
src/plugins/http/http2/hpack.c
src/plugins/http/http2/hpack.h
src/plugins/http/http2/http2.c

index a2028b8..c9c1931 100644 (file)
@@ -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;
index bfe4e12..53a37c1 100644 (file)
@@ -10,6 +10,7 @@
 #include <http/http2/http2.h>
 
 #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
  *
index f722f67..1a1ecd4 100644 (file)
@@ -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;
 }
index 4e254be..69144de 100644 (file)
@@ -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;
 
index b155a69..9e0bb06 100644 (file)
@@ -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 */