summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libsoup/soup-message-io.c8
-rw-r--r--libsoup/soup-message-queue.h3
-rw-r--r--libsoup/soup-session-async.c4
-rw-r--r--tests/misc-test.c362
4 files changed, 370 insertions, 7 deletions
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index a2b6bb50..d721c1df 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -149,8 +149,12 @@ soup_message_io_stop (SoupMessage *msg)
io->unpause_source = NULL;
}
- if (io->read_state < SOUP_MESSAGE_IO_STATE_FINISHING)
- g_io_stream_close (io->iostream, NULL, NULL);
+ if (io->read_state < SOUP_MESSAGE_IO_STATE_FINISHING) {
+ if (io->item && io->item->conn)
+ soup_connection_disconnect (io->item->conn);
+ else
+ g_io_stream_close (io->iostream, NULL, NULL);
+ }
}
void
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index 7e7a3086..54c2971f 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -45,7 +45,8 @@ struct _SoupMessageQueueItem {
guint paused : 1;
guint new_api : 1;
- guint redirection_count : 31;
+ guint io_started : 1;
+ guint redirection_count : 29;
SoupMessageQueueItemState state;
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 0f3aefff..4f882069 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -562,6 +562,7 @@ send_request_restarted (SoupSession *session, SoupMessageQueueItem *item)
{
/* We won't be needing this, then. */
g_object_set_data (G_OBJECT (item->msg), "SoupSessionAsync:ostream", NULL);
+ item->io_started = FALSE;
}
static void
@@ -587,7 +588,7 @@ send_request_finished (SoupSession *session, SoupMessageQueueItem *item)
size = g_memory_output_stream_get_data_size (mostream);
data = size ? g_memory_output_stream_steal_data (mostream) : g_strdup ("");
istream = g_memory_input_stream_new_from_data (data, size, g_free);
- } else {
+ } else if (item->io_started) {
/* The message finished before becoming readable. This
* will happen, eg, if it's cancelled from got-headers.
* Do nothing; the op will complete via read_ready_cb()
@@ -702,6 +703,7 @@ try_run_until_read (SoupMessageQueueItem *item)
static void
send_request_running (SoupSession *session, SoupMessageQueueItem *item)
{
+ item->io_started = TRUE;
try_run_until_read (item);
}
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 261672c3..39d63d3f 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -5,7 +5,9 @@
#include <string.h>
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
#include <libsoup/soup.h>
+#include <libsoup/soup-requester.h>
#include "test-utils.h"
@@ -217,7 +219,7 @@ do_callback_unref_test (void)
GMainLoop *loop;
char *bad_uri;
- debug_printf (1, "\nCallback unref handling\n");
+ debug_printf (1, "\nCallback unref handling (msg api)\n");
/* Get a guaranteed-bad URI */
addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
@@ -269,6 +271,123 @@ do_callback_unref_test (void)
/* Otherwise, if we haven't crashed, we're ok. */
}
+static void
+cur_one_completed (GObject *source, GAsyncResult *result, gpointer session)
+{
+ SoupRequest *one = SOUP_REQUEST (source);
+ GError *error = NULL;
+
+ debug_printf (2, " Request 1 completed\n");
+ if (soup_request_send_finish (one, result, &error)) {
+ debug_printf (1, " Request 1 succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
+ debug_printf (1, " Unexpected error on Request 1: %s\n",
+ error->message);
+ errors++;
+ }
+ g_clear_error (&error);
+
+ g_object_unref (session);
+}
+
+static gboolean
+cur_idle_quit (gpointer loop)
+{
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+cur_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
+{
+ SoupRequest *two = SOUP_REQUEST (source);
+ GError *error = NULL;
+
+ debug_printf (2, " Request 2 completed\n");
+ if (soup_request_send_finish (two, result, &error)) {
+ debug_printf (1, " Request 2 succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANT_CONNECT)) {
+ debug_printf (1, " Unexpected error on Request 2: %s\n",
+ error->message);
+ errors++;
+ }
+ g_clear_error (&error);
+
+ g_idle_add (cur_idle_quit, loop);
+}
+
+static void
+do_callback_unref_req_test (void)
+{
+ SoupServer *bad_server;
+ SoupAddress *addr;
+ SoupSession *session;
+ SoupRequester *requester;
+ SoupRequest *one, *two;
+ GMainLoop *loop;
+ char *bad_uri;
+
+ debug_printf (1, "\nCallback unref handling (request api)\n");
+
+ /* Get a guaranteed-bad URI */
+ addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
+ soup_address_resolve_sync (addr, NULL);
+ bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
+ NULL);
+ g_object_unref (addr);
+
+ bad_uri = g_strdup_printf ("http://127.0.0.1:%u/",
+ soup_server_get_port (bad_server));
+ g_object_unref (bad_server);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+
+ requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
+
+ loop = g_main_loop_new (NULL, TRUE);
+
+ one = soup_requester_request (requester, bad_uri, NULL);
+ g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+ two = soup_requester_request (requester, bad_uri, NULL);
+ g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
+ g_free (bad_uri);
+
+ soup_request_send_async (one, NULL, cur_one_completed, session);
+ g_object_unref (one);
+ soup_request_send_async (two, NULL, cur_two_completed, loop);
+ g_object_unref (two);
+
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ if (session) {
+ g_object_remove_weak_pointer (G_OBJECT (session), (gpointer *)&session);
+ debug_printf (1, " Session not destroyed?\n");
+ errors++;
+ g_object_unref (session);
+ }
+ if (one) {
+ g_object_remove_weak_pointer (G_OBJECT (one), (gpointer *)&one);
+ debug_printf (1, " Request 1 not destroyed?\n");
+ errors++;
+ g_object_unref (one);
+ }
+ if (two) {
+ g_object_remove_weak_pointer (G_OBJECT (two), (gpointer *)&two);
+ debug_printf (1, " Request 2 not destroyed?\n");
+ errors++;
+ g_object_unref (two);
+ }
+
+ /* Otherwise, if we haven't crashed, we're ok. */
+}
+
/* SoupSession should clean up all signal handlers on a message after
* it is finished, allowing the message to be reused if desired.
* #559054
@@ -472,7 +591,7 @@ do_early_abort_test (void)
GMainContext *context;
GMainLoop *loop;
- debug_printf (1, "\nAbort with pending connection\n");
+ debug_printf (1, "\nAbort with pending connection (msg api)\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
msg = soup_message_new_from_uri ("GET", base_uri);
@@ -530,6 +649,138 @@ do_early_abort_test (void)
}
static void
+ear_one_completed (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GError *error = NULL;
+
+ debug_printf (2, " Request 1 completed\n");
+ if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
+ debug_printf (1, " Request 1 succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
+ debug_printf (1, " Unexpected error on Request 1: %s\n",
+ error->message);
+ errors++;
+ }
+ g_clear_error (&error);
+}
+
+static void
+ear_two_completed (GObject *source, GAsyncResult *result, gpointer loop)
+{
+ GError *error = NULL;
+
+ debug_printf (2, " Request 2 completed\n");
+ if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
+ debug_printf (1, " Request 2 succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
+ debug_printf (1, " Unexpected error on Request 2: %s\n",
+ error->message);
+ errors++;
+ }
+ g_clear_error (&error);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop)
+{
+ GError *error = NULL;
+
+ debug_printf (2, " Request 3 completed\n");
+ if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
+ debug_printf (1, " Request 3 succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ debug_printf (1, " Unexpected error on Request 3: %s\n",
+ error->message);
+ errors++;
+ }
+ g_clear_error (&error);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+ear_request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer cancellable)
+{
+ g_cancellable_cancel (cancellable);
+}
+
+static void
+do_early_abort_req_test (void)
+{
+ SoupSession *session;
+ SoupRequester *requester;
+ SoupRequest *req;
+ GMainContext *context;
+ GMainLoop *loop;
+ GCancellable *cancellable;
+
+ debug_printf (1, "\nAbort with pending connection (request api)\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
+ req = soup_requester_request_uri (requester, base_uri, NULL);
+
+ context = g_main_context_default ();
+ loop = g_main_loop_new (context, TRUE);
+ soup_request_send_async (req, NULL, ear_one_completed, NULL);
+ g_object_unref (req);
+ g_main_context_iteration (context, FALSE);
+
+ soup_session_abort (session);
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+ soup_test_session_abort_unref (session);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
+ req = soup_requester_request_uri (requester, base_uri, NULL);
+
+ g_signal_connect (session, "connection-created",
+ G_CALLBACK (ea_connection_created), NULL);
+ soup_request_send_async (req, NULL, ear_two_completed, loop);
+ g_main_loop_run (loop);
+ g_object_unref (req);
+
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+
+ soup_test_session_abort_unref (session);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
+ req = soup_requester_request_uri (requester, base_uri, NULL);
+
+ cancellable = g_cancellable_new ();
+ g_signal_connect (session, "request-started",
+ G_CALLBACK (ear_request_started), cancellable);
+ soup_request_send_async (req, cancellable, ear_three_completed, loop);
+ g_main_loop_run (loop);
+ g_object_unref (req);
+ g_object_unref (cancellable);
+
+ while (g_main_context_pending (context))
+ g_main_context_iteration (context, FALSE);
+
+ soup_test_session_abort_unref (session);
+ g_main_loop_unref (loop);
+}
+
+static void
do_one_accept_language_test (const char *language, const char *expected_header)
{
SoupSession *session;
@@ -642,7 +893,7 @@ do_cancel_while_reading_test (void)
{
SoupSession *session;
- debug_printf (1, "\nCancelling message while reading response\n");
+ debug_printf (1, "\nCancelling message while reading response (msg api)\n");
debug_printf (1, " Async session\n");
session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
@@ -655,6 +906,108 @@ do_cancel_while_reading_test (void)
soup_test_session_abort_unref (session);
}
+static gboolean
+cancel_request_timeout (gpointer cancellable)
+{
+ g_cancellable_cancel (cancellable);
+ return FALSE;
+}
+
+static gpointer
+cancel_request_thread (gpointer cancellable)
+{
+ g_usleep (100000); /* .1s */
+ g_cancellable_cancel (cancellable);
+ return NULL;
+}
+
+static void
+cancel_request_finished (GObject *source, GAsyncResult *result, gpointer loop)
+{
+ GError *error = NULL;
+
+ if (soup_request_send_finish (SOUP_REQUEST (source), result, &error)) {
+ debug_printf (1, " Request succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ debug_printf (1, " Unexpected error: %s\n",
+ error->message);
+ errors++;
+ }
+ g_clear_error (&error);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+do_cancel_while_reading_req_test_for_session (SoupRequester *requester)
+{
+ SoupRequest *req;
+ SoupURI *uri;
+ GCancellable *cancellable;
+
+ uri = soup_uri_new_with_base (base_uri, "/slow");
+ req = soup_requester_request_uri (requester, uri, NULL);
+ soup_uri_free (uri);
+
+ cancellable = g_cancellable_new ();
+
+ if (SOUP_IS_SESSION_ASYNC (soup_request_get_session (req))) {
+ GMainLoop *loop;
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add (100, cancel_request_timeout, cancellable);
+ soup_request_send_async (req, cancellable,
+ cancel_request_finished, loop);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+ } else {
+ GThread *thread;
+ GError *error = NULL;
+
+ thread = g_thread_new ("cancel_request_thread", cancel_request_thread, cancellable);
+ soup_request_send (req, cancellable, &error);
+ if (!error) {
+ debug_printf (1, " Request succeeded?\n");
+ errors++;
+ } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ debug_printf (1, " Unexpected error: %s\n",
+ error->message);
+ errors++;
+ }
+ g_thread_unref (thread);
+ }
+
+ g_object_unref (req);
+ g_object_unref (cancellable);
+}
+
+static void
+do_cancel_while_reading_req_test (void)
+{
+ SoupSession *session;
+ SoupRequester *requester;
+
+ debug_printf (1, "\nCancelling message while reading response (request api)\n");
+
+ debug_printf (1, " Async session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
+ SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
+ NULL);
+ requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
+ do_cancel_while_reading_req_test_for_session (requester);
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " Sync session\n");
+ session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_REQUESTER,
+ NULL);
+ requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER);
+ do_cancel_while_reading_req_test_for_session (requester);
+ soup_test_session_abort_unref (session);
+}
+
static void
do_aliases_test_for_session (SoupSession *session,
const char *redirect_protocol)
@@ -878,11 +1231,14 @@ main (int argc, char **argv)
do_host_test ();
do_callback_unref_test ();
+ do_callback_unref_req_test ();
do_msg_reuse_test ();
do_star_test ();
do_early_abort_test ();
+ do_early_abort_req_test ();
do_accept_language_test ();
do_cancel_while_reading_test ();
+ do_cancel_while_reading_req_test ();
do_aliases_test ();
do_dot_dot_test ();
do_ipv6_test ();