diff options
Diffstat (limited to 'tests/connection-test.c')
-rw-r--r-- | tests/connection-test.c | 289 |
1 files changed, 271 insertions, 18 deletions
diff --git a/tests/connection-test.c b/tests/connection-test.c index 8f8c74be..ec54daea 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -51,16 +51,21 @@ timeout_request_started (SoupServer *server, SoupMessage *msg, SoupClientContext *client, gpointer user_data) { SoupSocket *sock; - GMainContext *context = soup_server_get_async_context (server); + GMainContext *context = g_main_context_get_thread_default (); guint readable; + g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL); + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (client); + G_GNUC_END_IGNORE_DEPRECATIONS; readable = g_signal_connect (sock, "readable", G_CALLBACK (timeout_socket), NULL); + + g_mutex_unlock (&server_mutex); while (soup_socket_is_connected (sock)) g_main_context_iteration (context, TRUE); g_signal_handler_disconnect (sock, readable); - g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL); } static void @@ -78,17 +83,18 @@ setup_timeout_persistent (SoupServer *server, SoupSocket *sock) * fail (since the client is waiting for us to * return a response). This will cause it to * emit "readable" later. - * 2. Connect to the server's request-started signal. - * 3. Run an inner main loop from that signal handler - * until the socket emits "readable". (If we don't - * do this then it's possible the client's next - * request would be ready before we returned to - * the main loop, and so the signal would never be - * emitted.) + * 2. Wait for the server to finish this request and + * start reading the next one (and lock server_mutex + * to interlock with the client and ensure that it + * doesn't start writing its next request until + * that point). + * 3. Block until "readable" is emitted, meaning the + * client has written its request. * 4. Close the socket. */ soup_socket_read (sock, buf, 1, &nread, NULL, NULL); + g_mutex_lock (&server_mutex); g_signal_connect (server, "request-started", G_CALLBACK (timeout_request_started), NULL); } @@ -130,7 +136,9 @@ server_callback (SoupServer *server, SoupMessage *msg, * the declared Content-Length. Instead, we * forcibly close the socket at that point. */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (context); + G_GNUC_END_IGNORE_DEPRECATIONS; g_signal_connect (msg, "wrote-chunk", G_CALLBACK (close_socket), sock); } else if (no_close) { @@ -148,7 +156,9 @@ server_callback (SoupServer *server, SoupMessage *msg, if (!strcmp (path, "/timeout-persistent")) { SoupSocket *sock; + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; sock = soup_client_context_get_socket (context); + G_GNUC_END_IGNORE_DEPRECATIONS; setup_timeout_persistent (server, sock); } @@ -250,6 +260,12 @@ do_timeout_test_for_session (SoupSession *session) } g_object_unref (msg); + /* The server will grab server_mutex before returning the response, + * and release it when it's ready for us to send the second request. + */ + g_mutex_lock (&server_mutex); + g_mutex_unlock (&server_mutex); + debug_printf (1, " Second message\n"); msg = soup_message_new_from_uri ("GET", base_uri); soup_session_send_message (session, msg); @@ -314,6 +330,12 @@ do_timeout_req_test_for_session (SoupSession *session) } g_object_unref (req); + /* The server will grab server_mutex before returning the response, + * and release it when it's ready for us to send the second request. + */ + g_mutex_lock (&server_mutex); + g_mutex_unlock (&server_mutex); + debug_printf (1, " Second request\n"); req = soup_session_request_uri (session, base_uri, NULL); @@ -382,7 +404,7 @@ static GMainLoop *max_conns_loop; static int msgs_done; static guint quit_loop_timeout; #define MAX_CONNS 2 -#define TEST_CONNS (MAX_CONNS * 2) +#define TEST_CONNS (MAX_CONNS * 2) + 1 static gboolean idle_start_server (gpointer data) @@ -403,7 +425,7 @@ static void max_conns_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data) { - if (++msgs_done == MAX_CONNS) { + if (++msgs_done >= MAX_CONNS) { if (quit_loop_timeout) g_source_remove (quit_loop_timeout); quit_loop_timeout = g_timeout_add (100, quit_loop, NULL); @@ -420,7 +442,8 @@ max_conns_message_complete (SoupSession *session, SoupMessage *msg, gpointer use static void do_max_conns_test_for_session (SoupSession *session) { - SoupMessage *msgs[TEST_CONNS]; + SoupMessage *msgs[TEST_CONNS + 1]; + SoupMessageFlags flags; int i; max_conns_loop = g_main_loop_new (NULL, TRUE); @@ -430,7 +453,7 @@ do_max_conns_test_for_session (SoupSession *session) g_signal_connect (session, "request-started", G_CALLBACK (max_conns_request_started), NULL); msgs_done = 0; - for (i = 0; i < TEST_CONNS; i++) { + for (i = 0; i < TEST_CONNS - 1; i++) { msgs[i] = soup_message_new_from_uri ("GET", base_uri); g_object_ref (msgs[i]); soup_session_queue_message (session, msgs[i], @@ -439,6 +462,21 @@ do_max_conns_test_for_session (SoupSession *session) g_main_loop_run (max_conns_loop); g_assert_cmpint (msgs_done, ==, MAX_CONNS); + + if (quit_loop_timeout) + g_source_remove (quit_loop_timeout); + quit_loop_timeout = g_timeout_add (1000, quit_loop, NULL); + + /* Message with SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS should start */ + msgs[i] = soup_message_new_from_uri ("GET", base_uri); + flags = soup_message_get_flags (msgs[i]); + soup_message_set_flags (msgs[i], flags | SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS); + g_object_ref (msgs[i]); + soup_session_queue_message (session, msgs[i], + max_conns_message_complete, NULL); + + g_main_loop_run (max_conns_loop); + g_assert_cmpint (msgs_done, ==, MAX_CONNS + 1); g_signal_handlers_disconnect_by_func (session, max_conns_request_started, NULL); msgs_done = 0; @@ -762,11 +800,46 @@ network_event (SoupMessage *msg, GSocketClientEvent event, { const char **events = user_data; - debug_printf (2, " %s\n", event_name_from_abbrev (**events)); + debug_printf (2, " %s\n", event_names[event]); soup_test_assert (**events == event_abbrevs[event], - "Unexpected event: %s (expected %s)\n", + "Unexpected event: %s (expected %s)", event_names[event], event_name_from_abbrev (**events)); + + if (**events == event_abbrevs[event]) { + if (event == G_SOCKET_CLIENT_RESOLVING || + event == G_SOCKET_CLIENT_RESOLVED) { + soup_test_assert (connection == NULL, + "Unexpectedly got connection (%s) with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else if (event < G_SOCKET_CLIENT_TLS_HANDSHAKING) { + soup_test_assert (G_IS_SOCKET_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING || + event == G_SOCKET_CLIENT_TLS_HANDSHAKED) { + soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else if (event == G_SOCKET_CLIENT_COMPLETE) { + /* See if the previous expected event was TLS_HANDSHAKED */ + if ((*events)[-1] == 'T') { + soup_test_assert (G_IS_TLS_CLIENT_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } else { + soup_test_assert (G_IS_SOCKET_CONNECTION (connection), + "Unexpectedly got %s with '%s' event", + G_OBJECT_TYPE_NAME (connection), + event_names[event]); + } + } + } + *events = *events + 1; } @@ -841,6 +914,186 @@ do_connection_event_test (void) soup_test_session_abort_unref (session); } +typedef struct { + GMainLoop *loop; + GIOStream *stream; + GError *error; + const char *events; +} ConnectTestData; + +static void +connect_progress (SoupSession *session, GSocketClientEvent event, GIOStream *connection, ConnectTestData *data) +{ + soup_test_assert (*data->events == event_abbrevs[event], + "Unexpected event: %s (expected %s)", + event_names[event], + event_name_from_abbrev (*data->events)); + data->events = data->events + 1; +} + +static void +connect_finished (SoupSession *session, GAsyncResult *result, ConnectTestData *data) +{ + data->stream = soup_session_connect_finish (session, result, &data->error); + g_main_loop_quit (data->loop); +} + +static void +do_one_connection_connect_test (SoupSession *session, SoupURI *uri, const char *response, const char *events) +{ + ConnectTestData data = { NULL, NULL, NULL, events }; + static const char *request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"; + gsize bytes = 0; + char buffer[128]; + + data.loop = g_main_loop_new (NULL, FALSE); + soup_session_connect_async (session, uri, NULL, + (SoupSessionConnectProgressCallback)connect_progress, + (GAsyncReadyCallback)connect_finished, + &data); + g_main_loop_run (data.loop); + + g_assert (G_IS_IO_STREAM (data.stream)); + g_assert_no_error (data.error); + g_assert (g_output_stream_write_all (g_io_stream_get_output_stream (data.stream), + request, strlen (request), &bytes, NULL, NULL)); + g_assert (g_input_stream_read_all (g_io_stream_get_input_stream (data.stream), + buffer, sizeof (buffer), &bytes, NULL, NULL)); + buffer[strlen (response)] = '\0'; + g_assert_cmpstr (buffer, ==, response); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } + + g_object_unref (data.stream); + g_main_loop_unref (data.loop); +} + +static void +do_one_connection_connect_fail_test (SoupSession *session, SoupURI *uri, GQuark domain, gint code, const char *events) +{ + ConnectTestData data = { NULL, NULL, NULL, events }; + + data.loop = g_main_loop_new (NULL, FALSE); + soup_session_connect_async (session, uri, NULL, + (SoupSessionConnectProgressCallback)connect_progress, + (GAsyncReadyCallback)connect_finished, + &data); + g_main_loop_run (data.loop); + + g_assert (!data.stream); + g_assert_error (data.error, domain, code); + + while (*data.events) { + soup_test_assert (!*data.events, + "Expected %s", + event_name_from_abbrev (*data.events)); + data.events++; + } +} + +static void +do_connection_connect_test (void) +{ + SoupSession *session; + SoupURI *http_uri; + SoupURI *https_uri = NULL; + SoupURI *ws_uri; + SoupURI *wss_uri = NULL; + SoupURI *file_uri; + SoupURI *wrong_http_uri; + SoupURI *proxy_uri; + + SOUP_TEST_SKIP_IF_NO_APACHE; + + session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + + debug_printf (1, " http\n"); + http_uri = soup_uri_new (HTTP_SERVER); + do_one_connection_connect_test (session, http_uri, + "HTTP/1.1 200 OK", "rRcCx"); + + if (tls_available) { + debug_printf (1, " https\n"); + https_uri = soup_uri_new (HTTPS_SERVER); + do_one_connection_connect_test (session, https_uri, + "HTTP/1.1 200 OK", "rRcCtTx"); + } else + debug_printf (1, " https -- SKIPPING\n"); + + debug_printf (1, " ws\n"); + ws_uri = soup_uri_new (HTTP_SERVER); + ws_uri->scheme = SOUP_URI_SCHEME_WS; + do_one_connection_connect_test (session, ws_uri, + "HTTP/1.1 200 OK", "rRcCx"); + + if (tls_available) { + debug_printf (1, " wss\n"); + wss_uri = soup_uri_new (HTTPS_SERVER); + do_one_connection_connect_test (session, wss_uri, + "HTTP/1.1 200 OK", "rRcCtTx"); + } else + debug_printf (1, " wss -- SKIPPING\n"); + + debug_printf (1, " file\n"); + file_uri = soup_uri_new ("file:///foo/bar"); + do_one_connection_connect_fail_test (session, file_uri, + G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND, + "r"); + + debug_printf (1, " wrong http (invalid port)\n"); + wrong_http_uri = soup_uri_new (HTTP_SERVER); + wrong_http_uri->port = 1234; + do_one_connection_connect_fail_test (session, wrong_http_uri, + G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED, + "rRcr"); /* FIXME: why r again? GLib bug? */ + + proxy_uri = soup_uri_new (HTTP_PROXY); + g_object_set (G_OBJECT (session), + SOUP_SESSION_PROXY_URI, proxy_uri, + NULL); + + debug_printf (1, " http with proxy\n"); + do_one_connection_connect_test (session, http_uri, + "HTTP/1.1 403 Forbidden", "rRcCx"); + + if (tls_available) { + debug_printf (1, " https with proxy\n"); + do_one_connection_connect_test (session, https_uri, + "HTTP/1.1 200 OK", "rRcCpPtTx"); + } else + debug_printf (1, " https with proxy -- SKIPPING\n"); + + debug_printf (1, " ws with proxy\n"); + do_one_connection_connect_test (session, ws_uri, + "HTTP/1.1 403 Forbidden", "rRcCx"); + + if (tls_available) { + debug_printf (1, " wss with proxy\n"); + do_one_connection_connect_test (session, wss_uri, + "HTTP/1.1 200 OK", "rRcCpPtTx"); + } else + debug_printf (1, " wss with proxy -- SKIPPING\n"); + + soup_uri_free (http_uri); + if (https_uri) + soup_uri_free (https_uri); + soup_uri_free (ws_uri); + if (wss_uri) + soup_uri_free (wss_uri); + soup_uri_free (file_uri); + soup_uri_free (wrong_http_uri); + soup_uri_free (proxy_uri); + + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -849,10 +1102,9 @@ main (int argc, char **argv) test_init (argc, argv, NULL); apache_init (); - 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); g_test_add_func ("/connection/content-length-framing", do_content_length_framing_test); g_test_add_func ("/connection/persistent-connection-timeout", do_persistent_connection_timeout_test); @@ -861,6 +1113,7 @@ main (int argc, char **argv) g_test_add_func ("/connection/non-idempotent", do_non_idempotent_connection_test); g_test_add_func ("/connection/state", do_connection_state_test); g_test_add_func ("/connection/event", do_connection_event_test); + g_test_add_func ("/connection/connect", do_connection_connect_test); ret = g_test_run (); |