summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2022-07-28 11:36:34 +0200
committerCarlos Garcia Campos <cgarcia@igalia.com>2022-08-12 12:20:02 +0200
commit7d22b092a47ca5858251fed95614c6656966456b (patch)
tree14d941648f8eb7f1d667a43ba31c6f6f0a4605ec
parent261cbad7cf9134ba5b63983ccbc61834724ae5ab (diff)
downloadlibsoup-7d22b092a47ca5858251fed95614c6656966456b.tar.gz
http2: add HTTP/2 utils with common functions and enums
-rw-r--r--libsoup/http2/soup-client-message-io-http2.c172
-rw-r--r--libsoup/meson.build1
-rw-r--r--libsoup/server/http2/soup-server-message-io-http2.c71
-rw-r--r--libsoup/soup-http2-utils.c114
-rw-r--r--libsoup/soup-http2-utils.h49
5 files changed, 186 insertions, 221 deletions
diff --git a/libsoup/http2/soup-client-message-io-http2.c b/libsoup/http2/soup-client-message-io-http2.c
index 39c9eb2f..8ee95e8a 100644
--- a/libsoup/http2/soup-client-message-io-http2.c
+++ b/libsoup/http2/soup-client-message-io-http2.c
@@ -40,25 +40,13 @@
#include "soup-client-input-stream.h"
#include "soup-logger-private.h"
#include "soup-uri-utils-private.h"
+#include "soup-http2-utils.h"
#include "content-decoder/soup-content-decoder.h"
#include "soup-body-input-stream-http2.h"
-#include <nghttp2/nghttp2.h>
-
#define FRAME_HEADER_SIZE 9
-typedef enum {
- STATE_NONE,
- STATE_WRITE_HEADERS,
- STATE_WRITE_DATA,
- STATE_WRITE_DONE,
- STATE_READ_HEADERS,
- STATE_READ_DATA_START,
- STATE_READ_DATA,
- STATE_READ_DONE,
-} SoupHTTP2IOState;
-
typedef struct {
SoupClientMessageIO iface;
@@ -127,93 +115,6 @@ typedef struct {
static void soup_client_message_io_http2_finished (SoupClientMessageIO *iface, SoupMessage *msg);
static ssize_t on_data_source_read_callback (nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data);
-static void
-NGCHECK (int return_code)
-{
- if (return_code == NGHTTP2_ERR_NOMEM)
- g_abort ();
- else if (return_code < 0)
- g_debug ("Unhandled NGHTTP2 Error: %s", nghttp2_strerror (return_code));
-}
-
-static const char *
-frame_type_to_string (nghttp2_frame_type type)
-{
- switch (type) {
- case NGHTTP2_DATA:
- return "DATA";
- case NGHTTP2_HEADERS:
- return "HEADERS";
- case NGHTTP2_PRIORITY:
- return "PRIORITY";
- case NGHTTP2_RST_STREAM:
- return "RST_STREAM";
- case NGHTTP2_SETTINGS:
- return "SETTINGS";
- case NGHTTP2_PING:
- return "PING";
- case NGHTTP2_GOAWAY:
- return "GOAWAY";
- case NGHTTP2_WINDOW_UPDATE:
- return "WINDOW_UPDATE";
- /* LCOV_EXCL_START */
- case NGHTTP2_PUSH_PROMISE:
- return "PUSH_PROMISE";
- case NGHTTP2_CONTINUATION:
- return "CONTINUATION";
- case NGHTTP2_ALTSVC:
- return "ALTSVC";
- case NGHTTP2_ORIGIN:
- return "ORIGIN";
- default:
- g_warn_if_reached ();
- return "UNKNOWN";
- /* LCOV_EXCL_STOP */
- }
-}
-
-static const char *
-headers_category_to_string (nghttp2_headers_category catergory)
-{
- switch (catergory) {
- case NGHTTP2_HCAT_REQUEST:
- return "REQUEST";
- case NGHTTP2_HCAT_RESPONSE:
- return "RESPONSE";
- case NGHTTP2_HCAT_PUSH_RESPONSE:
- return "PUSH_RESPONSE";
- case NGHTTP2_HCAT_HEADERS:
- return "HEADERS";
- }
- g_assert_not_reached ();
-}
-
-static const char *
-state_to_string (SoupHTTP2IOState state)
-{
- switch (state) {
- case STATE_NONE:
- return "NONE";
- case STATE_WRITE_HEADERS:
- return "WRITE_HEADERS";
- case STATE_WRITE_DATA:
- return "WRITE_DATA";
- case STATE_WRITE_DONE:
- return "WRITE_DONE";
- case STATE_READ_HEADERS:
- return "READ_HEADERS";
- case STATE_READ_DATA_START:
- return "READ_DATA_START";
- case STATE_READ_DATA:
- return "READ_DATA";
- case STATE_READ_DONE:
- return "READ_DONE";
- default:
- g_assert_not_reached ();
- return "";
- }
-}
-
G_GNUC_PRINTF(3, 0)
static void
h2_debug (SoupClientMessageIOHTTP2 *io,
@@ -236,7 +137,7 @@ h2_debug (SoupClientMessageIOHTTP2 *io,
stream_id = data->stream_id;
g_assert (io);
- g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "[C%" G_GUINT64_FORMAT "-S%u] [%s] %s", io->connection_id, stream_id, data ? state_to_string (data->state) : "-", message);
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "[C%" G_GUINT64_FORMAT "-S%u] [%s] %s", io->connection_id, stream_id, data ? soup_http2_io_state_to_string (data->state) : "-", message);
g_free (message);
}
@@ -288,20 +189,20 @@ advance_state_from (SoupHTTP2MessageData *data,
{
if (data->state != from) {
g_warning ("Unexpected state changed %s -> %s, expected to be from %s",
- state_to_string (data->state), state_to_string (to),
- state_to_string (from));
+ soup_http2_io_state_to_string (data->state), soup_http2_io_state_to_string (to),
+ soup_http2_io_state_to_string (from));
}
/* State never goes backwards */
if (to < data->state) {
g_warning ("Unexpected state changed %s -> %s, expected %s -> %s\n",
- state_to_string (data->state), state_to_string (to),
- state_to_string (from), state_to_string (to));
+ soup_http2_io_state_to_string (data->state), soup_http2_io_state_to_string (to),
+ soup_http2_io_state_to_string (from), soup_http2_io_state_to_string (to));
return;
}
h2_debug (data->io, data, "[SESSION] State %s -> %s",
- state_to_string (data->state), state_to_string (to));
+ soup_http2_io_state_to_string (data->state), soup_http2_io_state_to_string (to));
data->state = to;
}
@@ -617,7 +518,7 @@ on_begin_frame_callback (nghttp2_session *session,
{
SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, hd->stream_id);
- h2_debug (user_data, data, "[RECV] [%s] Beginning", frame_type_to_string (hd->type));
+ h2_debug (user_data, data, "[RECV] [%s] Beginning", soup_http2_frame_type_to_string (hd->type));
if (!data)
return 0;
@@ -688,7 +589,7 @@ on_frame_recv_callback (nghttp2_session *session,
io->in_callback++;
if (frame->hd.stream_id == 0) {
- h2_debug (io, NULL, "[RECV] [%s] Received (%u)", frame_type_to_string (frame->hd.type), frame->hd.flags);
+ h2_debug (io, NULL, "[RECV] [%s] Received (%u)", soup_http2_frame_type_to_string (frame->hd.type), frame->hd.flags);
switch (frame->hd.type) {
case NGHTTP2_GOAWAY:
@@ -711,7 +612,7 @@ on_frame_recv_callback (nghttp2_session *session,
}
data = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id);
- h2_debug (io, data, "[RECV] [%s] Received (%u)", frame_type_to_string (frame->hd.type), frame->hd.flags);
+ h2_debug (io, data, "[RECV] [%s] Received (%u)", soup_http2_frame_type_to_string (frame->hd.type), frame->hd.flags);
if (!data) {
if (!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) && frame->hd.type != NGHTTP2_RST_STREAM)
@@ -728,7 +629,7 @@ on_frame_recv_callback (nghttp2_session *session,
data->metrics->response_header_bytes_received += frame->hd.length + FRAME_HEADER_SIZE;
h2_debug (io, data, "[HEADERS] category=%s status=%u",
- headers_category_to_string (frame->headers.cat), status);
+ soup_http2_headers_category_to_string (frame->headers.cat), status);
switch (frame->headers.cat) {
case NGHTTP2_HCAT_HEADERS:
if (!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS)) {
@@ -885,7 +786,7 @@ on_frame_send_callback (nghttp2_session *session,
case NGHTTP2_HEADERS:
g_assert (data);
h2_debug (io, data, "[SEND] [HEADERS] category=%s finished=%d",
- headers_category_to_string (frame->headers.cat),
+ soup_http2_headers_category_to_string (frame->headers.cat),
(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) ? 1 : 0);
if (data->metrics)
@@ -930,7 +831,7 @@ on_frame_send_callback (nghttp2_session *session,
break;
case NGHTTP2_GOAWAY:
- h2_debug (io, data, "[SEND] [%s]", frame_type_to_string (frame->hd.type));
+ h2_debug (io, data, "[SEND] [%s]", soup_http2_frame_type_to_string (frame->hd.type));
io->goaway_sent = TRUE;
if (io->close_task) {
GSource *source;
@@ -944,7 +845,7 @@ on_frame_send_callback (nghttp2_session *session,
}
break;
default:
- h2_debug (io, data, "[SEND] [%s]", frame_type_to_string (frame->hd.type));
+ h2_debug (io, data, "[SEND] [%s]", soup_http2_frame_type_to_string (frame->hd.type));
break;
}
@@ -985,7 +886,7 @@ on_frame_not_send_callback (nghttp2_session *session,
SoupClientMessageIOHTTP2 *io = user_data;
SoupHTTP2MessageData *data = nghttp2_session_get_stream_user_data (session, frame->hd.stream_id);
- h2_debug (io, data, "[SEND] [%s] Failed: %s", frame_type_to_string (frame->hd.type),
+ h2_debug (io, data, "[SEND] [%s] Failed: %s", soup_http2_frame_type_to_string (frame->hd.type),
nghttp2_strerror (lib_error_code));
if (lib_error_code == NGHTTP2_ERR_SESSION_CLOSING)
@@ -1327,24 +1228,6 @@ request_header_is_valid (const char *name)
return !g_hash_table_contains (invalid_request_headers, name);
}
-#define MAKE_NV(NAME, VALUE, VALUELEN) \
- { \
- (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), VALUELEN, \
- NGHTTP2_NV_FLAG_NONE \
- }
-
-#define MAKE_NV2(NAME, VALUE) \
- { \
- (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), \
- NGHTTP2_NV_FLAG_NONE \
- }
-
-#define MAKE_NV3(NAME, VALUE, FLAGS) \
- { \
- (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), \
- FLAGS \
- }
-
static void
send_message_request (SoupMessage *msg,
SoupClientMessageIOHTTP2 *io,
@@ -1871,33 +1754,10 @@ static const SoupClientMessageIOFuncs io_funcs = {
soup_client_message_io_http2_owner_changed
};
-G_GNUC_PRINTF(1, 0)
-static void
-debug_nghttp2 (const char *format,
- va_list args)
-{
- char *message;
- gsize len;
-
- if (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "nghttp2"))
- return;
-
- message = g_strdup_vprintf (format, args);
- len = strlen (message);
- if (len >= 1 && message[len - 1] == '\n')
- message[len - 1] = '\0';
- g_log ("nghttp2", G_LOG_LEVEL_DEBUG, "[NGHTTP2] %s", message);
- g_free (message);
-}
-
static void
soup_client_message_io_http2_init (SoupClientMessageIOHTTP2 *io)
{
- static gsize nghttp2_debug_init = 0;
- if (g_once_init_enter (&nghttp2_debug_init)) {
- nghttp2_set_debug_vprintf_callback(debug_nghttp2);
- g_once_init_leave (&nghttp2_debug_init, 1);
- }
+ soup_http2_debug_init ();
nghttp2_session_callbacks *callbacks;
NGCHECK (nghttp2_session_callbacks_new (&callbacks));
diff --git a/libsoup/meson.build b/libsoup/meson.build
index a82a49fd..72fa86f0 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -70,6 +70,7 @@ soup_sources = [
'soup-form.c',
'soup-headers.c',
'soup-header-names.c',
+ 'soup-http2-utils.c',
'soup-init.c',
'soup-io-stream.c',
'soup-logger.c',
diff --git a/libsoup/server/http2/soup-server-message-io-http2.c b/libsoup/server/http2/soup-server-message-io-http2.c
index c1682e04..a08d53f2 100644
--- a/libsoup/server/http2/soup-server-message-io-http2.c
+++ b/libsoup/server/http2/soup-server-message-io-http2.c
@@ -20,18 +20,7 @@
#include "soup-message-headers-private.h"
#include "soup-server-message-private.h"
#include "soup-misc.h"
-
-#include <nghttp2/nghttp2.h>
-
-typedef enum {
- STATE_NONE,
- STATE_READ_HEADERS,
- STATE_READ_DATA,
- STATE_READ_DONE,
- STATE_WRITE_HEADERS,
- STATE_WRITE_DATA,
- STATE_WRITE_DONE,
-} SoupHTTP2IOState;
+#include "soup-http2-utils.h"
typedef struct {
SoupServerMessage *msg;
@@ -79,30 +68,6 @@ typedef struct {
static void soup_server_message_io_http2_send_response (SoupServerMessageIOHTTP2 *io,
SoupMessageIOHTTP2 *msg_io);
-static const char *
-state_to_string (SoupHTTP2IOState state)
-{
- switch (state) {
- case STATE_NONE:
- return "NONE";
- case STATE_READ_HEADERS:
- return "READ_HEADERS";
- case STATE_READ_DATA:
- return "READ_DATA";
- case STATE_READ_DONE:
- return "READ_DONE";
- case STATE_WRITE_HEADERS:
- return "WRITE_HEADERS";
- case STATE_WRITE_DATA:
- return "WRITE_DATA";
- case STATE_WRITE_DONE:
- return "WRITE_DONE";
- default:
- g_assert_not_reached ();
- return "";
- }
-}
-
static void
advance_state_from (SoupMessageIOHTTP2 *msg_io,
SoupHTTP2IOState from,
@@ -110,16 +75,8 @@ advance_state_from (SoupMessageIOHTTP2 *msg_io,
{
if (msg_io->state != from) {
g_warning ("Unexpected state changed %s -> %s, expected to be from %s",
- state_to_string (msg_io->state), state_to_string (to),
- state_to_string (from));
- }
-
- /* State never goes backwards */
- if (to < msg_io->state) {
- g_warning ("Unexpected state changed %s -> %s, expected %s -> %s\n",
- state_to_string (msg_io->state), state_to_string (to),
- state_to_string (from), state_to_string (to));
- return;
+ soup_http2_io_state_to_string (msg_io->state), soup_http2_io_state_to_string (to),
+ soup_http2_io_state_to_string (from));
}
msg_io->state = to;
@@ -183,7 +140,7 @@ soup_server_message_io_http2_finished (SoupServerMessageIO *iface,
SoupMessageIOCompletion completion;
g_hash_table_steal_extended (io->messages, msg, NULL, (gpointer *)&msg_io);
- completion = msg_io->state < STATE_WRITE_DONE ? SOUP_MESSAGE_IO_INTERRUPTED : SOUP_MESSAGE_IO_COMPLETE;
+ completion = msg_io->state != STATE_WRITE_DONE ? SOUP_MESSAGE_IO_INTERRUPTED : SOUP_MESSAGE_IO_COMPLETE;
completion_cb = msg_io->completion_cb;
completion_data = msg_io->completion_data;
@@ -588,24 +545,6 @@ on_data_source_read_callback (nghttp2_session *session,
return bytes_written;
}
-#define MAKE_NV(NAME, VALUE, VALUELEN) \
- { \
- (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), VALUELEN, \
- NGHTTP2_NV_FLAG_NONE \
- }
-
-#define MAKE_NV2(NAME, VALUE) \
- { \
- (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), \
- NGHTTP2_NV_FLAG_NONE \
- }
-
-#define MAKE_NV3(NAME, VALUE, FLAGS) \
- { \
- (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), \
- FLAGS \
- }
-
static void
soup_server_message_io_http2_send_response (SoupServerMessageIOHTTP2 *io,
SoupMessageIOHTTP2 *msg_io)
@@ -747,6 +686,8 @@ soup_server_message_io_http2_init (SoupServerMessageIOHTTP2 *io)
{
nghttp2_session_callbacks *callbacks;
+ soup_http2_debug_init ();
+
nghttp2_session_callbacks_new (&callbacks);
nghttp2_session_callbacks_set_on_begin_headers_callback (callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback (callbacks, on_header_callback);
diff --git a/libsoup/soup-http2-utils.c b/libsoup/soup-http2-utils.c
new file mode 100644
index 00000000..3bacf2c3
--- /dev/null
+++ b/libsoup/soup-http2-utils.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2022 Igalia, S.L.
+ */
+
+#include "soup-http2-utils.h"
+#include <glib.h>
+
+const char *
+soup_http2_io_state_to_string (SoupHTTP2IOState state)
+{
+ switch (state) {
+ case STATE_NONE:
+ return "NONE";
+ case STATE_WRITE_HEADERS:
+ return "WRITE_HEADERS";
+ case STATE_WRITE_DATA:
+ return "WRITE_DATA";
+ case STATE_WRITE_DONE:
+ return "WRITE_DONE";
+ case STATE_READ_HEADERS:
+ return "READ_HEADERS";
+ case STATE_READ_DATA_START:
+ return "READ_DATA_START";
+ case STATE_READ_DATA:
+ return "READ_DATA";
+ case STATE_READ_DONE:
+ return "READ_DONE";
+ }
+ g_assert_not_reached ();
+ return "";
+}
+
+const char *
+soup_http2_frame_type_to_string (nghttp2_frame_type type)
+{
+ switch (type) {
+ case NGHTTP2_DATA:
+ return "DATA";
+ case NGHTTP2_HEADERS:
+ return "HEADERS";
+ case NGHTTP2_PRIORITY:
+ return "PRIORITY";
+ case NGHTTP2_RST_STREAM:
+ return "RST_STREAM";
+ case NGHTTP2_SETTINGS:
+ return "SETTINGS";
+ case NGHTTP2_PING:
+ return "PING";
+ case NGHTTP2_GOAWAY:
+ return "GOAWAY";
+ case NGHTTP2_WINDOW_UPDATE:
+ return "WINDOW_UPDATE";
+ /* LCOV_EXCL_START */
+ case NGHTTP2_PUSH_PROMISE:
+ return "PUSH_PROMISE";
+ case NGHTTP2_CONTINUATION:
+ return "CONTINUATION";
+ case NGHTTP2_ALTSVC:
+ return "ALTSVC";
+ case NGHTTP2_ORIGIN:
+ return "ORIGIN";
+ default:
+ g_warn_if_reached ();
+ return "UNKNOWN";
+ /* LCOV_EXCL_STOP */
+ }
+}
+
+const char *
+soup_http2_headers_category_to_string (nghttp2_headers_category catergory)
+{
+ switch (catergory) {
+ case NGHTTP2_HCAT_REQUEST:
+ return "REQUEST";
+ case NGHTTP2_HCAT_RESPONSE:
+ return "RESPONSE";
+ case NGHTTP2_HCAT_PUSH_RESPONSE:
+ return "PUSH_RESPONSE";
+ case NGHTTP2_HCAT_HEADERS:
+ return "HEADERS";
+ }
+ g_assert_not_reached ();
+ return "";
+}
+
+G_GNUC_PRINTF(1, 0)
+static void
+debug_nghttp2 (const char *format,
+ va_list args)
+{
+ char *message;
+ gsize len;
+
+ if (g_log_writer_default_would_drop (G_LOG_LEVEL_DEBUG, "nghttp2"))
+ return;
+
+ message = g_strdup_vprintf (format, args);
+ len = strlen (message);
+ if (len >= 1 && message[len - 1] == '\n')
+ message[len - 1] = '\0';
+ g_log ("nghttp2", G_LOG_LEVEL_DEBUG, "[NGHTTP2] %s", message);
+ g_free (message);
+}
+
+void
+soup_http2_debug_init (void)
+{
+ static gsize nghttp2_debug_init = 0;
+ if (g_once_init_enter (&nghttp2_debug_init)) {
+ nghttp2_set_debug_vprintf_callback(debug_nghttp2);
+ g_once_init_leave (&nghttp2_debug_init, 1);
+ }
+
+}
diff --git a/libsoup/soup-http2-utils.h b/libsoup/soup-http2-utils.h
new file mode 100644
index 00000000..f3a7c735
--- /dev/null
+++ b/libsoup/soup-http2-utils.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 Igalia, S.L.
+ */
+
+#pragma once
+
+#include <nghttp2/nghttp2.h>
+
+#define NGCHECK(stm) \
+ G_STMT_START { \
+ int return_code = stm; \
+ if (return_code == NGHTTP2_ERR_NOMEM) \
+ g_abort (); \
+ else if (return_code < 0) \
+ g_debug ("Unhandled NGHTTP2 Error: %s", nghttp2_strerror (return_code)); \
+ } G_STMT_END
+
+#define MAKE_NV(NAME, VALUE, VALUELEN) \
+ { \
+ (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), VALUELEN, NGHTTP2_NV_FLAG_NONE \
+ }
+
+#define MAKE_NV2(NAME, VALUE) \
+ { \
+ (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), NGHTTP2_NV_FLAG_NONE \
+ }
+
+#define MAKE_NV3(NAME, VALUE, FLAGS) \
+ { \
+ (uint8_t *)NAME, (uint8_t *)VALUE, strlen (NAME), strlen (VALUE), FLAGS \
+ }
+
+
+typedef enum {
+ STATE_NONE,
+ STATE_WRITE_HEADERS,
+ STATE_WRITE_DATA,
+ STATE_WRITE_DONE,
+ STATE_READ_HEADERS,
+ STATE_READ_DATA_START,
+ STATE_READ_DATA,
+ STATE_READ_DONE,
+} SoupHTTP2IOState;
+
+const char *soup_http2_io_state_to_string (SoupHTTP2IOState state);
+const char *soup_http2_frame_type_to_string (nghttp2_frame_type type);
+const char *soup_http2_headers_category_to_string (nghttp2_headers_category catergory);
+
+void soup_http2_debug_init (void);