http: http2 frames 43/42443/5
authorMatus Fabian <[email protected]>
Fri, 28 Feb 2025 15:15:15 +0000 (10:15 -0500)
committerMatus Fabian <[email protected]>
Mon, 3 Mar 2025 21:07:10 +0000 (16:07 -0500)
Type: feature

Change-Id: Id2c6f6b5747c1f676048642e277eb66850f728b7
Signed-off-by: Matus Fabian <[email protected]>
src/plugins/http/CMakeLists.txt
src/plugins/http/http2/frame.c [new file with mode: 0644]
src/plugins/http/http2/frame.h [new file with mode: 0644]
src/plugins/http/http2/http2.h
src/plugins/http/test/http_test.c

index 95b5102..58cb4c0 100644 (file)
@@ -14,6 +14,7 @@
 add_vpp_plugin(http
   SOURCES
   http2/hpack.c
+  http2/frame.c
   http.c
   http_buffer.c
   http_timer.c
diff --git a/src/plugins/http/http2/frame.c b/src/plugins/http/http2/frame.c
new file mode 100644 (file)
index 0000000..577bb6c
--- /dev/null
@@ -0,0 +1,323 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#include <vppinfra/string.h>
+#include <http/http2/frame.h>
+
+#define MAX_U24 0xFFFFFF
+
+static_always_inline u8 *
+http2_decode_u24 (u8 *src, u32 *value)
+{
+  *value = 0;
+  *value = (u32) (src[0] << 16) | (u32) (src[1] << 8) | (u32) src[2];
+  return src + 3;
+}
+
+static_always_inline u8 *
+http2_encode_u24 (u8 *dst, u32 value)
+{
+  ASSERT (value <= MAX_U24);
+  *dst++ = (value >> 16) & 0xFF;
+  *dst++ = (value >> 8) & 0xFF;
+  *dst++ = value & 0xFF;
+  return dst;
+}
+
+/*
+ * RFC9113 section 4.1
+ *
+ * HTTP Frame {
+ *   Length (24),
+ *   Type (8),
+ *   Flags (8),
+ *   Reserved (1),
+ *   Stream Identifier (31),
+ *   Frame Payload (..),
+ * }
+ */
+
+__clib_export void
+http2_frame_header_read (u8 *src, http2_frame_header_t *fh)
+{
+  u32 *stream_id;
+  src = http2_decode_u24 (src, &fh->length);
+  fh->type = *src++;
+  fh->flags = *src++;
+  stream_id = (u32 *) src;
+  fh->stream_id = clib_net_to_host_u32 (*stream_id) & 0x7FFFFFFF;
+}
+
+static void
+http2_frame_header_write (http2_frame_header_t *fh, u8 *dst)
+{
+  u32 stream_id;
+
+  dst = http2_encode_u24 (dst, fh->length);
+  *dst++ = fh->type;
+  *dst++ = fh->flags;
+  stream_id = clib_host_to_net_u32 (fh->stream_id);
+  clib_memcpy_fast (dst, &stream_id, sizeof (stream_id));
+}
+
+__clib_export http2_error_t
+http2_frame_read_settings (http2_conn_settings_t *settings, u8 *payload,
+                          u32 payload_len)
+{
+  http2_settings_entry_t *entry;
+  u32 value;
+
+  while (payload_len >= sizeof (*entry))
+    {
+      entry = (http2_settings_entry_t *) payload;
+      switch (clib_net_to_host_u16 (entry->identifier))
+       {
+#define _(v, label, member, min, max, default_value, err_code)                \
+  case HTTP2_SETTINGS_##label:                                                \
+    value = clib_net_to_host_u32 (entry->value);                              \
+    if (!(value >= min && value <= max))                                      \
+      return err_code;                                                        \
+    settings->member = value;                                                 \
+    break;
+         foreach_http2_settings
+#undef _
+           /* ignore unknown or unsupported identifier */
+           default : break;
+       }
+      payload_len -= sizeof (*entry);
+      payload += sizeof (*entry);
+    }
+
+  if (payload_len != 0)
+    return HTTP2_ERROR_FRAME_SIZE_ERROR;
+
+  return HTTP2_ERROR_NO_ERROR;
+}
+
+__clib_export void
+http2_frame_write_settings_ack (u8 **dst)
+{
+  http2_frame_header_t fh = { .flags = HTTP2_FRAME_FLAG_ACK,
+                             .type = HTTP2_FRAME_TYPE_SETTINGS };
+  u8 *p = http2_frame_header_alloc (dst);
+  http2_frame_header_write (&fh, p);
+}
+
+__clib_export void
+http2_frame_write_settings (http2_settings_entry_t *settings, u8 **dst)
+{
+  u8 *p;
+  u32 length;
+  http2_settings_entry_t *entry, e;
+
+  ASSERT (settings);
+  ASSERT (vec_len (settings) > 0);
+
+  length = vec_len (settings) * sizeof (*entry);
+  http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_SETTINGS,
+                             .length = length };
+  p = http2_frame_header_alloc (dst);
+  http2_frame_header_write (&fh, p);
+
+  vec_add2 (*dst, p, length);
+  vec_foreach (entry, settings)
+    {
+      e.identifier = clib_host_to_net_u16 (entry->identifier);
+      e.value = clib_host_to_net_u32 (entry->value);
+      clib_memcpy_fast (p, &e, sizeof (e));
+      p += sizeof (e);
+    }
+}
+
+#define WINDOW_UPDATE_LENGTH 4
+
+__clib_export http2_error_t
+http2_frame_read_window_update (u32 *increment, u8 *payload, u32 payload_len)
+{
+  u32 *value;
+
+  if (payload_len != WINDOW_UPDATE_LENGTH)
+    return HTTP2_ERROR_FRAME_SIZE_ERROR;
+
+  value = (u32 *) payload;
+
+  if (value == 0)
+    return HTTP2_ERROR_PROTOCOL_ERROR;
+
+  *increment = clib_net_to_host_u32 (*value) & 0x7FFFFFFF;
+  return HTTP2_ERROR_NO_ERROR;
+}
+
+__clib_export void
+http2_frame_write_window_update (u32 increment, u32 stream_id, u8 **dst)
+{
+  u8 *p;
+  u32 value;
+
+  ASSERT (increment > 0 && increment <= 0x7FFFFFFF);
+
+  http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_WINDOW_UPDATE,
+                             .length = WINDOW_UPDATE_LENGTH,
+                             .stream_id = stream_id };
+  p = http2_frame_header_alloc (dst);
+  http2_frame_header_write (&fh, p);
+
+  vec_add2 (*dst, p, WINDOW_UPDATE_LENGTH);
+  value = clib_host_to_net_u32 (increment);
+  clib_memcpy_fast (p, &value, WINDOW_UPDATE_LENGTH);
+}
+
+#define RST_STREAM_LENGTH 4
+
+__clib_export http2_error_t
+http2_frame_read_rst_stream (u32 *error_code, u8 *payload, u32 payload_len)
+{
+  u32 *value;
+
+  if (payload_len != RST_STREAM_LENGTH)
+    return HTTP2_ERROR_FRAME_SIZE_ERROR;
+
+  value = (u32 *) payload;
+
+  *error_code = clib_net_to_host_u32 (*value);
+  return HTTP2_ERROR_NO_ERROR;
+}
+
+__clib_export void
+http2_frame_write_rst_stream (http2_error_t error_code, u32 stream_id,
+                             u8 **dst)
+{
+  u8 *p;
+  u32 value;
+
+  ASSERT (stream_id > 0 && stream_id <= 0x7FFFFFFF);
+
+  http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_RST_STREAM,
+                             .length = RST_STREAM_LENGTH,
+                             .stream_id = stream_id };
+  p = http2_frame_header_alloc (dst);
+  http2_frame_header_write (&fh, p);
+
+  vec_add2 (*dst, p, RST_STREAM_LENGTH);
+  value = clib_host_to_net_u32 ((u32) error_code);
+  clib_memcpy_fast (p, &value, RST_STREAM_LENGTH);
+}
+
+#define GOAWAY_MIN_SIZE 8
+
+__clib_export http2_error_t
+http2_frame_read_goaway (u32 *error_code, u32 *last_stream_id, u8 *payload,
+                        u32 payload_len)
+{
+  u32 *value;
+
+  if (payload_len < GOAWAY_MIN_SIZE)
+    return HTTP2_ERROR_FRAME_SIZE_ERROR;
+
+  value = (u32 *) payload;
+  *last_stream_id = clib_net_to_host_u32 (*value) & 0x7FFFFFFF;
+  payload += 4;
+
+  value = (u32 *) payload;
+  *error_code = clib_net_to_host_u32 (*value);
+
+  /* TODO: Additional Debug Data */
+  return HTTP2_ERROR_NO_ERROR;
+}
+
+__clib_export void
+http2_frame_write_goaway (http2_error_t error_code, u32 last_stream_id,
+                         u8 **dst)
+{
+  u8 *p;
+  u32 value;
+
+  ASSERT (last_stream_id > 0 && last_stream_id <= 0x7FFFFFFF);
+
+  http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_GOAWAY,
+                             .length = GOAWAY_MIN_SIZE };
+  p = http2_frame_header_alloc (dst);
+  http2_frame_header_write (&fh, p);
+
+  vec_add2 (*dst, p, GOAWAY_MIN_SIZE);
+  value = clib_host_to_net_u32 (last_stream_id);
+  clib_memcpy_fast (p, &value, 4);
+  p += 4;
+  value = clib_host_to_net_u32 ((u32) error_code);
+  clib_memcpy_fast (p, &value, 4);
+  /* TODO: Additional Debug Data */
+}
+
+#define PRIORITY_DATA_LEN 5
+
+__clib_export http2_error_t
+http2_frame_read_headers (u8 **headers, u32 *headers_len, u8 *payload,
+                         u32 payload_len, u8 flags)
+{
+  *headers_len = payload_len;
+
+  if (flags & HTTP2_FRAME_FLAG_PADED)
+    {
+      u8 pad_len = *payload++;
+      if ((u32) pad_len >= payload_len)
+       return HTTP2_ERROR_PROTOCOL_ERROR;
+      *headers_len -= pad_len;
+    }
+
+  if (flags & HTTP2_FRAME_FLAG_PRIORITY)
+    {
+      if (*headers_len <= PRIORITY_DATA_LEN)
+       return HTTP2_ERROR_FRAME_SIZE_ERROR;
+      /* just skip, priority scheme defined in RFC7540 is deprecated */
+      *headers_len -= PRIORITY_DATA_LEN;
+      payload += PRIORITY_DATA_LEN;
+    }
+
+  *headers = payload;
+
+  return HTTP2_ERROR_NO_ERROR;
+}
+
+__clib_export void
+http2_frame_write_headers_header (u32 headers_len, u32 stream_id, u8 flags,
+                                 u8 *dst)
+{
+  ASSERT (stream_id > 0 && stream_id <= 0x7FFFFFFF);
+
+  http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_HEADERS,
+                             .length = headers_len,
+                             .flags = flags,
+                             .stream_id = stream_id };
+  http2_frame_header_write (&fh, dst);
+}
+
+__clib_export http2_error_t
+http2_frame_read_data (u8 **data, u32 *data_len, u8 *payload, u32 payload_len,
+                      u8 flags)
+{
+  *data_len = payload_len;
+
+  if (flags & HTTP2_FRAME_FLAG_PADED)
+    {
+      u8 pad_len = *payload++;
+      if ((u32) pad_len >= payload_len)
+       return HTTP2_ERROR_PROTOCOL_ERROR;
+      *data_len -= pad_len;
+    }
+
+  *data = payload;
+  return HTTP2_ERROR_NO_ERROR;
+}
+
+__clib_export void
+http2_frame_write_data_header (u32 data_len, u32 stream_id, u8 flags, u8 *dst)
+{
+  ASSERT (stream_id > 0 && stream_id <= 0x7FFFFFFF);
+
+  http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_DATA,
+                             .length = data_len,
+                             .flags = flags,
+                             .stream_id = stream_id };
+  http2_frame_header_write (&fh, dst);
+}
diff --git a/src/plugins/http/http2/frame.h b/src/plugins/http/http2/frame.h
new file mode 100644 (file)
index 0000000..bfe4e12
--- /dev/null
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright(c) 2025 Cisco Systems, Inc.
+ */
+
+#ifndef SRC_PLUGINS_HTTP_HTTP2_FRAME_H_
+#define SRC_PLUGINS_HTTP_HTTP2_FRAME_H_
+
+#include <vppinfra/error.h>
+#include <vppinfra/types.h>
+#include <http/http2/http2.h>
+
+#define HTTP2_FRAME_HEADER_SIZE 9
+
+#define foreach_http2_frame_type                                              \
+  _ (0x00, DATA, "DATA")                                                      \
+  _ (0x01, HEADERS, "HEADERS")                                                \
+  _ (0x02, PRIORITY, "PRIORITY")                                              \
+  _ (0x03, RST_STREAM, "RST_STREAM")                                          \
+  _ (0x04, SETTINGS, "SETTINGS")                                              \
+  _ (0x05, PUSH_PROMISE, "PUSH_PROMISE")                                      \
+  _ (0x06, PING, "PING")                                                      \
+  _ (0x07, GOAWAY, "GOAWAY")                                                  \
+  _ (0x08, WINDOW_UPDATE, "WINDOW_UPDATE")                                    \
+  _ (0x09, CONTINUATION, "CONTINUATION")
+
+typedef enum
+{
+#define _(v, n, s) HTTP2_FRAME_TYPE_##n = v,
+  foreach_http2_frame_type
+#undef _
+} __clib_packed http2_frame_type_t;
+
+STATIC_ASSERT_SIZEOF (http2_frame_type_t, 1);
+
+#define foreach_http2_frame_flag                                              \
+  _ (0, NONE)                                                                 \
+  _ (1, END_STREAM)                                                           \
+  _ (1, ACK)                                                                  \
+  _ (1 << 2, END_HEADERS)                                                     \
+  _ (1 << 3, PADED)                                                           \
+  _ (1 << 5, PRIORITY)
+
+typedef enum
+{
+#define _(v, n) HTTP2_FRAME_FLAG_##n = v,
+  foreach_http2_frame_flag
+#undef _
+} __clib_packed http2_frame_flag_t;
+
+STATIC_ASSERT_SIZEOF (http2_frame_flag_t, 1);
+
+typedef struct
+{
+  u32 length;
+  http2_frame_type_t type;
+  u8 flags;
+  u32 stream_id;
+} http2_frame_header_t;
+
+typedef struct
+{
+  u16 identifier;
+  u32 value;
+} __clib_packed http2_settings_entry_t;
+
+/**
+ * Parse frame header
+ *
+ * @param src Pointer to the beginning of the frame
+ * @param fh  Parsed frame header
+ */
+void http2_frame_header_read (u8 *src, http2_frame_header_t *fh);
+
+/**
+ * Add 9 bytes (frame header size) to the end of given vector
+ *
+ * @param dst Pointer to vector
+ *
+ * @return Pointer to the frame header beginning
+ */
+static_always_inline u8 *
+http2_frame_header_alloc (u8 **dst)
+{
+  u8 *p;
+
+  vec_add2 (*dst, p, HTTP2_FRAME_HEADER_SIZE);
+  return p;
+}
+
+/**
+ * Parse SETTINGS frame payload
+ *
+ * @param settings    Vector of HTTP/2 settings
+ * @param payload     Payload to parse
+ * @param payload_len Payload length
+ *
+ * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise
+ */
+http2_error_t http2_frame_read_settings (http2_conn_settings_t *settings,
+                                        u8 *payload, u32 payload_len);
+
+/**
+ * Write SETTINGS ACK frame to the end of given vector
+ *
+ * @param dst Vector where SETTINGS ACK frame will be written
+ */
+void http2_frame_write_settings_ack (u8 **dst);
+
+/**
+ * Write SETTINGS frame to the end of given vector
+ *
+ * @param settings Vector of HTTP/2 settings
+ * @param dst      Vector where SETTINGS frame will be written
+ */
+void http2_frame_write_settings (http2_settings_entry_t *settings, u8 **dst);
+
+/**
+ * Parse WINDOW_UPDATE frame payload
+ *
+ * @param increment   Parsed window increment value
+ * @param payload     Payload to parse
+ * @param payload_len Payload length
+ *
+ * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise
+ */
+http2_error_t http2_frame_read_window_update (u32 *increment, u8 *payload,
+                                             u32 payload_len);
+
+/**
+ * Write WINDOW_UPDATE frame to the end of given vector
+ *
+ * @param increment Window increment value
+ * @param stream_id Stream ID
+ * @param dst       Vector where WINDOW_UPDATE frame will be written
+ */
+void http2_frame_write_window_update (u32 increment, u32 stream_id, u8 **dst);
+
+/**
+ * Parse RST_STREAM frame payload
+ *
+ * @param error_code  Parsed error code
+ * @param payload     Payload to parse
+ * @param payload_len Payload length
+ *
+ * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise
+ */
+http2_error_t http2_frame_read_rst_stream (u32 *error_code, u8 *payload,
+                                          u32 payload_len);
+
+/**
+ * Write RST_STREAM frame to the end of given vector
+ *
+ * @param error_code Error code
+ * @param stream_id  Stream ID, except 0
+ * @param dst        Vector where RST_STREAM frame will be written
+ */
+void http2_frame_write_rst_stream (http2_error_t error_code, u32 stream_id,
+                                  u8 **dst);
+
+/**
+ * Parse GOAWAY frame payload
+ *
+ * @param last_stream_id Parsed last stream ID
+ * @param error_code     Parsed error code
+ * @param payload        Payload to parse
+ * @param payload_len    Payload length
+ *
+ * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise
+ */
+http2_error_t http2_frame_read_goaway (u32 *last_stream_id, u32 *error_code,
+                                      u8 *payload, u32 payload_len);
+
+/**
+ * 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
+ */
+void http2_frame_write_goaway (http2_error_t error_code, u32 last_stream_id,
+                              u8 **dst);
+
+/**
+ * Parse HEADERS frame payload
+ *
+ * @param headers     Pointer to header block fragment
+ * @param headers_len Header block fragment length
+ * @param payload     Payload to parse
+ * @param payload_len Payload length
+ * @param flags       Flag field of frame header
+ *
+ * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise
+ */
+http2_error_t http2_frame_read_headers (u8 **headers, u32 *headers_len,
+                                       u8 *payload, u32 payload_len,
+                                       u8 flags);
+
+/**
+ * Write HEADERS frame header
+ *
+ * @param headers_len Header block fragment length
+ * @param stream_id   Stream ID, except 0
+ * @param flags       Frame header flags
+ * @param dst         Pointer where frame header will be written
+ *
+ * @note Use @c http2_frame_header_alloc before
+ */
+void http2_frame_write_headers_header (u32 headers_len, u32 stream_id,
+                                      u8 flags, u8 *dst);
+
+/**
+ * Parse DATA frame payload
+ *
+ * @param headers     Pointer to data
+ * @param headers_len Data length
+ * @param payload     Payload to parse
+ * @param payload_len Payload length
+ * @param flags       Flag field of frame header
+ *
+ * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise
+ */
+http2_error_t http2_frame_read_data (u8 **data, u32 *data_len, u8 *payload,
+                                    u32 payload_len, u8 flags);
+
+/**
+ * Write DATA frame header
+ *
+ * @param data_len  Data length
+ * @param stream_id Stream ID, except 0
+ * @param flags     Frame header flags
+ * @param dst       Pointer where frame header will be written
+ */
+void http2_frame_write_data_header (u32 data_len, u32 stream_id, u8 flags,
+                                   u8 *dst);
+
+#endif /* SRC_PLUGINS_HTTP_HTTP2_FRAME_H_ */
index 82403a4..9fc9534 100644 (file)
@@ -5,6 +5,9 @@
 #ifndef SRC_PLUGINS_HTTP_HTTP2_H_
 #define SRC_PLUGINS_HTTP_HTTP2_H_
 
