summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2023-04-08 09:15:59 +0200
committerCarlos Garcia Campos <cgarcia@igalia.com>2023-05-08 14:58:55 +0200
commitcad8cc385338bf5681e62b82979c4442bc64319b (patch)
tree8015291b3a94bc8e330149aeb900457588aa2b30
parentb78cb58ee9b47f8ffe18697ee3b096743a01e6a8 (diff)
downloadlibsoup-master.tar.gz
session: handle request cancellation earlierHEADmastercarlosgc/early-cancellation
Check if the message was cancelled before the request is sent.
-rw-r--r--libsoup/soup-session.c86
-rw-r--r--tests/connection-test.c56
-rw-r--r--tests/misc-test.c24
3 files changed, 139 insertions, 27 deletions
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 1020feed..5a0dce23 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -107,6 +107,9 @@ static void soup_session_process_queue_item (SoupSession *session,
SoupMessageQueueItem *item,
gboolean loop);
+static void async_send_request_return_result (SoupMessageQueueItem *item,
+ gpointer stream, GError *error);
+
#define SOUP_SESSION_MAX_CONNS_DEFAULT 10
#define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
@@ -1366,7 +1369,20 @@ soup_session_append_queue_item (SoupSession *session,
return item;
}
-static void
+static gboolean
+finish_request_if_item_cancelled (SoupMessageQueueItem *item)
+{
+ if (!g_cancellable_is_cancelled (item->cancellable))
+ return FALSE;
+
+ if (item->async)
+ async_send_request_return_result (item, NULL, NULL);
+
+ item->state = SOUP_MESSAGE_FINISHING;
+ return TRUE;
+}
+
+static gboolean
soup_session_send_queue_item (SoupSession *session,
SoupMessageQueueItem *item,
SoupMessageIOCompletionFn completion_cb)
@@ -1405,9 +1421,13 @@ soup_session_send_queue_item (SoupSession *session,
soup_message_headers_set_content_length (request_headers, 0);
}
- soup_message_starting (item->msg);
- if (item->state == SOUP_MESSAGE_RUNNING)
- soup_message_send_item (item->msg, item, completion_cb, item);
+ soup_message_starting (item->msg);
+ finish_request_if_item_cancelled (item);
+ if (item->state != SOUP_MESSAGE_RUNNING)
+ return FALSE;
+
+ soup_message_send_item (item->msg, item, completion_cb, item);
+ return TRUE;
}
static void
@@ -1541,15 +1561,19 @@ tunnel_message_completed (SoupMessage *msg, SoupMessageIOCompletion completion,
g_object_unref (conn);
g_clear_object (&tunnel_item->error);
tunnel_item->state = SOUP_MESSAGE_RUNNING;
- soup_session_send_queue_item (session, tunnel_item,
- (SoupMessageIOCompletionFn)tunnel_message_completed);
- soup_message_io_run (msg, !tunnel_item->async);
+ if (soup_session_send_queue_item (session, tunnel_item, (SoupMessageIOCompletionFn)tunnel_message_completed))
+ soup_message_io_run (msg, !tunnel_item->async);
+ else
+ tunnel_message_completed (msg, SOUP_MESSAGE_IO_INTERRUPTED, tunnel_item);
return;
}
item->state = SOUP_MESSAGE_RESTARTING;
}
+ if (item->state == SOUP_MESSAGE_FINISHING)
+ soup_message_finished (tunnel_item->msg);
+
tunnel_item->state = SOUP_MESSAGE_FINISHED;
soup_session_unqueue_item (session, tunnel_item);
@@ -1601,9 +1625,10 @@ tunnel_connect (SoupMessageQueueItem *item)
g_clear_object (&conn);
tunnel_item->state = SOUP_MESSAGE_RUNNING;
- soup_session_send_queue_item (session, tunnel_item,
- (SoupMessageIOCompletionFn)tunnel_message_completed);
- soup_message_io_run (msg, !item->async);
+ if (soup_session_send_queue_item (session, tunnel_item, (SoupMessageIOCompletionFn)tunnel_message_completed))
+ soup_message_io_run (msg, !item->async);
+ else
+ tunnel_message_completed (msg, SOUP_MESSAGE_IO_INTERRUPTED, tunnel_item);
g_object_unref (msg);
}
@@ -1742,20 +1767,24 @@ soup_session_process_queue_item (SoupSession *session,
switch (item->state) {
case SOUP_MESSAGE_STARTING:
- if (!soup_session_ensure_item_connection (session, item))
- return;
+ if (!finish_request_if_item_cancelled (item)) {
+ if (!soup_session_ensure_item_connection (session, item))
+ return;
+ }
break;
- case SOUP_MESSAGE_CONNECTED: {
- SoupConnection *conn = soup_message_get_connection (item->msg);
+ case SOUP_MESSAGE_CONNECTED:
+ if (!finish_request_if_item_cancelled (item)) {
+ SoupConnection *conn = soup_message_get_connection (item->msg);
- if (soup_connection_is_tunnelled (conn))
+ if (soup_connection_is_tunnelled (conn))
tunnel_connect (item);
- else
- item->state = SOUP_MESSAGE_READY;
- g_object_unref (conn);
+ else
+ item->state = SOUP_MESSAGE_READY;
+ g_object_unref (conn);
+ }
break;
- }
+
case SOUP_MESSAGE_READY:
if (item->connect_only) {
item->state = SOUP_MESSAGE_FINISHING;
@@ -1767,16 +1796,17 @@ soup_session_process_queue_item (SoupSession *session,
break;
}
- item->state = SOUP_MESSAGE_RUNNING;
+ if (!finish_request_if_item_cancelled (item)) {
+ item->state = SOUP_MESSAGE_RUNNING;
- soup_message_set_metrics_timestamp (item->msg, SOUP_MESSAGE_METRICS_REQUEST_START);
+ soup_message_set_metrics_timestamp (item->msg, SOUP_MESSAGE_METRICS_REQUEST_START);
- soup_session_send_queue_item (session, item,
- (SoupMessageIOCompletionFn)message_completed);
+ if (soup_session_send_queue_item (session, item, (SoupMessageIOCompletionFn)message_completed) && item->async)
+ async_send_request_running (session, item);
- if (item->async)
- async_send_request_running (session, item);
- return;
+ return;
+ }
+ break;
case SOUP_MESSAGE_RUNNING:
if (item->async)
@@ -3192,8 +3222,10 @@ soup_session_send (SoupSession *session,
while (!stream) {
/* Get a connection, etc */
soup_session_process_queue_item (session, item, TRUE);
- if (item->state != SOUP_MESSAGE_RUNNING)
+ if (item->state != SOUP_MESSAGE_RUNNING) {
+ g_cancellable_set_error_if_cancelled (item->cancellable, &my_error);
break;
+ }
/* Send request, read headers */
if (!soup_message_io_run_until_read (msg, item->cancellable, &my_error)) {
diff --git a/tests/connection-test.c b/tests/connection-test.c
index 307a695f..2978e796 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -1693,6 +1693,61 @@ do_connection_http_1_1_required_test (void)
soup_test_session_abort_unref (session);
}
+static void
+cancel_tunnel_test_request_queued (SoupSession *session,
+ SoupMessage *msg,
+ GCancellable *cancellable)
+{
+ if (soup_message_get_method (msg) != SOUP_METHOD_CONNECT)
+ return;
+
+ g_signal_connect_swapped (msg, "starting", G_CALLBACK (g_cancellable_cancel), cancellable);
+}
+
+static void
+do_cancel_tunnel_msg_on_starting (void)
+{
+ SoupSession *session;
+ SoupMessage *msg;
+ GProxyResolver *resolver;
+ GCancellable *cancellable;
+ GBytes *body;
+ GError *error = NULL;
+
+ SOUP_TEST_SKIP_IF_NO_TLS
+ SOUP_TEST_SKIP_IF_NO_APACHE;
+
+ session = soup_test_session_new (NULL);
+
+ if (tls_available) {
+ GTlsDatabase *tlsdb;
+
+ tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+ soup_session_set_tls_database (session, tlsdb);
+ g_object_unref (tlsdb);
+ }
+
+ resolver = g_simple_proxy_resolver_new (HTTP_PROXY, NULL);
+ soup_session_set_proxy_resolver (session, resolver);
+ g_object_unref (resolver);
+
+ cancellable = g_cancellable_new ();
+ g_signal_connect (session, "request-queued",
+ G_CALLBACK (cancel_tunnel_test_request_queued),
+ cancellable);
+
+ msg = soup_message_new ("GET", HTTPS_SERVER);
+ body = soup_session_send_and_read (session, msg, cancellable, &error);
+ soup_test_assert_message_status (msg, SOUP_STATUS_NONE);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+ g_object_unref (cancellable);
+
+ soup_test_session_abort_unref (session);
+}
+
int
main (int argc, char **argv)
{
@@ -1719,6 +1774,7 @@ main (int argc, char **argv)
g_test_add_func ("/connection/metrics", do_connection_metrics_test);
g_test_add_func ("/connection/force-http2", do_connection_force_http2_test);
g_test_add_func ("/connection/http2/http-1-1-required", do_connection_http_1_1_required_test);
+ g_test_add_func ("/connection/cancel-tunnel-msg-on-starting", do_cancel_tunnel_msg_on_starting);
ret = g_test_run ();
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 045161d8..4c14bb87 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -430,6 +430,14 @@ ea_message_starting (SoupMessage *msg,
}
static void
+ea_message_queued (SoupSession *session,
+ SoupMessage *msg,
+ GCancellable *cancellable)
+{
+ g_cancellable_cancel (cancellable);
+}
+
+static void
do_early_abort_test (void)
{
SoupSession *session;
@@ -488,6 +496,22 @@ do_early_abort_test (void)
g_object_unref (cancellable);
g_object_unref (msg);
soup_test_session_abort_unref (session);
+
+ session = soup_test_session_new (NULL);
+ msg = soup_message_new_from_uri ("GET", base_uri);
+ cancellable = g_cancellable_new ();
+
+ g_signal_connect (session, "request-queued",
+ G_CALLBACK (ea_message_queued), cancellable);
+ g_assert_null (soup_test_session_async_send (session, msg, cancellable, &error));
+ debug_printf (2, " Message 4 completed\n");
+
+ g_assert_cmpuint (soup_message_get_connection_id (msg), ==, 0);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+ g_object_unref (cancellable);
+ g_object_unref (msg);
+ soup_test_session_abort_unref (session);
}
static void