diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2023-04-08 09:15:59 +0200 |
---|---|---|
committer | Carlos Garcia Campos <cgarcia@igalia.com> | 2023-05-08 14:58:55 +0200 |
commit | cad8cc385338bf5681e62b82979c4442bc64319b (patch) | |
tree | 8015291b3a94bc8e330149aeb900457588aa2b30 | |
parent | b78cb58ee9b47f8ffe18697ee3b096743a01e6a8 (diff) | |
download | libsoup-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.c | 86 | ||||
-rw-r--r-- | tests/connection-test.c | 56 | ||||
-rw-r--r-- | tests/misc-test.c | 24 |
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 |