+#include <vppinfra/format.h>
+#include <vppinfra/types.h>
+
 /* RFC9113 section 7 */
 #define foreach_http2_error                                                   \
   _ (NO_ERROR, "NO_ERROR")                                                    \
@@ -55,4 +58,40 @@ format_http2_error (u8 *s, va_list *va)
   _ (3, PATH, "path")                                                         \
   _ (4, STATUS, "status")
 
+/* value, label, member, min, max, default_value, err_code */
+#define foreach_http2_settings                                                \
+  _ (1, HEADER_TABLE_SIZE, header_table_size, 0, CLIB_U32_MAX, 4096,          \
+     HTTP2_ERROR_NO_ERROR)                                                    \
+  _ (2, ENABLE_PUSH, enable_push, 0, 1, 1, HTTP2_ERROR_PROTOCOL_ERROR)        \
+  _ (3, MAX_CONCURRENT_STREAMS, max_concurrent_streams, 0, CLIB_U32_MAX,      \
+     CLIB_U32_MAX, HTTP2_ERROR_NO_ERROR)                                      \
+  _ (4, INITIAL_WINDOW_SIZE, initial_window_size, 0, 0x7FFFFFFF, 65535,       \
+     HTTP2_ERROR_FLOW_CONTROL_ERROR)                                          \
+  _ (5, MAX_FRAME_SIZE, max_frame_size, 16384, 16777215, 16384,               \
+     HTTP2_ERROR_PROTOCOL_ERROR)                                              \
+  _ (6, MAX_HEADER_LIST_SIZE, max_header_list_size, 0, CLIB_U32_MAX,          \
+     CLIB_U32_MAX, HTTP2_ERROR_NO_ERROR)
+
+typedef enum
+{
+#define _(value, label, member, min, max, default_value, err_code)            \
+  HTTP2_SETTINGS_##label = value,
+  foreach_http2_settings
+#undef _
+} http_settings_t;
+
+typedef struct
+{
+#define _(value, label, member, min, max, default_value, err_code) u32 member;
+  foreach_http2_settings
+#undef _
+} http2_conn_settings_t;
+
+static const http2_conn_settings_t http2_default_conn_settings = {
+#define _(value, label, member, min, max, default_value, err_code)            \
+  default_value,
+  foreach_http2_settings
+#undef _
+};
+
 #endif /* SRC_PLUGINS_HTTP_HTTP2_H_ */
