diff options
Diffstat (limited to 'tests/misc-test.c')
-rw-r--r-- | tests/misc-test.c | 361 |
1 files changed, 319 insertions, 42 deletions
diff --git a/tests/misc-test.c b/tests/misc-test.c index 00559a03..8cbda80a 100644 --- a/tests/misc-test.c +++ b/tests/misc-test.c @@ -73,7 +73,7 @@ server_callback (SoupServer *server, SoupMessage *msg, if (!strcmp (path, "/slow")) { soup_server_pause_message (server, msg); g_object_set_data (G_OBJECT (msg), "server", server); - soup_add_timeout (soup_server_get_async_context (server), + soup_add_timeout (g_main_context_get_thread_default (), 1000, timeout_finish_message, msg); } @@ -120,6 +120,38 @@ do_host_test (void) g_object_unref (two); } +/* request with too big header should be discarded with a IO error to + * prevent DOS attacks. + */ +static void +do_host_big_header (void) +{ + SoupMessage *msg; + SoupSession *session; + int i; + + g_test_bug ("792173"); + + session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL); + + msg = soup_message_new_from_uri ("GET", base_uri); + for (i = 0; i < 2048; i++) { + char *key = g_strdup_printf ("test-long-header-key%d", i); + char *value = g_strdup_printf ("test-long-header-key%d", i); + soup_message_headers_append (msg->request_headers, key, value); + g_free (value); + g_free (key); + } + + soup_session_send_message (session, msg); + + soup_test_session_abort_unref (session); + + soup_test_assert_message_status (msg, SOUP_STATUS_IO_ERROR); + + g_object_unref (msg); +} + /* Dropping the application's ref on the session from a callback * should not cause the session to be freed at an incorrect time. * (This test will crash if it fails.) @@ -151,35 +183,28 @@ static void do_callback_unref_test (void) { SoupServer *bad_server; - SoupAddress *addr; SoupSession *session; SoupMessage *one, *two; GMainLoop *loop; - char *bad_uri; + SoupURI *bad_uri; g_test_bug ("533473"); /* 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); + bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + bad_uri = soup_test_server_get_uri (bad_server, "http", NULL); + soup_test_server_quit_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session); loop = g_main_loop_new (NULL, TRUE); - one = soup_message_new ("GET", bad_uri); + one = soup_message_new_from_uri ("GET", bad_uri); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_message_new ("GET", bad_uri); + two = soup_message_new_from_uri ("GET", bad_uri); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); - g_free (bad_uri); + soup_uri_free (bad_uri); soup_session_queue_message (session, one, cu_one_completed, loop); soup_session_queue_message (session, two, cu_two_completed, loop); @@ -245,22 +270,15 @@ static void do_callback_unref_req_test (void) { SoupServer *bad_server; - SoupAddress *addr; SoupSession *session; SoupRequest *one, *two; GMainLoop *loop; - char *bad_uri; + SoupURI *bad_uri; /* 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); + bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT); + bad_uri = soup_test_server_get_uri (bad_server, "http", NULL); + soup_test_server_quit_unref (bad_server); session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, @@ -269,11 +287,11 @@ do_callback_unref_req_test (void) loop = g_main_loop_new (NULL, TRUE); - one = soup_session_request (session, bad_uri, NULL); + one = soup_session_request_uri (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one); - two = soup_session_request (session, bad_uri, NULL); + two = soup_session_request_uri (session, bad_uri, NULL); g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two); - g_free (bad_uri); + soup_uri_free (bad_uri); soup_request_send_async (one, NULL, cur_one_completed, session); g_object_unref (one); @@ -418,7 +436,7 @@ ea_connection_created (SoupSession *session, GObject *conn, gpointer user_data) } static void -ea_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data) +ea_message_starting (SoupMessage *msg, SoupSession *session) { soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); } @@ -469,8 +487,8 @@ do_early_abort_test (void) session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL); msg = soup_message_new_from_uri ("GET", base_uri); - g_signal_connect (session, "request-started", - G_CALLBACK (ea_request_started), NULL); + g_signal_connect (msg, "starting", + G_CALLBACK (ea_message_starting), session); soup_session_send_message (session, msg); debug_printf (2, " Message 3 completed\n"); @@ -521,13 +539,21 @@ ear_three_completed (GObject *source, GAsyncResult *result, gpointer loop) } static void -ear_request_started (SoupSession *session, SoupMessage *msg, - SoupSocket *socket, gpointer cancellable) +ear_message_starting (SoupMessage *msg, gpointer cancellable) { g_cancellable_cancel (cancellable); } static void +ear_request_queued (SoupSession *session, SoupMessage *msg, + gpointer cancellable) +{ + g_signal_connect (msg, "starting", + G_CALLBACK (ear_message_starting), + cancellable); +} + +static void do_early_abort_req_test (void) { SoupSession *session; @@ -574,8 +600,8 @@ do_early_abort_req_test (void) req = soup_session_request_uri (session, base_uri, NULL); cancellable = g_cancellable_new (); - g_signal_connect (session, "request-started", - G_CALLBACK (ear_request_started), cancellable); + g_signal_connect (session, "request-queued", + G_CALLBACK (ear_request_queued), cancellable); soup_request_send_async (req, cancellable, ear_three_completed, loop); g_main_loop_run (loop); g_object_unref (req); @@ -914,6 +940,254 @@ do_pause_abort_test (void) g_assert_null (ptr); } +static GMainLoop *pause_cancel_loop; + +static void +pause_cancel_got_headers (SoupMessage *msg, gpointer user_data) +{ + SoupSession *session = user_data; + + soup_session_pause_message (session, msg); + g_main_loop_quit (pause_cancel_loop); +} + +static void +pause_cancel_finished (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + gboolean *finished = user_data; + + *finished = TRUE; + g_main_loop_quit (pause_cancel_loop); +} + +static gboolean +pause_cancel_timeout (gpointer user_data) +{ + gboolean *timed_out = user_data; + + *timed_out = TRUE; + g_main_loop_quit (pause_cancel_loop); + return FALSE; +} + +static void +do_pause_cancel_test (void) +{ + SoupSession *session; + SoupMessage *msg; + gboolean finished = FALSE, timed_out = FALSE; + guint timeout_id; + + g_test_bug ("745094"); + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + pause_cancel_loop = g_main_loop_new (NULL, FALSE); + + timeout_id = g_timeout_add_seconds (5, pause_cancel_timeout, &timed_out); + + msg = soup_message_new_from_uri ("GET", base_uri); + g_object_ref (msg); + g_signal_connect (msg, "got-headers", + G_CALLBACK (pause_cancel_got_headers), session); + + soup_session_queue_message (session, msg, pause_cancel_finished, &finished); + g_main_loop_run (pause_cancel_loop); + g_assert_false (finished); + + soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); + g_main_loop_run (pause_cancel_loop); + g_assert_true (finished); + g_assert_false (timed_out); + + soup_test_assert_message_status (msg, SOUP_STATUS_CANCELLED); + g_object_unref (msg); + + soup_test_session_abort_unref (session); + g_main_loop_unref (pause_cancel_loop); + if (!timed_out) + g_source_remove (timeout_id); +} + +static gboolean +run_echo_server (gpointer user_data) +{ + GIOStream *stream = user_data; + GInputStream *istream; + GDataInputStream *distream; + GOutputStream *ostream; + char *str, *caps; + gssize n; + GError *error = NULL; + + istream = g_io_stream_get_input_stream (stream); + distream = G_DATA_INPUT_STREAM (g_data_input_stream_new (istream)); + ostream = g_io_stream_get_output_stream (stream); + + /* Echo until the client disconnects */ + while (TRUE) { + str = g_data_input_stream_read_line (distream, NULL, NULL, &error); + g_assert_no_error (error); + if (!str) + break; + + caps = g_ascii_strup (str, -1); + n = g_output_stream_write (ostream, caps, strlen (caps), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, strlen (caps)); + n = g_output_stream_write (ostream, "\n", 1, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, 1); + g_free (caps); + g_free (str); + } + + g_object_unref (distream); + + g_io_stream_close (stream, NULL, &error); + g_assert_no_error (error); + g_object_unref (stream); + + return FALSE; +} + +static void +steal_after_upgrade (SoupMessage *msg, gpointer user_data) +{ + SoupClientContext *context = user_data; + GIOStream *stream; + GSource *source; + + /* This should not ever be seen. */ + soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR); + + stream = soup_client_context_steal_connection (context); + + source = g_idle_source_new (); + g_source_set_callback (source, run_echo_server, stream, NULL); + g_source_attach (source, g_main_context_get_thread_default ()); + g_source_unref (source); +} + +static void +upgrade_server_callback (SoupServer *server, SoupMessage *msg, + const char *path, GHashTable *query, + SoupClientContext *context, gpointer data) +{ + if (msg->method != SOUP_METHOD_GET) { + soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED); + return; + } + + soup_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS); + soup_message_headers_append (msg->request_headers, "Upgrade", "ECHO"); + soup_message_headers_append (msg->request_headers, "Connection", "upgrade"); + + g_signal_connect (msg, "wrote-informational", + G_CALLBACK (steal_after_upgrade), context); +} + +static void +callback_not_reached (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + g_assert_not_reached (); +} + +static void +switching_protocols (SoupMessage *msg, gpointer user_data) +{ + GIOStream **out_iostream = user_data; + SoupSession *session = g_object_get_data (G_OBJECT (msg), "SoupSession"); + + *out_iostream = soup_session_steal_connection (session, msg); +} + +static void +do_stealing_test (gconstpointer data) +{ + gboolean sync = GPOINTER_TO_INT (data); + SoupServer *server; + SoupURI *uri; + SoupSession *session; + SoupMessage *msg; + GIOStream *iostream; + GInputStream *istream; + GDataInputStream *distream; + GOutputStream *ostream; + int i; + gssize n; + char *str, *caps; + GError *error = NULL; + static const char *strings[] = { "one", "two", "three", "four", "five" }; + + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + uri = soup_test_server_get_uri (server, SOUP_URI_SCHEME_HTTP, "127.0.0.1"); + soup_server_add_handler (server, NULL, upgrade_server_callback, NULL, NULL); + + session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + msg = soup_message_new_from_uri ("GET", uri); + soup_message_headers_append (msg->request_headers, "Upgrade", "echo"); + soup_message_headers_append (msg->request_headers, "Connection", "upgrade"); + g_object_set_data (G_OBJECT (msg), "SoupSession", session); + + soup_message_add_status_code_handler (msg, "got-informational", + SOUP_STATUS_SWITCHING_PROTOCOLS, + G_CALLBACK (switching_protocols), &iostream); + + iostream = NULL; + + if (sync) { + soup_session_send_message (session, msg); + soup_test_assert_message_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS); + } else { + g_object_ref (msg); + soup_session_queue_message (session, msg, callback_not_reached, NULL); + while (iostream == NULL) + g_main_context_iteration (NULL, TRUE); + } + + g_assert (iostream != NULL); + + g_object_unref (msg); + soup_test_session_abort_unref (session); + soup_uri_free (uri); + + /* Now iostream connects to a (capitalizing) echo server */ + + istream = g_io_stream_get_input_stream (iostream); + distream = G_DATA_INPUT_STREAM (g_data_input_stream_new (istream)); + ostream = g_io_stream_get_output_stream (iostream); + + for (i = 0; i < G_N_ELEMENTS (strings); i++) { + n = g_output_stream_write (ostream, strings[i], strlen (strings[i]), + NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, strlen (strings[i])); + n = g_output_stream_write (ostream, "\n", 1, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (n, ==, 1); + } + + for (i = 0; i < G_N_ELEMENTS (strings); i++) { + str = g_data_input_stream_read_line (distream, NULL, NULL, &error); + g_assert_no_error (error); + caps = g_ascii_strup (strings[i], -1); + g_assert_cmpstr (caps, ==, str); + g_free (caps); + g_free (str); + } + + g_object_unref (distream); + + g_io_stream_close (iostream, NULL, &error); + g_assert_no_error (error); + g_object_unref (iostream); + + /* We can't do this until the end because it's in another thread, and + * soup_test_server_quit_unref() will wait for that thread to exit. + */ + soup_test_server_quit_unref (server); +} + int main (int argc, char **argv) { @@ -922,10 +1196,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); - server = soup_test_server_new (TRUE); + server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (server, NULL, server_callback, "http", NULL); - base_uri = soup_uri_new ("http://127.0.0.1/"); - soup_uri_set_port (base_uri, soup_server_get_port (server)); + base_uri = soup_test_server_get_uri (server, "http", NULL); auth_domain = soup_auth_domain_basic_new ( SOUP_AUTH_DOMAIN_REALM, "misc-test", @@ -936,12 +1209,12 @@ main (int argc, char **argv) g_object_unref (auth_domain); if (tls_available) { - ssl_server = soup_test_server_new_ssl (TRUE); + ssl_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL); - ssl_base_uri = soup_uri_new ("https://127.0.0.1/"); - soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server)); + ssl_base_uri = soup_test_server_get_uri (ssl_server, "https", "127.0.0.1"); } + g_test_add_func ("/misc/bigheader", do_host_big_header); g_test_add_func ("/misc/host", do_host_test); g_test_add_func ("/misc/callback-unref/msg", do_callback_unref_test); g_test_add_func ("/misc/callback-unref/req", do_callback_unref_req_test); @@ -956,6 +1229,9 @@ main (int argc, char **argv) g_test_add_func ("/misc/aliases", do_aliases_test); g_test_add_func ("/misc/idle-on-dispose", do_idle_on_dispose_test); g_test_add_func ("/misc/pause-abort", do_pause_abort_test); + g_test_add_func ("/misc/pause-cancel", do_pause_cancel_test); + g_test_add_data_func ("/misc/stealing/async", GINT_TO_POINTER (FALSE), do_stealing_test); + g_test_add_data_func ("/misc/stealing/sync", GINT_TO_POINTER (TRUE), do_stealing_test); ret = g_test_run (); @@ -967,5 +1243,6 @@ main (int argc, char **argv) soup_test_server_quit_unref (ssl_server); } + test_cleanup (); return ret; } |