[libsoup/carlosgc/http2-terminate-session] http2: send goaway frame before closing the connection
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/carlosgc/http2-terminate-session] http2: send goaway frame before closing the connection
- Date: Tue, 25 May 2021 13:22:03 +0000 (UTC)
commit 6207a446aae9974ba4428d8f4c49f3b3c047ee7a
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Tue May 25 15:20:20 2021 +0200
http2: send goaway frame before closing the connection
libsoup/http1/soup-client-message-io-http1.c | 1 +
libsoup/http2/soup-client-message-io-http2.c | 85 ++++++++++++++++++++++++++++
libsoup/soup-client-message-io.c | 12 ++++
libsoup/soup-client-message-io.h | 6 ++
libsoup/soup-connection.c | 51 +++++++++++------
5 files changed, 138 insertions(+), 17 deletions(-)
---
diff --git a/libsoup/http1/soup-client-message-io-http1.c b/libsoup/http1/soup-client-message-io-http1.c
index 627348c5..e6a2ece3 100644
--- a/libsoup/http1/soup-client-message-io-http1.c
+++ b/libsoup/http1/soup-client-message-io-http1.c
@@ -1132,6 +1132,7 @@ static const SoupClientMessageIOFuncs io_funcs = {
soup_client_message_io_http1_run,
soup_client_message_io_http1_run_until_read,
soup_client_message_io_http1_run_until_read_async,
+ NULL,
soup_client_message_io_http1_skip,
soup_client_message_io_http1_is_open,
soup_client_message_io_http1_in_progress,
diff --git a/libsoup/http2/soup-client-message-io-http2.c b/libsoup/http2/soup-client-message-io-http2.c
index 343c4533..e4e6fca8 100644
--- a/libsoup/http2/soup-client-message-io-http2.c
+++ b/libsoup/http2/soup-client-message-io-http2.c
@@ -78,6 +78,9 @@ typedef struct {
gssize written_bytes;
gboolean is_shutdown;
+
+ GSource *close_source;
+ gboolean goaway_sent;
} SoupClientMessageIOHTTP2;
typedef struct {
@@ -1343,6 +1346,83 @@ soup_client_message_io_http2_run_until_read_async (SoupClientMessageIO *iface,
io_run_until_read_async (msg, task);
}
+static void io_close_async (SoupClientMessageIOHTTP2 *io,
+ GTask *task);
+
+static gboolean
+io_close_ready (GTask *task)
+{
+ SoupClientMessageIOHTTP2 *io = g_task_get_task_data (task);
+
+ io_close_async (io, task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+io_try_write (SoupClientMessageIOHTTP2 *io,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ while (nghttp2_session_want_write (io->session) && !error)
+ io_write (io, FALSE, FALSE, &error);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ io->close_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM
(io->ostream), NULL);
+ g_source_set_callback (io->close_source, (GSourceFunc)io_close_ready, task, NULL);
+ g_source_attach (io->close_source, g_main_context_get_thread_default ());
+ return FALSE;
+ }
+
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+io_close_async (SoupClientMessageIOHTTP2 *io,
+ GTask *task)
+{
+ if (io->close_source) {
+ g_source_destroy (io->close_source);
+ g_clear_pointer (&io->close_source, g_source_unref);
+ }
+
+ /* Finish any pending write */
+ if (!io_try_write (io, task))
+ return;
+
+ if (!io->goaway_sent) {
+ io->goaway_sent = TRUE;
+ NGCHECK (nghttp2_session_terminate_session (io->session, NGHTTP2_NO_ERROR));
+
+ /* Write the goaway frame */
+ if (!io_try_write (io, task))
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+soup_client_message_io_http2_close_async (SoupClientMessageIO *iface,
+ SoupConnection *conn,
+ GAsyncReadyCallback callback)
+{
+ SoupClientMessageIOHTTP2 *io = (SoupClientMessageIOHTTP2 *)iface;
+ GTask *task;
+
+ task = g_task_new (conn, NULL, callback, NULL);
+ g_task_set_task_data (task, io, NULL);
+ io_close_async (io, task);
+}
+
static gboolean
soup_client_message_io_http2_is_open (SoupClientMessageIO *iface)
{
@@ -1356,6 +1436,10 @@ soup_client_message_io_http2_destroy (SoupClientMessageIO *iface)
{
SoupClientMessageIOHTTP2 *io = (SoupClientMessageIOHTTP2 *)iface;
+ if (io->close_source) {
+ g_source_destroy (io->close_source);
+ g_source_unref (io->close_source);
+ }
g_clear_object (&io->stream);
g_clear_pointer (&io->async_context, g_main_context_unref);
g_clear_pointer (&io->session, nghttp2_session_del);
@@ -1376,6 +1460,7 @@ static const SoupClientMessageIOFuncs io_funcs = {
soup_client_message_io_http2_run,
soup_client_message_io_http2_run_until_read,
soup_client_message_io_http2_run_until_read_async,
+ soup_client_message_io_http2_close_async,
soup_client_message_io_http2_skip,
soup_client_message_io_http2_is_open,
soup_client_message_io_http2_in_progress,
diff --git a/libsoup/soup-client-message-io.c b/libsoup/soup-client-message-io.c
index c9c78450..958e47fc 100644
--- a/libsoup/soup-client-message-io.c
+++ b/libsoup/soup-client-message-io.c
@@ -89,6 +89,18 @@ soup_client_message_io_run_until_read_async (SoupClientMessageIO *io,
io->funcs->run_until_read_async (io, msg, io_priority, cancellable, callback, user_data);
}
+gboolean
+soup_client_message_io_close_async (SoupClientMessageIO *io,
+ SoupConnection *conn,
+ GAsyncReadyCallback callback)
+{
+ if (!io->funcs->close_async)
+ return FALSE;
+
+ io->funcs->close_async (io, conn, callback);
+ return TRUE;
+}
+
gboolean
soup_client_message_io_skip (SoupClientMessageIO *io,
SoupMessage *msg,
diff --git a/libsoup/soup-client-message-io.h b/libsoup/soup-client-message-io.h
index 2a4d3b38..3e930bbf 100644
--- a/libsoup/soup-client-message-io.h
+++ b/libsoup/soup-client-message-io.h
@@ -41,6 +41,9 @@ typedef struct {
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
+ void (*close_async) (SoupClientMessageIO *io,
+ SoupConnection *conn,
+ GAsyncReadyCallback callback);
gboolean (*skip) (SoupClientMessageIO *io,
SoupMessage *msg,
gboolean blocking,
@@ -83,6 +86,9 @@ void soup_client_message_io_run_until_read_async (SoupClientMessageIO
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
+gboolean soup_client_message_io_close_async (SoupClientMessageIO *io,
+ SoupConnection *conn,
+ GAsyncReadyCallback callback);
gboolean soup_client_message_io_skip (SoupClientMessageIO *io,
SoupMessage *msg,
gboolean blocking,
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 5c2f0040..6ead5709 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -888,6 +888,33 @@ soup_connection_tunnel_handshake (SoupConnection *conn,
return TRUE;
}
+static void
+soup_connection_disconnected (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+
+ if (priv->connection) {
+ GIOStream *connection;
+
+ connection = priv->connection;
+ priv->connection = NULL;
+
+ g_io_stream_close (connection, NULL, NULL);
+ g_signal_handlers_disconnect_by_data (connection, conn);
+ g_object_unref (connection);
+ }
+
+ g_signal_emit (conn, signals[DISCONNECTED], 0);
+}
+
+static void
+client_message_io_closed_cb (SoupConnection *conn,
+ GAsyncResult *result)
+{
+ g_task_propagate_boolean (G_TASK (result), NULL);
+ soup_connection_disconnected (conn);
+}
+
/**
* soup_connection_disconnect:
* @conn: a connection
@@ -898,13 +925,11 @@ soup_connection_tunnel_handshake (SoupConnection *conn,
void
soup_connection_disconnect (SoupConnection *conn)
{
- SoupConnectionPrivate *priv;
- SoupConnectionState old_state;
+ SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
- g_return_if_fail (SOUP_IS_CONNECTION (conn));
- priv = soup_connection_get_instance_private (conn);
+ if (priv->state == SOUP_CONNECTION_DISCONNECTED)
+ return;
- old_state = priv->state;
soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED);
if (priv->cancellable) {
@@ -912,19 +937,11 @@ soup_connection_disconnect (SoupConnection *conn)
priv->cancellable = NULL;
}
- if (priv->connection) {
- GIOStream *connection;
-
- connection = priv->connection;
- priv->connection = NULL;
-
- g_io_stream_close (connection, NULL, NULL);
- g_signal_handlers_disconnect_by_data (connection, conn);
- g_object_unref (connection);
- }
+ if (priv->io_data &&
+ soup_client_message_io_close_async (priv->io_data, conn,
(GAsyncReadyCallback)client_message_io_closed_cb))
+ return;
- if (old_state != SOUP_CONNECTION_DISCONNECTED)
- g_signal_emit (conn, signals[DISCONNECTED], 0);
+ soup_connection_disconnected (conn);
}
GSocket *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]