index 691efdf..f44d3cb 100644 (file)
@@ -7,6 +7,7 @@
 #include <http/http.h>
 #include <http/http_header_names.h>
 #include <http/http2/hpack.h>
+#include <http/http2/frame.h>
 
 #define HTTP_TEST_I(_cond, _comment, _args...)                                \
   ({                                                                          \
@@ -1020,6 +1021,285 @@ http_test_hpack (vlib_main_t *vm)
   return 0;
 }
 
+static int
+http_test_h2_frame (vlib_main_t *vm)
+{
+  static void (*_http2_frame_header_read) (u8 * src,
+                                          http2_frame_header_t * fh);
+
+  _http2_frame_header_read =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_header_read");
+
+  vlib_cli_output (vm, "http2_frame_read_settings");
+
+  static http2_error_t (*_http2_frame_read_settings) (
+    http2_conn_settings_t * settings, u8 * payload, u32 payload_len);
+
+  _http2_frame_read_settings =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_settings");
+
+  http2_error_t rv;
+  http2_frame_header_t fh = { 0 };
+  http2_conn_settings_t conn_settings = http2_default_conn_settings;
+
+  u8 settings[] = { 0x0, 0x0, 0x12, 0x4, 0x0, 0x0,  0x0, 0x0, 0x0,
+                   0x0, 0x3, 0x0,  0x0, 0x0, 0x64, 0x0, 0x4, 0x40,
+                   0x0, 0x0, 0x0,  0x0, 0x2, 0x0,  0x0, 0x0, 0x0 };
+  _http2_frame_header_read (settings, &fh);
+  HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_SETTINGS &&
+             fh.stream_id == 0 && fh.length == 18),
+            "frame identified as SETTINGS");
+
+  rv = _http2_frame_read_settings (
+    &conn_settings, settings + HTTP2_FRAME_HEADER_SIZE, fh.length);
+  HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR &&
+             conn_settings.max_concurrent_streams == 100 &&
+             conn_settings.initial_window_size == 1073741824 &&
+             conn_settings.enable_push == 0),
+            "SETTINGS frame payload parsed")
+
+  u8 settings_ack[] = { 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0 };
+  _http2_frame_header_read (settings_ack, &fh);
+  HTTP_TEST ((fh.flags == HTTP2_FRAME_FLAG_ACK &&
+             fh.type == HTTP2_FRAME_TYPE_SETTINGS && fh.stream_id == 0 &&
+             fh.length == 0),
+            "frame identified as SETTINGS ACK");
+
+  vlib_cli_output (vm, "http2_frame_write_settings_ack");
+
+  static void (*_http2_frame_write_settings_ack) (u8 * *dst);
+
+  _http2_frame_write_settings_ack = vlib_get_plugin_symbol (
+    "http_plugin.so", "http2_frame_write_settings_ack");
+
+  u8 *buf = 0;
+
+  _http2_frame_write_settings_ack (&buf);
+  HTTP_TEST ((vec_len (buf) == sizeof (settings_ack)) &&
+              !memcmp (buf, settings_ack, sizeof (settings_ack)),
+            "SETTINGS ACK frame written");
+  vec_free (buf);
+
+  vlib_cli_output (vm, "http2_frame_write_settings");
+
+  static void (*_http2_frame_write_settings) (
+    http2_settings_entry_t * settings, u8 * *dst);
+
+  _http2_frame_write_settings =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_settings");
+
+  http2_settings_entry_t *settings_list = 0;
+  vec_validate (settings_list, 2);
+  settings_list[0].identifier = HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
+  settings_list[0].value = 100;
+  settings_list[1].identifier = HTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
+  settings_list[1].value = 1073741824;
+  settings_list[2].identifier = HTTP2_SETTINGS_ENABLE_PUSH;
+  settings_list[2].value = 0;
+
+  _http2_frame_write_settings (settings_list, &buf);
+  HTTP_TEST ((vec_len (buf) == sizeof (settings) &&
+             !memcmp (buf, settings, sizeof (settings))),
+            "SETTINGS frame written");
+  vec_free (settings_list);
+  vec_free (buf);
+
+  vlib_cli_output (vm, "http2_frame_read_window_update");
+
+  static http2_error_t (*_http2_frame_read_window_update) (
+    u32 * increment, u8 * payload, u32 payload_len);
+
+  _http2_frame_read_window_update = vlib_get_plugin_symbol (
+    "http_plugin.so", "http2_frame_read_window_update");
+
+  u32 win_increment;
+  u8 win_update[] = { 0x0, 0x0, 0x4,  0x8,  0x0, 0x0, 0x0,
+                     0x0, 0x0, 0x3f, 0xff, 0x0, 0x1 };
+  _http2_frame_header_read (win_update, &fh);
+  HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_WINDOW_UPDATE &&
+             fh.stream_id == 0 && fh.length == 4),
+            "frame identified as WINDOW_UPDATE");
+
+  rv = _http2_frame_read_window_update (
+    &win_increment, win_update + HTTP2_FRAME_HEADER_SIZE, fh.length);
+  HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && win_increment == 1073676289),
+            "WINDOW_UPDATE frame payload parsed")
+
+  vlib_cli_output (vm, "http2_frame_write_window_update");
+
+  static void (*_http2_frame_write_window_update) (u32 increment,
+                                                  u32 stream_id, u8 * *dst);
+
+  _http2_frame_write_window_update = vlib_get_plugin_symbol (
+    "http_plugin.so", "http2_frame_write_window_update");
+
+  _http2_frame_write_window_update (1073676289, 0, &buf);
+  HTTP_TEST ((vec_len (buf) == sizeof (win_update) &&
+             !memcmp (buf, win_update, sizeof (win_update))),
+            "WINDOW_UPDATE frame written");
+  vec_free (buf);
+
+  vlib_cli_output (vm, "http2_frame_read_rst_stream");
+
+  static http2_error_t (*_http2_frame_read_rst_stream) (
+    u32 * error_code, u8 * payload, u32 payload_len);
+
+  _http2_frame_read_rst_stream =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_rst_stream");
+
+  u32 error_code;
+  u8 rst_stream[] = { 0x0, 0x0, 0x4, 0x3, 0x0, 0x0, 0x0,
+                     0x0, 0x5, 0x0, 0x0, 0x0, 0x01 };
+  _http2_frame_header_read (rst_stream, &fh);
+  HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_RST_STREAM &&
+             fh.stream_id == 5 && fh.length == 4),
+            "frame identified as RST_STREAM");
+
+  rv = _http2_frame_read_rst_stream (
+    &error_code, rst_stream + HTTP2_FRAME_HEADER_SIZE, fh.length);
+  HTTP_TEST (
+    (rv == HTTP2_ERROR_NO_ERROR && error_code == HTTP2_ERROR_PROTOCOL_ERROR),
+    "RST_STREAM frame payload parsed")
+
+  vlib_cli_output (vm, "http2_frame_write_rst_stream");
+
+  static void (*_http2_frame_write_rst_stream) (u32 increment, u32 stream_id,
+                                               u8 * *dst);
+
+  _http2_frame_write_rst_stream =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_rst_stream");
+
+  _http2_frame_write_rst_stream (HTTP2_ERROR_PROTOCOL_ERROR, 5, &buf);
+  HTTP_TEST ((vec_len (buf) == sizeof (rst_stream) &&
+             !memcmp (buf, rst_stream, sizeof (rst_stream))),
+            "RST_STREAM frame written");
+  vec_free (buf);
+
+  vlib_cli_output (vm, "http2_frame_read_goaway");
+
+  static http2_error_t (*_http2_frame_read_goaway) (
+    u32 * error_code, u32 * last_stream_id, u8 * payload, u32 payload_len);
+
+  _http2_frame_read_goaway =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_goaway");
+
+  u32 last_stream_id;
+  u8 goaway[] = { 0x0, 0x0, 0x8, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0,
+                 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x2 };
+
+  _http2_frame_header_read (goaway, &fh);
+  HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_GOAWAY &&
+             fh.stream_id == 0 && fh.length == 8),
+            "frame identified as GOAWAY");
+
+  rv = _http2_frame_read_goaway (&error_code, &last_stream_id,
+                                goaway + HTTP2_FRAME_HEADER_SIZE, fh.length);
+  HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR &&
+             error_code == HTTP2_ERROR_INTERNAL_ERROR && last_stream_id == 5),
+            "GOAWAY frame payload parsed")
+
+  vlib_cli_output (vm, "http2_frame_write_goaway");
+
+  static void (*_http2_frame_write_goaway) (http2_error_t error_code,
+                                           u32 last_stream_id, u8 * *dst);
+
+  _http2_frame_write_goaway =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_goaway");
+
+  _http2_frame_write_goaway (HTTP2_ERROR_INTERNAL_ERROR, 5, &buf);
+  HTTP_TEST ((vec_len (buf) == sizeof (goaway) &&
+             !memcmp (buf, goaway, sizeof (goaway))),
+            "GOAWAY frame written");
+  vec_free (buf);
+
+  vlib_cli_output (vm, "http2_frame_read_headers");
+
+  static http2_error_t (*_http2_frame_read_headers) (
+    u8 * *headers, u32 * headers_len, u8 * payload, u32 payload_len, u8 flags);
+
+  _http2_frame_read_headers =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_headers");
+
+  u8 *h;
+  u32 h_len;
+  u8 headers[] = { 0x0,         0x0,  0x28, 0x1,  0x5,  0x0,  0x0,  0x0,  0x3,  0x3f,
+                  0xe1, 0x1f, 0x82, 0x4,  0x88, 0x62, 0x7b, 0x69, 0x1d, 0x48,
+                  0x5d, 0x3e, 0x53, 0x86, 0x41, 0x88, 0xaa, 0x69, 0xd2, 0x9a,
+                  0xc4, 0xb9, 0xec, 0x9b, 0x7a, 0x88, 0x25, 0xb6, 0x50, 0xc3,
+                  0xab, 0xb8, 0x15, 0xc1, 0x53, 0x3,  0x2a, 0x2f, 0x2a };
+
+  _http2_frame_header_read (headers, &fh);
+  HTTP_TEST ((fh.flags ==
+               (HTTP2_FRAME_FLAG_END_HEADERS | HTTP2_FRAME_FLAG_END_STREAM) &&
+             fh.type == HTTP2_FRAME_TYPE_HEADERS && fh.stream_id == 3 &&
+             fh.length == 40),
+            "frame identified as HEADERS");
+
+  rv = _http2_frame_read_headers (
+    &h, &h_len, headers + HTTP2_FRAME_HEADER_SIZE, fh.length, fh.flags);
+  HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && h_len == 40 &&
+             *h == headers[HTTP2_FRAME_HEADER_SIZE]),
+            "HEADERS frame payload parsed")
+
+  vlib_cli_output (vm, "http2_frame_write_headers_header");
+
+  static void (*_http2_frame_write_headers_header) (
+    u32 headers_len, u32 stream_id, u8 flags, u8 * dst);
+
+  _http2_frame_write_headers_header = vlib_get_plugin_symbol (
+    "http_plugin.so", "http2_frame_write_headers_header");
+
+  u8 *p = http2_frame_header_alloc (&buf);
+  _http2_frame_write_headers_header (
+    40, 3, HTTP2_FRAME_FLAG_END_HEADERS | HTTP2_FRAME_FLAG_END_STREAM, p);
+  HTTP_TEST ((vec_len (buf) == HTTP2_FRAME_HEADER_SIZE &&
+             !memcmp (buf, headers, HTTP2_FRAME_HEADER_SIZE)),
+            "HEADERS frame header written");
+  vec_free (buf);
+
+  vlib_cli_output (vm, "http2_frame_read_data");
+
+  static http2_error_t (*_http2_frame_read_data) (
+    u8 * *data, u32 * data_len, u8 * payload, u32 payload_len, u8 flags);
+
+  _http2_frame_read_data =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_data");
+
+  u8 *d;
+  u32 d_len;
+  u8 data[] = { 0x0,  0x0,  0x9,  0x0, 0x1,  0x0,  0x0,  0x0,  0x3,
+               0x6e, 0x6f, 0x74, 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64 };
+
+  _http2_frame_header_read (data, &fh);
+  HTTP_TEST ((fh.flags == HTTP2_FRAME_FLAG_END_STREAM &&
+             fh.type == HTTP2_FRAME_TYPE_DATA && fh.stream_id == 3 &&
+             fh.length == 9),
+            "frame identified as DATA");
+
+  rv = _http2_frame_read_data (&d, &d_len, data + HTTP2_FRAME_HEADER_SIZE,
+                              fh.length, fh.flags);
+  HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && d_len == 9 &&
+             *d == data[HTTP2_FRAME_HEADER_SIZE]),
+            "DATA frame payload parsed")
+
+  vlib_cli_output (vm, "http2_frame_write_data_header");
+
+  static void (*_http2_frame_write_data_header) (
+    u32 headers_len, u32 stream_id, u8 flags, u8 * dst);
+
+  _http2_frame_write_data_header =
+    vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_data_header");
+
+  p = http2_frame_header_alloc (&buf);
+  _http2_frame_write_data_header (9, 3, HTTP2_FRAME_FLAG_END_STREAM, p);
+  HTTP_TEST ((vec_len (buf) == HTTP2_FRAME_HEADER_SIZE &&
+             !memcmp (buf, data, HTTP2_FRAME_HEADER_SIZE)),
+            "DATA frame header written");
+  vec_free (buf);
+
+  return 0;
+}
+
 static clib_error_t *
 test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
                      vlib_cli_command_t *cmd)
@@ -1039,6 +1319,8 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
        res = http_test_http_header_table (vm);
       else if (unformat (input, "hpack"))
        res = http_test_hpack (vm);
+      else if (unformat (input, "h2-frame"))
+       res = http_test_h2_frame (vm);
       else if (unformat (input, "all"))
        {
          if ((res = http_test_parse_authority (vm)))
@@ -1053,6 +1335,8 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input,
            goto done;
          if ((res = http_test_hpack (vm)))
            goto done;
+         if ((res = http_test_h2_frame (vm)))
+           goto done;
        }
       else
        break;