summaryrefslogtreecommitdiff
path: root/tests/websocket-test.c
diff options
context:
space:
mode:
authorSeonah Moon <seonah1.moon@samsung.com>2020-02-28 12:54:59 +0900
committerSeonah Moon <seonah1.moon@samsung.com>2020-02-28 12:55:12 +0900
commitb74c5fd5483969d55625576448662edf864b6b9e (patch)
treee3cd0fca2b0804eb86ec06c49c5495444a86f98d /tests/websocket-test.c
parent58894334cd3f0b89c865304e8e9d6eea3cd20a55 (diff)
downloadlibsoup-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.c1214
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 ();