[libsoup] Fix a few recent request API bugs, add some more tests



commit 62b501232e0657d55d8dc3a1570e817b29e8d4c7
Author: Dan Winship <danw gnome org>
Date:   Thu May 3 20:20:29 2012 -0400

    Fix a few recent request API bugs, add some more tests

 libsoup/soup-message-io.c    |    8 +-
 libsoup/soup-message-queue.h |    3 +-
 libsoup/soup-session-async.c |    4 +-
 tests/misc-test.c            |  362 +++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 370 insertions(+), 7 deletions(-)
---
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 <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 ();



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]