summaryrefslogtreecommitdiff
path: root/tests/connection-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/connection-test.c')
-rw-r--r--tests/connection-test.c289
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 ();