diff options
author | Seonah Moon <seonah1.moon@samsung.com> | 2020-02-28 12:54:59 +0900 |
---|---|---|
committer | Seonah Moon <seonah1.moon@samsung.com> | 2020-02-28 12:55:12 +0900 |
commit | b74c5fd5483969d55625576448662edf864b6b9e (patch) | |
tree | e3cd0fca2b0804eb86ec06c49c5495444a86f98d /tests/websocket-test.c | |
parent | 58894334cd3f0b89c865304e8e9d6eea3cd20a55 (diff) | |
download | libsoup-upstream/2.69.0.tar.gz libsoup-upstream/2.69.0.tar.bz2 libsoup-upstream/2.69.0.zip |
Imported Upstream version 2.69.90upstream/2.69.90upstream/2.69.0
Change-Id: I484cce89dab3187f91ab6e005e265514a03028bd
Diffstat (limited to 'tests/websocket-test.c')
-rw-r--r-- | tests/websocket-test.c | 1214 |
1 files changed, 1172 insertions, 42 deletions
diff --git a/tests/websocket-test.c b/tests/websocket-test.c index 722ccbdf..5e40cf36 100644 --- a/tests/websocket-test.c +++ b/tests/websocket-test.c @@ -20,6 +20,8 @@ #include "test-utils.h" +#include <zlib.h> + typedef struct { GSocket *listener; gushort port; @@ -35,6 +37,11 @@ typedef struct { gboolean no_server; GIOStream *raw_server; + gboolean enable_extensions; + gboolean disable_deflate_in_message; + + GList *initial_cookies; + GMutex mutex; } Test; @@ -100,15 +107,26 @@ direct_connection_complete (GObject *object, GSocketConnection *conn; SoupURI *uri; GError *error = NULL; + GList *extensions = NULL; conn = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (object), result, &error); g_assert_no_error (error); uri = soup_uri_new ("http://127.0.0.1/"); - test->client = soup_websocket_connection_new (G_IO_STREAM (conn), uri, - SOUP_WEBSOCKET_CONNECTION_CLIENT, - NULL, NULL); + if (test->enable_extensions) { + SoupWebsocketExtension *extension; + + extension = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL); + g_assert_true (soup_websocket_extension_configure (extension, + SOUP_WEBSOCKET_CONNECTION_CLIENT, + NULL, NULL)); + extensions = g_list_prepend (extensions, extension); + } + test->client = soup_websocket_connection_new_with_extensions (G_IO_STREAM (conn), uri, + SOUP_WEBSOCKET_CONNECTION_CLIENT, + NULL, NULL, + extensions); soup_uri_free (uri); g_object_unref (conn); } @@ -122,6 +140,7 @@ got_connection (GSocket *listener, GSocket *sock; GSocketConnection *conn; SoupURI *uri; + GList *extensions = NULL; GError *error = NULL; sock = g_socket_accept (listener, NULL, &error); @@ -135,9 +154,19 @@ got_connection (GSocket *listener, test->raw_server = G_IO_STREAM (conn); else { uri = soup_uri_new ("http://127.0.0.1/"); - test->server = soup_websocket_connection_new (G_IO_STREAM (conn), uri, - SOUP_WEBSOCKET_CONNECTION_SERVER, - NULL, NULL); + if (test->enable_extensions) { + SoupWebsocketExtension *extension; + + extension = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL); + g_assert_true (soup_websocket_extension_configure (extension, + SOUP_WEBSOCKET_CONNECTION_SERVER, + NULL, NULL)); + extensions = g_list_prepend (extensions, extension); + } + test->server = soup_websocket_connection_new_with_extensions (G_IO_STREAM (conn), uri, + SOUP_WEBSOCKET_CONNECTION_SERVER, + NULL, NULL, + extensions); soup_uri_free (uri); g_object_unref (conn); } @@ -171,6 +200,14 @@ setup_direct_connection (Test *test, } static void +setup_direct_connection_with_extensions (Test *test, + gconstpointer data) +{ + test->enable_extensions = TRUE; + setup_direct_connection (test, data); +} + +static void setup_half_direct_connection (Test *test, gconstpointer data) { @@ -179,6 +216,14 @@ setup_half_direct_connection (Test *test, } static void +setup_half_direct_connection_with_extensions (Test *test, + gconstpointer data) +{ + test->no_server = TRUE; + setup_direct_connection_with_extensions (test, data); +} + +static void teardown_direct_connection (Test *test, gconstpointer data) { @@ -200,6 +245,8 @@ setup_soup_server (Test *test, setup_listener (test); test->soup_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + if (!test->enable_extensions) + soup_server_remove_websocket_extension (test->soup_server, SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE); soup_server_listen_socket (test->soup_server, test->listener, 0, &error); g_assert_no_error (error); @@ -216,12 +263,25 @@ client_connect (Test *test, gpointer user_data) { char *url; + SoupCookieJar *jar; + GList *l; + + test->session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + if (test->enable_extensions) + soup_session_add_feature_by_type (test->session, SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER); - if (!test->session) - test->session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + jar = soup_cookie_jar_new (); + soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); + soup_session_add_feature (test->session, SOUP_SESSION_FEATURE (jar)); + for (l = test->initial_cookies; l; l = g_list_next (l)) + soup_cookie_jar_add_cookie (jar, (SoupCookie *)l->data); + g_clear_pointer (&test->initial_cookies, g_list_free); + g_object_unref (jar); url = g_strdup_printf ("ws://127.0.0.1:%u/unix", test->port); test->msg = soup_message_new ("GET", url); + if (test->disable_deflate_in_message) + soup_message_disable_feature (test->msg, SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE); g_free (url); soup_session_websocket_connect_async (test->session, test->msg, @@ -264,6 +324,14 @@ setup_soup_connection (Test *test, } static void +setup_soup_connection_with_extensions (Test *test, + gconstpointer data) +{ + test->enable_extensions = TRUE; + setup_soup_connection (test, data); +} + +static void teardown_soup_connection (Test *test, gconstpointer data) { @@ -292,6 +360,21 @@ on_text_message (SoupWebsocketConnection *ws, } static void +on_binary_message (SoupWebsocketConnection *ws, + SoupWebsocketDataType type, + GBytes *message, + gpointer user_data) +{ + GBytes **receive = user_data; + + g_assert_cmpint (type, ==, SOUP_WEBSOCKET_DATA_BINARY); + g_assert (*receive == NULL); + g_assert (message != NULL); + + *receive = g_bytes_ref (message); +} + +static void on_close_set_flag (SoupWebsocketConnection *ws, gpointer user_data) { @@ -308,15 +391,86 @@ test_handshake (Test *test, gconstpointer data) { g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_OPEN); + if (test->enable_extensions) { + GList *extensions = soup_websocket_connection_get_extensions (test->client); + + g_assert_nonnull (extensions); + g_assert_cmpuint (g_list_length (extensions), ==, 1); + g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (extensions->data)); + } else { + g_assert_null (soup_websocket_connection_get_extensions (test->client)); + } + g_assert_cmpint (soup_websocket_connection_get_state (test->server), ==, SOUP_WEBSOCKET_STATE_OPEN); + if (test->enable_extensions) { + GList *extensions = soup_websocket_connection_get_extensions (test->server); + + g_assert_nonnull (extensions); + g_assert_cmpuint (g_list_length (extensions), ==, 1); + g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (extensions->data)); + } else { + g_assert_null (soup_websocket_connection_get_extensions (test->server)); + } + +} + +static void +websocket_server_request_started (SoupServer *server, SoupMessage *msg, + SoupClientContext *client, gpointer user_data) +{ + soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Extensions", "x-foo"); +} + +static void +request_unqueued (SoupSession *session, + SoupMessage *msg, + gpointer data) +{ + Test *test = data; + + if (test->msg == msg) + g_clear_object (&test->msg); +} + + +static void +test_handshake_unsupported_extension (Test *test, + gconstpointer data) +{ + char *url; + + setup_listener (test); + test->soup_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD); + soup_server_listen_socket (test->soup_server, test->listener, 0, NULL); + g_signal_connect (test->soup_server, "request-started", + G_CALLBACK (websocket_server_request_started), + NULL); + soup_server_add_websocket_handler (test->soup_server, "/unix", NULL, NULL, + got_server_connection, test, NULL); + + test->session = soup_test_session_new (SOUP_TYPE_SESSION, NULL); + g_signal_connect (test->session, "request-unqueued", + G_CALLBACK (request_unqueued), + test); + url = g_strdup_printf ("ws://127.0.0.1:%u/unix", test->port); + test->msg = soup_message_new ("GET", url); + g_free (url); + + soup_session_websocket_connect_async (test->session, test->msg, NULL, NULL, NULL, + got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->msg == NULL); + g_assert_error (test->client_error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE); } #define TEST_STRING "this is a test" +#define TEST_STRING_WITH_NULL "this is\0 a test" static void test_send_client_to_server (Test *test, gconstpointer data) { + GBytes *sent; GBytes *received = NULL; const char *contents; gsize len; @@ -331,14 +485,23 @@ test_send_client_to_server (Test *test, contents = g_bytes_get_data (received, &len); g_assert_cmpstr (contents, ==, TEST_STRING); g_assert_cmpint (len, ==, strlen (TEST_STRING)); + g_clear_pointer (&received, g_bytes_unref); - g_bytes_unref (received); + sent = g_bytes_new_static (TEST_STRING_WITH_NULL, sizeof (TEST_STRING_WITH_NULL)); + soup_websocket_connection_send_message (test->client, SOUP_WEBSOCKET_DATA_TEXT, sent); + + WAIT_UNTIL (received != NULL); + + g_assert (g_bytes_equal (sent, received)); + g_clear_pointer (&sent, g_bytes_unref); + g_clear_pointer (&received, g_bytes_unref); } static void test_send_server_to_client (Test *test, gconstpointer data) { + GBytes *sent; GBytes *received = NULL; const char *contents; gsize len; @@ -353,8 +516,16 @@ test_send_server_to_client (Test *test, contents = g_bytes_get_data (received, &len); g_assert_cmpstr (contents, ==, TEST_STRING); g_assert_cmpint (len, ==, strlen (TEST_STRING)); + g_clear_pointer (&received, g_bytes_unref); - g_bytes_unref (received); + sent = g_bytes_new_static (TEST_STRING_WITH_NULL, sizeof (TEST_STRING_WITH_NULL)); + soup_websocket_connection_send_message (test->server, SOUP_WEBSOCKET_DATA_TEXT, sent); + + WAIT_UNTIL (received != NULL); + + g_assert (g_bytes_equal (sent, received)); + g_clear_pointer (&sent, g_bytes_unref); + g_clear_pointer (&received, g_bytes_unref); } static void @@ -396,6 +567,37 @@ test_send_big_packets (Test *test, } static void +test_send_empty_packets (Test *test, + gconstpointer data) +{ + GBytes *received = NULL; + gulong id; + + id = g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); + + soup_websocket_connection_send_text (test->server, "\0"); + WAIT_UNTIL (received != NULL); + g_assert_nonnull (g_bytes_get_data (received, NULL)); + g_assert_cmpuint (((char *) g_bytes_get_data (received, NULL))[0], ==, '\0'); + g_assert_cmpuint (g_bytes_get_size (received), ==, 0); + g_bytes_unref (received); + received = NULL; + g_signal_handler_disconnect (test->client, id); + + id = g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received); + + soup_websocket_connection_send_binary (test->server, NULL, 0); + WAIT_UNTIL (received != NULL); + /* We always include at least a null character */ + g_assert_nonnull (g_bytes_get_data (received, NULL)); + g_assert_cmpuint (((char *) g_bytes_get_data (received, NULL))[0], ==, '\0'); + g_assert_cmpuint (g_bytes_get_size (received), ==, 0); + g_bytes_unref (received); + received = NULL; + g_signal_handler_disconnect (test->client, id); +} + +static void test_send_bad_data (Test *test, gconstpointer unused) { @@ -403,24 +605,27 @@ test_send_bad_data (Test *test, GIOStream *io; gsize written; const char *frame; + gboolean close_event = FALSE; g_signal_handlers_disconnect_by_func (test->server, on_error_not_reached, NULL); g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error); + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event); io = soup_websocket_connection_get_io_stream (test->client); /* Bad UTF-8 frame */ - frame = "\x81\x04\xEE\xEE\xEE\xEE"; + frame = "\x81\x84\x00\x00\x00\x00\xEE\xEE\xEE\xEE"; if (!g_output_stream_write_all (g_io_stream_get_output_stream (io), - frame, 6, &written, NULL, NULL)) + frame, 10, &written, NULL, NULL)) g_assert_not_reached (); - g_assert_cmpuint (written, ==, 6); + g_assert_cmpuint (written, ==, 10); WAIT_UNTIL (error != NULL); g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_BAD_DATA); g_clear_error (&error); WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + g_assert (close_event); g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_BAD_DATA); } @@ -616,9 +821,28 @@ test_protocol_client_any_soup (Test *test, g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Protocol"), ==, NULL); } +static const struct { + gushort code; + const char *reason; + gushort expected_sender_code; + const char *expected_sender_reason; + gushort expected_receiver_code; + const char *expected_receiver_reason; +} close_clean_tests[] = { + { SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL" }, + { SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY" }, + { SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL }, + { SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL }, +}; + static void -test_close_clean_client (Test *test, - gconstpointer data) +do_close_clean_client (Test *test, + gushort code, + const char *reason, + gushort expected_sender_code, + const char *expected_sender_reason, + gushort expected_receiver_code, + const char *expected_receiver_reason) { gboolean close_event_client = FALSE; gboolean close_event_server = FALSE; @@ -626,7 +850,7 @@ test_close_clean_client (Test *test, g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client); g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server); - soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "give me a reason"); + soup_websocket_connection_close (test->client, code, reason); g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING); WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED); @@ -635,14 +859,62 @@ test_close_clean_client (Test *test, g_assert (close_event_client); g_assert (close_event_server); - g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); - g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); - g_assert_cmpstr (soup_websocket_connection_get_close_data (test->server), ==, "give me a reason"); + g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, expected_sender_code); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, expected_sender_reason); + g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, expected_receiver_code); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->server), ==, expected_receiver_reason); } static void -test_close_clean_server (Test *test, - gconstpointer data) +test_close_clean_client_soup (Test *test, + gconstpointer data) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + setup_soup_connection (test, data); + + do_close_clean_client (test, + close_clean_tests[i].code, + close_clean_tests[i].reason, + close_clean_tests[i].expected_sender_code, + close_clean_tests[i].expected_sender_reason, + close_clean_tests[i].expected_receiver_code, + close_clean_tests[i].expected_receiver_reason); + + teardown_soup_connection (test, data); + } +} + +static void +test_close_clean_client_direct (Test *test, + gconstpointer data) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + setup_direct_connection (test, data); + + do_close_clean_client (test, + close_clean_tests[i].code, + close_clean_tests[i].reason, + close_clean_tests[i].expected_sender_code, + close_clean_tests[i].expected_sender_reason, + close_clean_tests[i].expected_receiver_code, + close_clean_tests[i].expected_receiver_reason); + + teardown_direct_connection (test, data); + } +} + +static void +do_close_clean_server (Test *test, + gushort code, + const char *reason, + gushort expected_sender_code, + const char *expected_sender_reason, + gushort expected_receiver_code, + const char *expected_receiver_reason) { gboolean close_event_client = FALSE; gboolean close_event_server = FALSE; @@ -650,7 +922,7 @@ test_close_clean_server (Test *test, g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client); g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server); - soup_websocket_connection_close (test->server, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "another reason"); + soup_websocket_connection_close (test->server, code, reason); g_assert_cmpint (soup_websocket_connection_get_state (test->server), ==, SOUP_WEBSOCKET_STATE_CLOSING); WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED); @@ -659,9 +931,52 @@ test_close_clean_server (Test *test, g_assert (close_event_client); g_assert (close_event_server); - g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); - g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_GOING_AWAY); - g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "another reason"); + g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, expected_sender_code); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->server), ==, expected_sender_reason); + g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, expected_receiver_code); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, expected_receiver_reason); +} + +static void +test_close_clean_server_soup (Test *test, + gconstpointer data) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + setup_direct_connection (test, data); + + do_close_clean_server (test, + close_clean_tests[i].code, + close_clean_tests[i].reason, + close_clean_tests[i].expected_sender_code, + close_clean_tests[i].expected_sender_reason, + close_clean_tests[i].expected_receiver_code, + close_clean_tests[i].expected_receiver_reason); + + teardown_direct_connection (test, data); + } +} + +static void +test_close_clean_server_direct (Test *test, + gconstpointer data) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) { + setup_direct_connection (test, data); + + do_close_clean_server (test, + close_clean_tests[i].code, + close_clean_tests[i].reason, + close_clean_tests[i].expected_sender_code, + close_clean_tests[i].expected_sender_reason, + close_clean_tests[i].expected_receiver_code, + close_clean_tests[i].expected_receiver_reason); + + teardown_direct_connection (test, data); + } } static gboolean @@ -707,6 +1022,80 @@ test_message_after_closing (Test *test, } static gpointer +close_after_close_server_thread (gpointer user_data) +{ + Test *test = user_data; + gsize written; + const char frames[] = + "\x88\x09\x03\xe8""reason1" + "\x88\x09\x03\xe8""reason2"; + GSocket *socket; + GError *error = NULL; + + g_mutex_lock (&test->mutex); + g_mutex_unlock (&test->mutex); + + g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server), + frames, sizeof (frames) -1, &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, sizeof (frames) - 1); + socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (test->raw_server)); + g_socket_shutdown (socket, FALSE, TRUE, &error); + g_assert_no_error (error); + + return NULL; +} + +static void +test_close_after_close (Test *test, + gconstpointer data) +{ + GThread *thread; + + g_mutex_lock (&test->mutex); + + thread = g_thread_new ("close-after-close-thread", close_after_close_server_thread, test); + + soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_NORMAL, "reason1"); + g_mutex_unlock (&test->mutex); + + g_thread_join (thread); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL); + g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1"); + g_io_stream_close (test->raw_server, NULL, NULL); +} + +static gboolean +on_close_unref_connection (SoupWebsocketConnection *ws, + gpointer user_data) +{ + Test *test = user_data; + + g_assert_true (test->server == ws); + g_clear_object (&test->server); + return TRUE; +} + +static void +test_server_unref_connection_on_close (Test *test, + gconstpointer data) +{ + gboolean close_event_client = FALSE; + + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client); + g_signal_connect (test->server, "closed", G_CALLBACK (on_close_unref_connection), test); + soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "client closed"); + g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING); + + WAIT_UNTIL (test->server == NULL); + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + + g_assert_true (close_event_client); +} + +static gpointer timeout_server_thread (gpointer user_data) { Test *test = user_data; @@ -771,6 +1160,79 @@ send_fragments_server_thread (gpointer user_data) } static void +do_deflate (z_stream *zstream, + const char *str, + guint8 *buffer, + gsize *length) +{ + zstream->next_in = (void *)str; + zstream->avail_in = strlen (str); + zstream->next_out = buffer; + zstream->avail_out = 512; + + g_assert_cmpint (deflate(zstream, Z_NO_FLUSH), ==, Z_OK); + g_assert_cmpint (zstream->avail_in, ==, 0); + g_assert_cmpint (deflate(zstream, Z_SYNC_FLUSH), ==, Z_OK); + g_assert_cmpint (deflate(zstream, Z_SYNC_FLUSH), ==, Z_BUF_ERROR); + + *length = 512 - zstream->avail_out; + g_assert_cmpuint (*length, <, 126); +} + +static gpointer +send_compressed_fragments_server_thread (gpointer user_data) +{ + Test *test = user_data; + gsize written; + z_stream zstream; + GByteArray *data; + guint8 byte; + guint8 buffer[512]; + gsize buffer_length; + GError *error = NULL; + + memset (&zstream, 0, sizeof(z_stream)); + g_assert (deflateInit2 (&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) == Z_OK); + + data = g_byte_array_new (); + + do_deflate (&zstream, "one ", buffer, &buffer_length); + byte = 0x00 | 0x01 | 0x40; /* !fin | opcode | compressed */ + data = g_byte_array_append (data, &byte, 1); + byte = (0xFF & buffer_length); /* mask | 7-bit-len */ + data = g_byte_array_append (data, &byte, 1); + data = g_byte_array_append (data, buffer, buffer_length); + + do_deflate (&zstream, "two ", buffer, &buffer_length); + byte = 0x00; /* !fin | no opcode */ + data = g_byte_array_append (data, &byte, 1); + byte = (0xFF & buffer_length); /* mask | 7-bit-len */ + data = g_byte_array_append (data, &byte, 1); + data = g_byte_array_append (data, buffer, buffer_length); + + do_deflate (&zstream, "three", buffer, &buffer_length); + g_assert_cmpuint (buffer_length, >=, 4); + buffer_length -= 4; + byte = 0x80; /* fin | no opcode */ + data = g_byte_array_append (data, &byte, 1); + byte = (0xFF & buffer_length); /* mask | 7-bit-len */ + data = g_byte_array_append (data, &byte, 1); + data = g_byte_array_append (data, buffer, buffer_length); + + g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server), + data->data, data->len, &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, data->len); + g_io_stream_close (test->raw_server, NULL, &error); + g_assert_no_error (error); + + deflateEnd (&zstream); + g_byte_array_free (data, TRUE); + + return NULL; +} + +static void test_receive_fragmented (Test *test, gconstpointer data) { @@ -778,7 +1240,11 @@ test_receive_fragmented (Test *test, GBytes *received = NULL; GBytes *expect; - thread = g_thread_new ("fragment-thread", send_fragments_server_thread, test); + thread = g_thread_new ("fragment-thread", + test->enable_extensions ? + send_compressed_fragments_server_thread : + send_fragments_server_thread, + test); g_signal_connect (test->client, "error", G_CALLBACK (on_error_not_reached), NULL); g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); @@ -794,6 +1260,175 @@ test_receive_fragmented (Test *test, WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); } +typedef struct { + Test *test; + const char *header; + GString *payload; +} InvalidEncodeLengthTest; + +static gpointer +send_invalid_encode_length_server_thread (gpointer user_data) +{ + InvalidEncodeLengthTest *test = user_data; + gsize header_size; + gsize written; + GError *error = NULL; + + header_size = test->payload->len == 125 ? 6 : 10; + g_output_stream_write_all (g_io_stream_get_output_stream (test->test->raw_server), + test->header, header_size, &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, header_size); + + g_output_stream_write_all (g_io_stream_get_output_stream (test->test->raw_server), + test->payload->str, test->payload->len, &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, test->payload->len); + + g_io_stream_close (test->test->raw_server, NULL, &error); + g_assert_no_error (error); + + return NULL; +} + +static void +test_receive_invalid_encode_length_16 (Test *test, + gconstpointer data) +{ + GThread *thread; + GBytes *received = NULL; + GError *error = NULL; + InvalidEncodeLengthTest context = { test, NULL }; + guint i; + + g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error); + g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received); + + /* We use 126(~) as payload length with 125 extended length */ + context.header = "\x82~\x00}"; + context.payload = g_string_new (NULL); + for (i = 0; i < 125; i++) + g_string_append (context.payload, "X"); + thread = g_thread_new ("invalid-encode-length-thread", send_invalid_encode_length_server_thread, &context); + + WAIT_UNTIL (error != NULL || received != NULL); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR); + g_clear_error (&error); + g_assert_null (received); + + g_thread_join (thread); + g_string_free (context.payload, TRUE); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); +} + +static void +test_receive_invalid_encode_length_64 (Test *test, + gconstpointer data) +{ + GThread *thread; + GBytes *received = NULL; + GError *error = NULL; + InvalidEncodeLengthTest context = { test, NULL }; + guint i; + + g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error); + g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received); + + /* We use 127(\x7f) as payload length with 65535 extended length */ + context.header = "\x82\x7f\x00\x00\x00\x00\x00\x00\xff\xff"; + context.payload = g_string_new (NULL); + for (i = 0; i < 65535; i++) + g_string_append (context.payload, "X"); + thread = g_thread_new ("invalid-encode-length-thread", send_invalid_encode_length_server_thread, &context); + + WAIT_UNTIL (error != NULL || received != NULL); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR); + g_clear_error (&error); + g_assert_null (received); + + g_thread_join (thread); + g_string_free (context.payload, TRUE); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); +} + +static gpointer +send_masked_frame_server_thread (gpointer user_data) +{ + Test *test = user_data; + const char frame[] = "\x82\x8e\x9a"; + gsize written; + GError *error = NULL; + + g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server), + frame, sizeof (frame), &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, sizeof (frame)); + + g_io_stream_close (test->raw_server, NULL, &error); + g_assert_no_error (error); + + return NULL; +} + +static void +test_client_receive_masked_frame (Test *test, + gconstpointer data) +{ + GThread *thread; + GBytes *received = NULL; + GError *error = NULL; + + g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error); + g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received); + + thread = g_thread_new ("send-masked-frame-thread", send_masked_frame_server_thread, test); + + WAIT_UNTIL (error != NULL || received != NULL); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR); + g_clear_error (&error); + g_assert_null (received); + + g_thread_join (thread); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); +} + +static void +test_server_receive_unmasked_frame (Test *test, + gconstpointer data) +{ + GError *error = NULL; + GIOStream *io; + gsize written; + const char *frame; + gboolean close_event = FALSE; + + g_signal_handlers_disconnect_by_func (test->server, on_error_not_reached, NULL); + g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error); + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event); + + io = soup_websocket_connection_get_io_stream (test->client); + + /* Unmasked frame */ + frame = "\x81\x0bHello World"; + if (!g_output_stream_write_all (g_io_stream_get_output_stream (io), + frame, 13, &written, NULL, NULL)) + g_assert_not_reached (); + g_assert_cmpuint (written, ==, 13); + + WAIT_UNTIL (error != NULL); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR); + g_clear_error (&error); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + g_assert (close_event); + + g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR); + +} + static void test_client_context_got_server_connection (SoupServer *server, SoupWebsocketConnection *connection, @@ -842,6 +1477,390 @@ test_client_context (Test *test, g_assert_no_error (test->client_error); } +static struct { + const char *client_extension; + gboolean expected_prepare_result; + gboolean server_supports_extensions; + gboolean expected_check_result; + gboolean expected_accepted_extension; + gboolean expected_verify_result; + const char *server_extension; +} deflate_negotiate_tests[] = { + { "permessage-deflate", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate" + }, + { "permessage-deflate", + /* prepare supported check accepted verify */ + TRUE, FALSE, TRUE, FALSE, TRUE, + "permessage-deflate" + }, + { "permessage-deflate; server_no_context_takeover", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate; server_no_context_takeover" + }, + { "permessage-deflate; client_no_context_takeover", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate; client_no_context_takeover" + }, + { "permessage-deflate; server_max_window_bits=8", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate; server_max_window_bits=8" + }, + { "permessage-deflate; client_max_window_bits", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate; client_max_window_bits=15" + }, + { "permessage-deflate; client_max_window_bits=10", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate; client_max_window_bits=10" + }, + { "permessage-deflate; client_no_context_takeover; server_max_window_bits=10", + /* prepare supported check accepted verify */ + TRUE, TRUE, TRUE, TRUE, TRUE, + "permessage-deflate; client_no_context_takeover; server_max_window_bits=10" + }, + { "permessage-deflate; unknown_parameter", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; client_no_context_takeover; client_no_context_takeover", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_max_window_bits=10; server_max_window_bits=15", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; client_no_context_takeover=15", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_no_context_takeover=15", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_max_window_bits", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_max_window_bits=7", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_max_window_bits=16", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; client_max_window_bits=7", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; client_max_window_bits=16", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; client_max_window_bits=foo", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_max_window_bits=bar", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; client_max_window_bits=15foo", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, + { "permessage-deflate; server_max_window_bits=10bar", + /* prepare supported check accepted verify */ + TRUE, TRUE, FALSE, FALSE, FALSE, + NULL + }, +}; + +static void +test_deflate_negotiate_direct (Test *test, + gconstpointer unused) +{ + GPtrArray *supported_extensions; + guint i; + + supported_extensions = g_ptr_array_new_full (1, g_type_class_unref); + g_ptr_array_add (supported_extensions, g_type_class_ref (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE)); + + for (i = 0; i < G_N_ELEMENTS (deflate_negotiate_tests); i++) { + SoupMessage *msg; + gboolean result; + GList *accepted_extensions = NULL; + GError *error = NULL; + + msg = soup_message_new ("GET", "http://127.0.0.1"); + + soup_websocket_client_prepare_handshake (msg, NULL, NULL); + soup_message_headers_append (msg->request_headers, "Sec-WebSocket-Extensions", deflate_negotiate_tests[i].client_extension); + result = soup_websocket_server_check_handshake_with_extensions (msg, NULL, NULL, + deflate_negotiate_tests[i].server_supports_extensions ? + supported_extensions : NULL, + &error); + g_assert (result == deflate_negotiate_tests[i].expected_check_result); + if (result) { + g_assert_no_error (error); + } else { + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE); + g_clear_error (&error); + } + + result = soup_websocket_server_process_handshake_with_extensions (msg, NULL, NULL, + deflate_negotiate_tests[i].server_supports_extensions ? + supported_extensions : NULL, + &accepted_extensions); + g_assert (result == deflate_negotiate_tests[i].expected_check_result); + if (deflate_negotiate_tests[i].expected_accepted_extension) { + const char *extension; + + extension = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Extensions"); + g_assert_cmpstr (extension, ==, deflate_negotiate_tests[i].server_extension); + g_assert_nonnull (accepted_extensions); + g_assert_cmpuint (g_list_length (accepted_extensions), ==, 1); + g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (accepted_extensions->data)); + g_list_free_full (accepted_extensions, g_object_unref); + accepted_extensions = NULL; + } else { + g_assert_null (accepted_extensions); + } + + result = soup_websocket_client_verify_handshake_with_extensions (msg, supported_extensions, &accepted_extensions, &error); + g_assert (result == deflate_negotiate_tests[i].expected_verify_result); + if (result) { + g_assert_no_error (error); + } else { + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE); + g_clear_error (&error); + } + if (deflate_negotiate_tests[i].expected_accepted_extension) { + g_assert_nonnull (accepted_extensions); + g_assert_cmpuint (g_list_length (accepted_extensions), ==, 1); + g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (accepted_extensions->data)); + g_list_free_full (accepted_extensions, g_object_unref); + accepted_extensions = NULL; + } else { + g_assert_null (accepted_extensions); + } + + g_object_unref (msg); + } + + g_ptr_array_unref (supported_extensions); +} + +static void +test_deflate_disabled_in_message_direct (Test *test, + gconstpointer unused) +{ + SoupMessage *msg; + GPtrArray *supported_extensions; + GList *accepted_extensions = NULL; + GError *error = NULL; + + supported_extensions = g_ptr_array_new_full (1, g_type_class_unref); + g_ptr_array_add (supported_extensions, g_type_class_ref (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE)); + + msg = soup_message_new ("GET", "http://127.0.0.1"); + soup_message_disable_feature (msg, SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE); + soup_websocket_client_prepare_handshake_with_extensions (msg, NULL, NULL, supported_extensions); + g_assert_cmpstr (soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Extensions"), ==, NULL); + + g_assert_true (soup_websocket_server_check_handshake_with_extensions (msg, NULL, NULL, supported_extensions, &error)); + g_assert_no_error (error); + + g_assert_true (soup_websocket_server_process_handshake_with_extensions (msg, NULL, NULL, supported_extensions, &accepted_extensions)); + g_assert_null (accepted_extensions); + g_assert_cmpstr (soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Extensions"), ==, NULL); + + g_assert_true (soup_websocket_client_verify_handshake_with_extensions (msg, supported_extensions, &accepted_extensions, &error)); + g_assert_no_error (error); + g_assert_null (accepted_extensions); + + g_object_unref (msg); + g_ptr_array_unref (supported_extensions); +} + +static void +test_deflate_disabled_in_message_soup (Test *test, + gconstpointer unused) +{ + test->enable_extensions = TRUE; + test->disable_deflate_in_message = TRUE; + setup_soup_server (test, NULL, NULL, got_server_connection, test); + client_connect (test, NULL, NULL, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); + + g_assert_cmpstr (soup_message_headers_get_one (test->msg->request_headers, "Sec-WebSocket-Extensions"), ==, NULL); + g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Extensions"), ==, NULL); +} + +static gpointer +send_compressed_fragments_error_server_thread (gpointer user_data) +{ + Test *test = user_data; + gsize written; + z_stream zstream; + GByteArray *data; + guint8 byte; + guint8 buffer[512]; + gsize buffer_length; + GError *error = NULL; + + memset (&zstream, 0, sizeof(z_stream)); + g_assert (deflateInit2 (&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) == Z_OK); + + data = g_byte_array_new (); + + do_deflate (&zstream, "one ", buffer, &buffer_length); + byte = 0x00 | 0x01 | 0x40; /* !fin | opcode | compressed */ + data = g_byte_array_append (data, &byte, 1); + byte = (0xFF & buffer_length); /* mask | 7-bit-len */ + data = g_byte_array_append (data, &byte, 1); + data = g_byte_array_append (data, buffer, buffer_length); + + /* Only the first fragment should include the compressed bit set. */ + do_deflate (&zstream, "two ", buffer, &buffer_length); + byte = 0x00 | 0x00 | 0x40; /* !fin | no opcode | compressed */ + data = g_byte_array_append (data, &byte, 1); + byte = (0xFF & buffer_length); /* mask | 7-bit-len */ + data = g_byte_array_append (data, &byte, 1); + data = g_byte_array_append (data, buffer, buffer_length); + + do_deflate (&zstream, "three", buffer, &buffer_length); + g_assert_cmpuint (buffer_length, >=, 4); + buffer_length -= 4; + byte = 0x80; /* fin | no opcode */ + data = g_byte_array_append (data, &byte, 1); + byte = (0xFF & buffer_length); /* mask | 7-bit-len */ + data = g_byte_array_append (data, &byte, 1); + data = g_byte_array_append (data, buffer, buffer_length); + + g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server), + data->data, data->len, &written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpuint (written, ==, data->len); + g_io_stream_close (test->raw_server, NULL, &error); + g_assert_no_error (error); + + deflateEnd (&zstream); + g_byte_array_free (data, TRUE); + + return NULL; +} + +static void +test_deflate_receive_fragmented_error (Test *test, + gconstpointer data) +{ + GThread *thread; + GBytes *received = NULL; + gboolean close_event = FALSE; + GError *error = NULL; + + thread = g_thread_new ("deflate-fragment-error-thread", + send_compressed_fragments_error_server_thread, + test); + + g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error); + g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received); + g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event); + + WAIT_UNTIL (error != NULL || received != NULL); + g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR); + g_clear_error (&error); + g_assert_null (received); + + g_thread_join (thread); + + WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED); + g_assert (close_event); +} + +static void +test_cookies_in_request (Test *test, + gconstpointer data) +{ + SoupCookie *cookie; + const char *cookie_header; + SoupCookie *requested_cookie; + + cookie = soup_cookie_new ("foo", "bar", "127.0.0.1", "/", -1); + test->initial_cookies = g_list_prepend (test->initial_cookies, soup_cookie_copy (cookie)); + + setup_soup_server (test, NULL, NULL, got_server_connection, test); + client_connect (test, NULL, NULL, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); + + cookie_header = soup_message_headers_get_one (test->msg->request_headers, "Cookie"); + requested_cookie = soup_cookie_parse (cookie_header, NULL); + g_assert_true (soup_cookie_equal (cookie, requested_cookie)); + soup_cookie_free (cookie); + soup_cookie_free (requested_cookie); +} + +static void +cookies_test_websocket_server_request_started (SoupServer *server, SoupMessage *msg, + SoupClientContext *client, gpointer user_data) +{ + soup_message_headers_append (msg->response_headers, "Set-Cookie", "foo=bar; Path=/"); +} + +static void +test_cookies_in_response (Test *test, + gconstpointer data) +{ + SoupCookieJar *jar; + GSList *cookies; + SoupCookie *cookie; + + setup_soup_server (test, NULL, NULL, got_server_connection, test); + g_signal_connect (test->soup_server, "request-started", + G_CALLBACK (cookies_test_websocket_server_request_started), + NULL); + client_connect (test, NULL, NULL, got_client_connection, test); + WAIT_UNTIL (test->server != NULL); + WAIT_UNTIL (test->client != NULL || test->client_error != NULL); + g_assert_no_error (test->client_error); + + jar = SOUP_COOKIE_JAR (soup_session_get_feature (test->session, SOUP_TYPE_COOKIE_JAR)); + cookies = soup_cookie_jar_all_cookies (jar); + g_assert_nonnull (cookies); + g_assert_cmpuint (g_slist_length (cookies), ==, 1); + cookie = soup_cookie_new ("foo", "bar", "127.0.0.1", "/", -1); + g_assert_true (soup_cookie_equal (cookie, (SoupCookie *)cookies->data)); + g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free); + soup_cookie_free (cookie); +} + int main (int argc, char *argv[]) @@ -855,6 +1874,10 @@ main (int argc, test_handshake, teardown_soup_connection); + g_test_add ("/websocket/soup/handshake-error", Test, NULL, NULL, + test_handshake_unsupported_extension, + teardown_soup_connection); + g_test_add ("/websocket/direct/send-client-to-server", Test, NULL, setup_direct_connection, test_send_client_to_server, @@ -882,32 +1905,37 @@ main (int argc, test_send_big_packets, teardown_soup_connection); - g_test_add ("/websocket/direct/send-bad-data", Test, NULL, + g_test_add ("/websocket/direct/send-empty-packets", Test, NULL, setup_direct_connection, - test_send_bad_data, + test_send_empty_packets, teardown_direct_connection); - g_test_add ("/websocket/soup/send-bad-data", Test, NULL, + g_test_add ("/websocket/soup/send-empty-packets", Test, NULL, setup_soup_connection, - test_send_bad_data, + test_send_empty_packets, teardown_soup_connection); - g_test_add ("/websocket/direct/close-clean-client", Test, NULL, + g_test_add ("/websocket/direct/send-bad-data", Test, NULL, setup_direct_connection, - test_close_clean_client, + test_send_bad_data, teardown_direct_connection); - g_test_add ("/websocket/soup/close-clean-client", Test, NULL, + g_test_add ("/websocket/soup/send-bad-data", Test, NULL, setup_soup_connection, - test_close_clean_client, + test_send_bad_data, teardown_soup_connection); - g_test_add ("/websocket/direct/close-clean-server", Test, NULL, - setup_direct_connection, - test_close_clean_server, - teardown_direct_connection); - g_test_add ("/websocket/soup/close-clean-server", Test, NULL, - setup_soup_connection, - test_close_clean_server, - teardown_soup_connection); + g_test_add ("/websocket/direct/close-clean-client", Test, NULL, NULL, + test_close_clean_client_direct, + NULL); + g_test_add ("/websocket/soup/close-clean-client", Test, NULL, NULL, + test_close_clean_client_soup, + NULL); + + g_test_add ("/websocket/direct/close-clean-server", Test, NULL, NULL, + test_close_clean_server_direct, + NULL); + g_test_add ("/websocket/soup/close-clean-server", Test, NULL, NULL, + test_close_clean_server_soup, + NULL); g_test_add ("/websocket/direct/message-after-closing", Test, NULL, setup_direct_connection, @@ -918,6 +1946,16 @@ main (int argc, test_message_after_closing, teardown_soup_connection); + g_test_add ("/websocket/direct/close-after-close", Test, NULL, + setup_half_direct_connection, + test_close_after_close, + teardown_direct_connection); + + g_test_add ("/websocket/soup/server-unref-connection-on-close", Test, NULL, + setup_soup_connection, + test_server_unref_connection_on_close, + teardown_soup_connection); + g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL, test_protocol_negotiate_direct, @@ -953,6 +1991,91 @@ main (int argc, test_receive_fragmented, teardown_direct_connection); + g_test_add ("/websocket/direct/receive-invalid-encode-length-16", Test, NULL, + setup_half_direct_connection, + test_receive_invalid_encode_length_16, + teardown_direct_connection); + + g_test_add ("/websocket/direct/receive-invalid-encode-length-64", Test, NULL, + setup_half_direct_connection, + test_receive_invalid_encode_length_64, + teardown_direct_connection); + + g_test_add ("/websocket/direct/client-receive-masked-frame", Test, NULL, + setup_half_direct_connection, + test_client_receive_masked_frame, + teardown_direct_connection); + + g_test_add ("/websocket/direct/server-receive-unmasked-frame", Test, NULL, + setup_direct_connection, + test_server_receive_unmasked_frame, + teardown_direct_connection); + g_test_add ("/websocket/soup/server-receive-unmasked-frame", Test, NULL, + setup_soup_connection, + test_server_receive_unmasked_frame, + teardown_soup_connection); + + g_test_add ("/websocket/soup/deflate-handshake", Test, NULL, + setup_soup_connection_with_extensions, + test_handshake, + teardown_soup_connection); + + g_test_add ("/websocket/direct/deflate-negotiate", Test, NULL, NULL, + test_deflate_negotiate_direct, + NULL); + + g_test_add ("/websocket/direct/deflate-disabled-in-message", Test, NULL, NULL, + test_deflate_disabled_in_message_direct, + NULL); + g_test_add ("/websocket/soup/deflate-disabled-in-message", Test, NULL, NULL, + test_deflate_disabled_in_message_soup, + teardown_soup_connection); + + g_test_add ("/websocket/direct/deflate-send-client-to-server", Test, NULL, + setup_direct_connection_with_extensions, + test_send_client_to_server, + teardown_direct_connection); + g_test_add ("/websocket/soup/deflate-send-client-to-server", Test, NULL, + setup_soup_connection_with_extensions, + test_send_client_to_server, + teardown_soup_connection); + + g_test_add ("/websocket/direct/deflate-send-server-to-client", Test, NULL, + setup_direct_connection_with_extensions, + test_send_server_to_client, + teardown_direct_connection); + g_test_add ("/websocket/soup/deflate-send-server-to-client", Test, NULL, + setup_soup_connection_with_extensions, + test_send_server_to_client, + teardown_soup_connection); + + g_test_add ("/websocket/direct/deflate-send-big-packets", Test, NULL, + setup_direct_connection_with_extensions, + test_send_big_packets, + teardown_direct_connection); + g_test_add ("/websocket/soup/deflate-send-big-packets", Test, NULL, + setup_soup_connection_with_extensions, + test_send_big_packets, + teardown_soup_connection); + + g_test_add ("/websocket/direct/deflate-send-empty-packets", Test, NULL, + setup_direct_connection_with_extensions, + test_send_empty_packets, + teardown_direct_connection); + g_test_add ("/websocket/soup/deflate-send-empty-packets", Test, NULL, + setup_soup_connection_with_extensions, + test_send_empty_packets, + teardown_soup_connection); + + g_test_add ("/websocket/direct/deflate-receive-fragmented", Test, NULL, + setup_half_direct_connection_with_extensions, + test_receive_fragmented, + teardown_direct_connection); + g_test_add ("/websocket/direct/deflate-receive-fragmented-error", Test, NULL, + setup_half_direct_connection_with_extensions, + test_deflate_receive_fragmented_error, + teardown_direct_connection); + if (g_test_slow ()) { g_test_add ("/websocket/direct/close-after-timeout", Test, NULL, setup_half_direct_connection, @@ -964,6 +2087,13 @@ main (int argc, test_client_context, teardown_soup_connection); + g_test_add ("/websocket/soup/cookies-in-request", Test, NULL, NULL, + test_cookies_in_request, + teardown_soup_connection); + g_test_add ("/websocket/soup/cookies-in-response", Test, NULL, NULL, + test_cookies_in_response, + teardown_soup_connection); + ret = g_test_run (); test_cleanup (); |