From: Dan Winship Date: Fri, 4 May 2012 00:20:29 +0000 (-0400) Subject: Fix a few recent request API bugs, add some more tests X-Git-Tag: LIBSOUP_2_39_2~31 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=62b501232e0657d55d8dc3a1570e817b29e8d4c7;p=platform%2Fupstream%2Flibsoup.git Fix a few recent request API bugs, add some more tests --- diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c index a2b6bb5..d721c1d 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 7e7a308..54c2971 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 0f3aeff..4f88206 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 261672c..39d63d3 100644 --- a/tests/misc-test.c +++ b/tests/misc-test.c @@ -5,7 +5,9 @@ #include +#define LIBSOUP_USE_UNSTABLE_REQUEST_API #include +#include #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 ();