1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * This file was originally part of Cockpit.
4 *
5 * Copyright (C) 2013 Red Hat, Inc.
6 *
7 * Cockpit is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * Cockpit is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "test-utils.h"
22
23 #include <zlib.h>
24
25 typedef struct {
26 GSocket *listener;
27 gushort port;
28
29 SoupSession *session;
30 SoupMessage *msg;
31 SoupWebsocketConnection *client;
32 GError *client_error;
33
34 SoupServer *soup_server;
35 SoupWebsocketConnection *server;
36
37 gboolean no_server;
38 GIOStream *raw_server;
39
40 gboolean enable_extensions;
41 gboolean disable_deflate_in_message;
42
43 GList *initial_cookies;
44
45 GMutex mutex;
46 } Test;
47
48 #define WAIT_UNTIL(cond) \
49 G_STMT_START \
50 while (!(cond)) g_main_context_iteration (NULL, TRUE); \
51 G_STMT_END
52
53 static void
on_error_not_reached(SoupWebsocketConnection * ws,GError * error,gpointer user_data)54 on_error_not_reached (SoupWebsocketConnection *ws,
55 GError *error,
56 gpointer user_data)
57 {
58 /* At this point we know this will fail, but is informative */
59 g_assert_no_error (error);
60 }
61
62 static void
on_error_copy(SoupWebsocketConnection * ws,GError * error,gpointer user_data)63 on_error_copy (SoupWebsocketConnection *ws,
64 GError *error,
65 gpointer user_data)
66 {
67 GError **copy = user_data;
68 g_assert (*copy == NULL);
69 *copy = g_error_copy (error);
70 }
71
72 static void
setup_listener(Test * test)73 setup_listener (Test *test)
74 {
75 GSocketAddress *addr;
76 GError *error = NULL;
77
78 test->listener = g_socket_new (G_SOCKET_FAMILY_IPV4,
79 G_SOCKET_TYPE_STREAM,
80 G_SOCKET_PROTOCOL_TCP,
81 &error);
82 g_assert_no_error (error);
83
84 addr = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
85 g_assert_no_error (error);
86
87 g_socket_bind (test->listener, addr, TRUE, &error);
88 g_assert_no_error (error);
89 g_object_unref (addr);
90
91 addr = g_socket_get_local_address (test->listener, &error);
92 g_assert_no_error (error);
93
94 test->port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
95 g_object_unref (addr);
96
97 g_socket_listen (test->listener, &error);
98 g_assert_no_error (error);
99 }
100
101 static void
direct_connection_complete(GObject * object,GAsyncResult * result,gpointer user_data)102 direct_connection_complete (GObject *object,
103 GAsyncResult *result,
104 gpointer user_data)
105 {
106 Test *test = user_data;
107 GSocketConnection *conn;
108 SoupURI *uri;
109 GError *error = NULL;
110 GList *extensions = NULL;
111
112 conn = g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (object),
113 result, &error);
114 g_assert_no_error (error);
115
116 uri = soup_uri_new ("http://127.0.0.1/");
117 if (test->enable_extensions) {
118 SoupWebsocketExtension *extension;
119
120 extension = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL);
121 g_assert_true (soup_websocket_extension_configure (extension,
122 SOUP_WEBSOCKET_CONNECTION_CLIENT,
123 NULL, NULL));
124 extensions = g_list_prepend (extensions, extension);
125 }
126 test->client = soup_websocket_connection_new_with_extensions (G_IO_STREAM (conn), uri,
127 SOUP_WEBSOCKET_CONNECTION_CLIENT,
128 NULL, NULL,
129 extensions);
130 soup_uri_free (uri);
131 g_object_unref (conn);
132 }
133
134 static gboolean
got_connection(GSocket * listener,GIOCondition cond,gpointer user_data)135 got_connection (GSocket *listener,
136 GIOCondition cond,
137 gpointer user_data)
138 {
139 Test *test = user_data;
140 GSocket *sock;
141 GSocketConnection *conn;
142 SoupURI *uri;
143 GList *extensions = NULL;
144 GError *error = NULL;
145
146 sock = g_socket_accept (listener, NULL, &error);
147 g_assert_no_error (error);
148
149 conn = g_socket_connection_factory_create_connection (sock);
150 g_assert (conn != NULL);
151 g_object_unref (sock);
152
153 if (test->no_server)
154 test->raw_server = G_IO_STREAM (conn);
155 else {
156 uri = soup_uri_new ("http://127.0.0.1/");
157 if (test->enable_extensions) {
158 SoupWebsocketExtension *extension;
159
160 extension = g_object_new (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE, NULL);
161 g_assert_true (soup_websocket_extension_configure (extension,
162 SOUP_WEBSOCKET_CONNECTION_SERVER,
163 NULL, NULL));
164 extensions = g_list_prepend (extensions, extension);
165 }
166 test->server = soup_websocket_connection_new_with_extensions (G_IO_STREAM (conn), uri,
167 SOUP_WEBSOCKET_CONNECTION_SERVER,
168 NULL, NULL,
169 extensions);
170 soup_uri_free (uri);
171 g_object_unref (conn);
172 }
173
174 return FALSE;
175 }
176
177 static void
setup_direct_connection(Test * test,gconstpointer data)178 setup_direct_connection (Test *test,
179 gconstpointer data)
180 {
181 GSocketClient *client;
182 GSource *listen_source;
183
184 setup_listener (test);
185
186 client = g_socket_client_new ();
187 g_socket_client_connect_to_host_async (client, "127.0.0.1", test->port,
188 NULL, direct_connection_complete, test);
189
190 listen_source = g_socket_create_source (test->listener, G_IO_IN, NULL);
191 g_source_set_callback (listen_source, (GSourceFunc) got_connection, test, NULL);
192 g_source_attach (listen_source, NULL);
193
194 while (test->client == NULL || (test->server == NULL && test->raw_server == NULL))
195 g_main_context_iteration (NULL, TRUE);
196
197 g_source_destroy (listen_source);
198 g_source_unref (listen_source);
199 g_object_unref (client);
200 }
201
202 static void
setup_direct_connection_with_extensions(Test * test,gconstpointer data)203 setup_direct_connection_with_extensions (Test *test,
204 gconstpointer data)
205 {
206 test->enable_extensions = TRUE;
207 setup_direct_connection (test, data);
208 }
209
210 static void
setup_half_direct_connection(Test * test,gconstpointer data)211 setup_half_direct_connection (Test *test,
212 gconstpointer data)
213 {
214 test->no_server = TRUE;
215 setup_direct_connection (test, data);
216 }
217
218 static void
setup_half_direct_connection_with_extensions(Test * test,gconstpointer data)219 setup_half_direct_connection_with_extensions (Test *test,
220 gconstpointer data)
221 {
222 test->no_server = TRUE;
223 setup_direct_connection_with_extensions (test, data);
224 }
225
226 static void
teardown_direct_connection(Test * test,gconstpointer data)227 teardown_direct_connection (Test *test,
228 gconstpointer data)
229 {
230 g_clear_object (&test->listener);
231 g_clear_object (&test->client);
232 g_clear_object (&test->server);
233 g_clear_object (&test->raw_server);
234 }
235
236 static void
setup_soup_server(Test * test,const char * origin,const char ** protocols,SoupServerWebsocketCallback callback,gpointer user_data)237 setup_soup_server (Test *test,
238 const char *origin,
239 const char **protocols,
240 SoupServerWebsocketCallback callback,
241 gpointer user_data)
242 {
243 GError *error = NULL;
244
245 setup_listener (test);
246
247 test->soup_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
248 if (!test->enable_extensions)
249 soup_server_remove_websocket_extension (test->soup_server, SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE);
250 soup_server_listen_socket (test->soup_server, test->listener, 0, &error);
251 g_assert_no_error (error);
252
253 soup_server_add_websocket_handler (test->soup_server, "/unix",
254 origin, (char **) protocols,
255 callback, user_data, NULL);
256 }
257
258 static void
client_connect(Test * test,const char * origin,const char ** protocols,GAsyncReadyCallback callback,gpointer user_data)259 client_connect (Test *test,
260 const char *origin,
261 const char **protocols,
262 GAsyncReadyCallback callback,
263 gpointer user_data)
264 {
265 char *url;
266 SoupCookieJar *jar;
267 GList *l;
268
269 test->session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
270 if (test->enable_extensions)
271 soup_session_add_feature_by_type (test->session, SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER);
272
273 jar = soup_cookie_jar_new ();
274 soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
275 soup_session_add_feature (test->session, SOUP_SESSION_FEATURE (jar));
276 for (l = test->initial_cookies; l; l = g_list_next (l))
277 soup_cookie_jar_add_cookie (jar, (SoupCookie *)l->data);
278 g_clear_pointer (&test->initial_cookies, g_list_free);
279 g_object_unref (jar);
280
281 url = g_strdup_printf ("ws://127.0.0.1:%u/unix", test->port);
282 test->msg = soup_message_new ("GET", url);
283 if (test->disable_deflate_in_message)
284 soup_message_disable_feature (test->msg, SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE);
285 g_free (url);
286
287 soup_session_websocket_connect_async (test->session, test->msg,
288 origin, (char **) protocols,
289 NULL, callback, user_data);
290 }
291
292 static void
got_server_connection(SoupServer * server,SoupWebsocketConnection * connection,const char * path,SoupClientContext * client,gpointer user_data)293 got_server_connection (SoupServer *server,
294 SoupWebsocketConnection *connection,
295 const char *path,
296 SoupClientContext *client,
297 gpointer user_data)
298 {
299 Test *test = user_data;
300
301 test->server = g_object_ref (connection);
302 }
303
304 static void
got_client_connection(GObject * object,GAsyncResult * result,gpointer user_data)305 got_client_connection (GObject *object,
306 GAsyncResult *result,
307 gpointer user_data)
308 {
309 Test *test = user_data;
310
311 test->client = soup_session_websocket_connect_finish (SOUP_SESSION (object),
312 result, &test->client_error);
313 }
314
315 static void
setup_soup_connection(Test * test,gconstpointer data)316 setup_soup_connection (Test *test,
317 gconstpointer data)
318 {
319 setup_soup_server (test, NULL, NULL, got_server_connection, test);
320 client_connect (test, NULL, NULL, got_client_connection, test);
321 WAIT_UNTIL (test->server != NULL);
322 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
323 g_assert_no_error (test->client_error);
324 }
325
326 static void
setup_soup_connection_with_extensions(Test * test,gconstpointer data)327 setup_soup_connection_with_extensions (Test *test,
328 gconstpointer data)
329 {
330 test->enable_extensions = TRUE;
331 setup_soup_connection (test, data);
332 }
333
334 static void
teardown_soup_connection(Test * test,gconstpointer data)335 teardown_soup_connection (Test *test,
336 gconstpointer data)
337 {
338 teardown_direct_connection (test, data);
339
340 g_clear_object (&test->msg);
341 g_clear_error (&test->client_error);
342 g_clear_pointer (&test->session, soup_test_session_abort_unref);
343 g_clear_pointer (&test->soup_server, soup_test_server_quit_unref);
344 }
345
346
347 static void
on_text_message(SoupWebsocketConnection * ws,SoupWebsocketDataType type,GBytes * message,gpointer user_data)348 on_text_message (SoupWebsocketConnection *ws,
349 SoupWebsocketDataType type,
350 GBytes *message,
351 gpointer user_data)
352 {
353 GBytes **receive = user_data;
354
355 g_assert_cmpint (type, ==, SOUP_WEBSOCKET_DATA_TEXT);
356 g_assert (*receive == NULL);
357 g_assert (message != NULL);
358
359 *receive = g_bytes_ref (message);
360 }
361
362 static void
on_binary_message(SoupWebsocketConnection * ws,SoupWebsocketDataType type,GBytes * message,gpointer user_data)363 on_binary_message (SoupWebsocketConnection *ws,
364 SoupWebsocketDataType type,
365 GBytes *message,
366 gpointer user_data)
367 {
368 GBytes **receive = user_data;
369
370 g_assert_cmpint (type, ==, SOUP_WEBSOCKET_DATA_BINARY);
371 g_assert (*receive == NULL);
372 g_assert (message != NULL);
373
374 *receive = g_bytes_ref (message);
375 }
376
377 static void
on_close_set_flag(SoupWebsocketConnection * ws,gpointer user_data)378 on_close_set_flag (SoupWebsocketConnection *ws,
379 gpointer user_data)
380 {
381 gboolean *flag = user_data;
382
383 g_assert (*flag == FALSE);
384
385 *flag = TRUE;
386 }
387
388
389 static void
test_handshake(Test * test,gconstpointer data)390 test_handshake (Test *test,
391 gconstpointer data)
392 {
393 g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_OPEN);
394 if (test->enable_extensions) {
395 GList *extensions = soup_websocket_connection_get_extensions (test->client);
396
397 g_assert_nonnull (extensions);
398 g_assert_cmpuint (g_list_length (extensions), ==, 1);
399 g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (extensions->data));
400 } else {
401 g_assert_null (soup_websocket_connection_get_extensions (test->client));
402 }
403
404 g_assert_cmpint (soup_websocket_connection_get_state (test->server), ==, SOUP_WEBSOCKET_STATE_OPEN);
405 if (test->enable_extensions) {
406 GList *extensions = soup_websocket_connection_get_extensions (test->server);
407
408 g_assert_nonnull (extensions);
409 g_assert_cmpuint (g_list_length (extensions), ==, 1);
410 g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (extensions->data));
411 } else {
412 g_assert_null (soup_websocket_connection_get_extensions (test->server));
413 }
414
415 }
416
417 static void
websocket_server_request_started(SoupServer * server,SoupMessage * msg,SoupClientContext * client,gpointer user_data)418 websocket_server_request_started (SoupServer *server, SoupMessage *msg,
419 SoupClientContext *client, gpointer user_data)
420 {
421 soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Extensions", "x-foo");
422 }
423
424 static void
request_unqueued(SoupSession * session,SoupMessage * msg,gpointer data)425 request_unqueued (SoupSession *session,
426 SoupMessage *msg,
427 gpointer data)
428 {
429 Test *test = data;
430
431 if (test->msg == msg)
432 g_clear_object (&test->msg);
433 }
434
435
436 static void
test_handshake_unsupported_extension(Test * test,gconstpointer data)437 test_handshake_unsupported_extension (Test *test,
438 gconstpointer data)
439 {
440 char *url;
441
442 setup_listener (test);
443 test->soup_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
444 soup_server_listen_socket (test->soup_server, test->listener, 0, NULL);
445 g_signal_connect (test->soup_server, "request-started",
446 G_CALLBACK (websocket_server_request_started),
447 NULL);
448 soup_server_add_websocket_handler (test->soup_server, "/unix", NULL, NULL,
449 got_server_connection, test, NULL);
450
451 test->session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
452 g_signal_connect (test->session, "request-unqueued",
453 G_CALLBACK (request_unqueued),
454 test);
455 url = g_strdup_printf ("ws://127.0.0.1:%u/unix", test->port);
456 test->msg = soup_message_new ("GET", url);
457 g_free (url);
458
459 soup_session_websocket_connect_async (test->session, test->msg, NULL, NULL, NULL,
460 got_client_connection, test);
461 WAIT_UNTIL (test->server != NULL);
462 WAIT_UNTIL (test->msg == NULL);
463 g_assert_error (test->client_error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE);
464 }
465
466 #define TEST_STRING "this is a test"
467 #define TEST_STRING_WITH_NULL "this is\0 a test"
468
469 static void
test_send_client_to_server(Test * test,gconstpointer data)470 test_send_client_to_server (Test *test,
471 gconstpointer data)
472 {
473 GBytes *sent;
474 GBytes *received = NULL;
475 const char *contents;
476 gsize len;
477
478 g_signal_connect (test->server, "message", G_CALLBACK (on_text_message), &received);
479
480 soup_websocket_connection_send_text (test->client, TEST_STRING);
481
482 WAIT_UNTIL (received != NULL);
483
484 /* Received messages should be null terminated (outside of len) */
485 contents = g_bytes_get_data (received, &len);
486 g_assert_cmpstr (contents, ==, TEST_STRING);
487 g_assert_cmpint (len, ==, strlen (TEST_STRING));
488 g_clear_pointer (&received, g_bytes_unref);
489
490 sent = g_bytes_new_static (TEST_STRING_WITH_NULL, sizeof (TEST_STRING_WITH_NULL));
491 soup_websocket_connection_send_message (test->client, SOUP_WEBSOCKET_DATA_TEXT, sent);
492
493 WAIT_UNTIL (received != NULL);
494
495 g_assert (g_bytes_equal (sent, received));
496 g_clear_pointer (&sent, g_bytes_unref);
497 g_clear_pointer (&received, g_bytes_unref);
498 }
499
500 static void
test_send_server_to_client(Test * test,gconstpointer data)501 test_send_server_to_client (Test *test,
502 gconstpointer data)
503 {
504 GBytes *sent;
505 GBytes *received = NULL;
506 const char *contents;
507 gsize len;
508
509 g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
510
511 soup_websocket_connection_send_text (test->server, TEST_STRING);
512
513 WAIT_UNTIL (received != NULL);
514
515 /* Received messages should be null terminated (outside of len) */
516 contents = g_bytes_get_data (received, &len);
517 g_assert_cmpstr (contents, ==, TEST_STRING);
518 g_assert_cmpint (len, ==, strlen (TEST_STRING));
519 g_clear_pointer (&received, g_bytes_unref);
520
521 sent = g_bytes_new_static (TEST_STRING_WITH_NULL, sizeof (TEST_STRING_WITH_NULL));
522 soup_websocket_connection_send_message (test->server, SOUP_WEBSOCKET_DATA_TEXT, sent);
523
524 WAIT_UNTIL (received != NULL);
525
526 g_assert (g_bytes_equal (sent, received));
527 g_clear_pointer (&sent, g_bytes_unref);
528 g_clear_pointer (&received, g_bytes_unref);
529 }
530
531 static void
test_send_big_packets(Test * test,gconstpointer data)532 test_send_big_packets (Test *test,
533 gconstpointer data)
534 {
535 GBytes *sent = NULL;
536 GBytes *received = NULL;
537
538 g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
539
540 sent = g_bytes_new_take (g_strnfill (400, '!'), 400);
541 soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
542 WAIT_UNTIL (received != NULL);
543 g_assert (g_bytes_equal (sent, received));
544 g_bytes_unref (sent);
545 g_bytes_unref (received);
546 received = NULL;
547
548 sent = g_bytes_new_take (g_strnfill (100 * 1000, '?'), 100 * 1000);
549 soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
550 WAIT_UNTIL (received != NULL);
551 g_assert (g_bytes_equal (sent, received));
552 g_bytes_unref (sent);
553 g_bytes_unref (received);
554 received = NULL;
555
556 soup_websocket_connection_set_max_incoming_payload_size (test->client, 1000 * 1000 + 1);
557 g_assert (soup_websocket_connection_get_max_incoming_payload_size (test->client) == (1000 * 1000 + 1));
558 soup_websocket_connection_set_max_incoming_payload_size (test->server, 1000 * 1000 + 1);
559 g_assert (soup_websocket_connection_get_max_incoming_payload_size (test->server) == (1000 * 1000 + 1));
560
561 sent = g_bytes_new_take (g_strnfill (1000 * 1000, '?'), 1000 * 1000);
562 soup_websocket_connection_send_text (test->server, g_bytes_get_data (sent, NULL));
563 WAIT_UNTIL (received != NULL);
564 g_assert (g_bytes_equal (sent, received));
565 g_bytes_unref (sent);
566 g_bytes_unref (received);
567 }
568
569 static void
test_send_empty_packets(Test * test,gconstpointer data)570 test_send_empty_packets (Test *test,
571 gconstpointer data)
572 {
573 GBytes *received = NULL;
574 gulong id;
575
576 id = g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
577
578 soup_websocket_connection_send_text (test->server, "\0");
579 WAIT_UNTIL (received != NULL);
580 g_assert_nonnull (g_bytes_get_data (received, NULL));
581 g_assert_cmpuint (((char *) g_bytes_get_data (received, NULL))[0], ==, '\0');
582 g_assert_cmpuint (g_bytes_get_size (received), ==, 0);
583 g_bytes_unref (received);
584 received = NULL;
585 g_signal_handler_disconnect (test->client, id);
586
587 id = g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
588
589 soup_websocket_connection_send_binary (test->server, NULL, 0);
590 WAIT_UNTIL (received != NULL);
591 /* We always include at least a null character */
592 g_assert_nonnull (g_bytes_get_data (received, NULL));
593 g_assert_cmpuint (((char *) g_bytes_get_data (received, NULL))[0], ==, '\0');
594 g_assert_cmpuint (g_bytes_get_size (received), ==, 0);
595 g_bytes_unref (received);
596 received = NULL;
597 g_signal_handler_disconnect (test->client, id);
598 }
599
600 static void
test_send_bad_data(Test * test,gconstpointer unused)601 test_send_bad_data (Test *test,
602 gconstpointer unused)
603 {
604 GError *error = NULL;
605 GIOStream *io;
606 gsize written;
607 const char *frame;
608 gboolean close_event = FALSE;
609
610 g_signal_handlers_disconnect_by_func (test->server, on_error_not_reached, NULL);
611 g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error);
612 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
613
614 io = soup_websocket_connection_get_io_stream (test->client);
615
616 /* Bad UTF-8 frame */
617 frame = "\x81\x84\x00\x00\x00\x00\xEE\xEE\xEE\xEE";
618 if (!g_output_stream_write_all (g_io_stream_get_output_stream (io),
619 frame, 10, &written, NULL, NULL))
620 g_assert_not_reached ();
621 g_assert_cmpuint (written, ==, 10);
622
623 WAIT_UNTIL (error != NULL);
624 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
625 g_clear_error (&error);
626
627 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
628 g_assert (close_event);
629
630 g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_BAD_DATA);
631 }
632
633 static const char *negotiate_client_protocols[] = { "bbb", "ccc", NULL };
634 static const char *negotiate_server_protocols[] = { "aaa", "bbb", "ccc", NULL };
635 static const char *negotiated_protocol = "bbb";
636
637 static void
test_protocol_negotiate_direct(Test * test,gconstpointer unused)638 test_protocol_negotiate_direct (Test *test,
639 gconstpointer unused)
640 {
641 SoupMessage *msg;
642 gboolean ok;
643 const char *protocol;
644 GError *error = NULL;
645
646 msg = soup_message_new ("GET", "http://127.0.0.1");
647 soup_websocket_client_prepare_handshake (msg, NULL,
648 (char **) negotiate_client_protocols);
649
650 ok = soup_websocket_server_check_handshake (msg, NULL,
651 (char **) negotiate_server_protocols,
652 &error);
653 g_assert_no_error (error);
654 g_assert_true (ok);
655
656 ok = soup_websocket_server_process_handshake (msg, NULL,
657 (char **) negotiate_server_protocols);
658 g_assert_true (ok);
659
660 protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
661 g_assert_cmpstr (protocol, ==, negotiated_protocol);
662
663 ok = soup_websocket_client_verify_handshake (msg, &error);
664 g_assert_no_error (error);
665 g_assert_true (ok);
666
667 g_object_unref (msg);
668 }
669
670 static void
test_protocol_negotiate_soup(Test * test,gconstpointer unused)671 test_protocol_negotiate_soup (Test *test,
672 gconstpointer unused)
673 {
674 setup_soup_server (test, NULL, negotiate_server_protocols, got_server_connection, test);
675 client_connect (test, NULL, negotiate_client_protocols, got_client_connection, test);
676 WAIT_UNTIL (test->server != NULL);
677 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
678 g_assert_no_error (test->client_error);
679
680 g_assert_cmpstr (soup_websocket_connection_get_protocol (test->client), ==, negotiated_protocol);
681 g_assert_cmpstr (soup_websocket_connection_get_protocol (test->server), ==, negotiated_protocol);
682 }
683
684 static const char *mismatch_client_protocols[] = { "ddd", NULL };
685 static const char *mismatch_server_protocols[] = { "aaa", "bbb", "ccc", NULL };
686
687 static void
test_protocol_mismatch_direct(Test * test,gconstpointer unused)688 test_protocol_mismatch_direct (Test *test,
689 gconstpointer unused)
690 {
691 SoupMessage *msg;
692 gboolean ok;
693 const char *protocol;
694 GError *error = NULL;
695
696 msg = soup_message_new ("GET", "http://127.0.0.1");
697 soup_websocket_client_prepare_handshake (msg, NULL,
698 (char **) mismatch_client_protocols);
699
700 ok = soup_websocket_server_check_handshake (msg, NULL,
701 (char **) mismatch_server_protocols,
702 &error);
703 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE);
704 g_clear_error (&error);
705 g_assert_false (ok);
706
707 ok = soup_websocket_server_process_handshake (msg, NULL,
708 (char **) mismatch_server_protocols);
709 g_assert_false (ok);
710 soup_test_assert_message_status (msg, SOUP_STATUS_BAD_REQUEST);
711
712 protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
713 g_assert_cmpstr (protocol, ==, NULL);
714
715 ok = soup_websocket_client_verify_handshake (msg, &error);
716 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE);
717 g_clear_error (&error);
718 g_assert_false (ok);
719
720 g_object_unref (msg);
721 }
722
723 static void
test_protocol_mismatch_soup(Test * test,gconstpointer unused)724 test_protocol_mismatch_soup (Test *test,
725 gconstpointer unused)
726 {
727 setup_soup_server (test, NULL, mismatch_server_protocols, got_server_connection, test);
728 client_connect (test, NULL, mismatch_client_protocols, got_client_connection, test);
729 WAIT_UNTIL (test->client_error != NULL);
730
731 g_assert_error (test->client_error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET);
732 }
733
734 static const char *all_protocols[] = { "aaa", "bbb", "ccc", NULL };
735
736 static void
test_protocol_server_any_direct(Test * test,gconstpointer unused)737 test_protocol_server_any_direct (Test *test,
738 gconstpointer unused)
739 {
740 SoupMessage *msg;
741 gboolean ok;
742 const char *protocol;
743 GError *error = NULL;
744
745 msg = soup_message_new ("GET", "http://127.0.0.1");
746 soup_websocket_client_prepare_handshake (msg, NULL, (char **) all_protocols);
747
748 ok = soup_websocket_server_check_handshake (msg, NULL, NULL, &error);
749 g_assert_no_error (error);
750 g_assert_true (ok);
751
752 ok = soup_websocket_server_process_handshake (msg, NULL, NULL);
753 g_assert_true (ok);
754
755 protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
756 g_assert_cmpstr (protocol, ==, NULL);
757
758 ok = soup_websocket_client_verify_handshake (msg, &error);
759 g_assert_no_error (error);
760 g_assert_true (ok);
761
762 g_object_unref (msg);
763 }
764
765 static void
test_protocol_server_any_soup(Test * test,gconstpointer unused)766 test_protocol_server_any_soup (Test *test,
767 gconstpointer unused)
768 {
769 setup_soup_server (test, NULL, NULL, got_server_connection, test);
770 client_connect (test, NULL, all_protocols, got_client_connection, test);
771 WAIT_UNTIL (test->server != NULL);
772 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
773 g_assert_no_error (test->client_error);
774
775 g_assert_cmpstr (soup_websocket_connection_get_protocol (test->client), ==, NULL);
776 g_assert_cmpstr (soup_websocket_connection_get_protocol (test->server), ==, NULL);
777 g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Protocol"), ==, NULL);
778 }
779
780 static void
test_protocol_client_any_direct(Test * test,gconstpointer unused)781 test_protocol_client_any_direct (Test *test,
782 gconstpointer unused)
783 {
784 SoupMessage *msg;
785 gboolean ok;
786 const char *protocol;
787 GError *error = NULL;
788
789 msg = soup_message_new ("GET", "http://127.0.0.1");
790 soup_websocket_client_prepare_handshake (msg, NULL, NULL);
791
792 ok = soup_websocket_server_check_handshake (msg, NULL, (char **) all_protocols, &error);
793 g_assert_no_error (error);
794 g_assert_true (ok);
795
796 ok = soup_websocket_server_process_handshake (msg, NULL, (char **) all_protocols);
797 g_assert_true (ok);
798
799 protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
800 g_assert_cmpstr (protocol, ==, NULL);
801
802 ok = soup_websocket_client_verify_handshake (msg, &error);
803 g_assert_no_error (error);
804 g_assert_true (ok);
805
806 g_object_unref (msg);
807 }
808
809 static void
test_protocol_client_any_soup(Test * test,gconstpointer unused)810 test_protocol_client_any_soup (Test *test,
811 gconstpointer unused)
812 {
813 setup_soup_server (test, NULL, all_protocols, got_server_connection, test);
814 client_connect (test, NULL, NULL, got_client_connection, test);
815 WAIT_UNTIL (test->server != NULL);
816 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
817 g_assert_no_error (test->client_error);
818
819 g_assert_cmpstr (soup_websocket_connection_get_protocol (test->client), ==, NULL);
820 g_assert_cmpstr (soup_websocket_connection_get_protocol (test->server), ==, NULL);
821 g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Protocol"), ==, NULL);
822 }
823
824 static const struct {
825 gushort code;
826 const char *reason;
827 gushort expected_sender_code;
828 const char *expected_sender_reason;
829 gushort expected_receiver_code;
830 const char *expected_receiver_reason;
831 } close_clean_tests[] = {
832 { SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL", SOUP_WEBSOCKET_CLOSE_NORMAL, "NORMAL" },
833 { SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY", SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "GOING_AWAY" },
834 { SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL },
835 { SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL, SOUP_WEBSOCKET_CLOSE_NORMAL, NULL, SOUP_WEBSOCKET_CLOSE_NO_STATUS, NULL },
836 };
837
838 static void
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)839 do_close_clean_client (Test *test,
840 gushort code,
841 const char *reason,
842 gushort expected_sender_code,
843 const char *expected_sender_reason,
844 gushort expected_receiver_code,
845 const char *expected_receiver_reason)
846 {
847 gboolean close_event_client = FALSE;
848 gboolean close_event_server = FALSE;
849
850 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client);
851 g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server);
852
853 soup_websocket_connection_close (test->client, code, reason);
854 g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING);
855
856 WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED);
857 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
858
859 g_assert (close_event_client);
860 g_assert (close_event_server);
861
862 g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, expected_sender_code);
863 g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, expected_sender_reason);
864 g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, expected_receiver_code);
865 g_assert_cmpstr (soup_websocket_connection_get_close_data (test->server), ==, expected_receiver_reason);
866 }
867
868 static void
test_close_clean_client_soup(Test * test,gconstpointer data)869 test_close_clean_client_soup (Test *test,
870 gconstpointer data)
871 {
872 guint i;
873
874 for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) {
875 setup_soup_connection (test, data);
876
877 do_close_clean_client (test,
878 close_clean_tests[i].code,
879 close_clean_tests[i].reason,
880 close_clean_tests[i].expected_sender_code,
881 close_clean_tests[i].expected_sender_reason,
882 close_clean_tests[i].expected_receiver_code,
883 close_clean_tests[i].expected_receiver_reason);
884
885 teardown_soup_connection (test, data);
886 }
887 }
888
889 static void
test_close_clean_client_direct(Test * test,gconstpointer data)890 test_close_clean_client_direct (Test *test,
891 gconstpointer data)
892 {
893 guint i;
894
895 for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) {
896 setup_direct_connection (test, data);
897
898 do_close_clean_client (test,
899 close_clean_tests[i].code,
900 close_clean_tests[i].reason,
901 close_clean_tests[i].expected_sender_code,
902 close_clean_tests[i].expected_sender_reason,
903 close_clean_tests[i].expected_receiver_code,
904 close_clean_tests[i].expected_receiver_reason);
905
906 teardown_direct_connection (test, data);
907 }
908 }
909
910 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)911 do_close_clean_server (Test *test,
912 gushort code,
913 const char *reason,
914 gushort expected_sender_code,
915 const char *expected_sender_reason,
916 gushort expected_receiver_code,
917 const char *expected_receiver_reason)
918 {
919 gboolean close_event_client = FALSE;
920 gboolean close_event_server = FALSE;
921
922 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client);
923 g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server);
924
925 soup_websocket_connection_close (test->server, code, reason);
926 g_assert_cmpint (soup_websocket_connection_get_state (test->server), ==, SOUP_WEBSOCKET_STATE_CLOSING);
927
928 WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED);
929 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
930
931 g_assert (close_event_client);
932 g_assert (close_event_server);
933
934 g_assert_cmpint (soup_websocket_connection_get_close_code (test->server), ==, expected_sender_code);
935 g_assert_cmpstr (soup_websocket_connection_get_close_data (test->server), ==, expected_sender_reason);
936 g_assert_cmpint (soup_websocket_connection_get_close_code (test->client), ==, expected_receiver_code);
937 g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, expected_receiver_reason);
938 }
939
940 static void
test_close_clean_server_soup(Test * test,gconstpointer data)941 test_close_clean_server_soup (Test *test,
942 gconstpointer data)
943 {
944 guint i;
945
946 for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) {
947 setup_direct_connection (test, data);
948
949 do_close_clean_server (test,
950 close_clean_tests[i].code,
951 close_clean_tests[i].reason,
952 close_clean_tests[i].expected_sender_code,
953 close_clean_tests[i].expected_sender_reason,
954 close_clean_tests[i].expected_receiver_code,
955 close_clean_tests[i].expected_receiver_reason);
956
957 teardown_direct_connection (test, data);
958 }
959 }
960
961 static void
test_close_clean_server_direct(Test * test,gconstpointer data)962 test_close_clean_server_direct (Test *test,
963 gconstpointer data)
964 {
965 guint i;
966
967 for (i = 0; i < G_N_ELEMENTS (close_clean_tests); i++) {
968 setup_direct_connection (test, data);
969
970 do_close_clean_server (test,
971 close_clean_tests[i].code,
972 close_clean_tests[i].reason,
973 close_clean_tests[i].expected_sender_code,
974 close_clean_tests[i].expected_sender_reason,
975 close_clean_tests[i].expected_receiver_code,
976 close_clean_tests[i].expected_receiver_reason);
977
978 teardown_direct_connection (test, data);
979 }
980 }
981
982 static gboolean
on_closing_send_message(SoupWebsocketConnection * ws,gpointer data)983 on_closing_send_message (SoupWebsocketConnection *ws,
984 gpointer data)
985 {
986 GBytes *message = data;
987
988 soup_websocket_connection_send_text (ws, g_bytes_get_data (message, NULL));
989 g_signal_handlers_disconnect_by_func (ws, on_closing_send_message, data);
990 return TRUE;
991 }
992
993 static void
test_message_after_closing(Test * test,gconstpointer data)994 test_message_after_closing (Test *test,
995 gconstpointer data)
996 {
997 gboolean close_event_client = FALSE;
998 gboolean close_event_server = FALSE;
999 GBytes *received = NULL;
1000 GBytes *message;
1001
1002 message = g_bytes_new_static ("another test because", strlen ("another test because"));
1003 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client);
1004 g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
1005 g_signal_connect (test->server, "closed", G_CALLBACK (on_close_set_flag), &close_event_server);
1006 g_signal_connect (test->server, "closing", G_CALLBACK (on_closing_send_message), message);
1007
1008 soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "another reason");
1009 g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING);
1010
1011 WAIT_UNTIL (soup_websocket_connection_get_state (test->server) == SOUP_WEBSOCKET_STATE_CLOSED);
1012 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1013
1014 g_assert (close_event_client);
1015 g_assert (close_event_server);
1016
1017 g_assert (received != NULL);
1018 g_assert (g_bytes_equal (message, received));
1019
1020 g_bytes_unref (received);
1021 g_bytes_unref (message);
1022 }
1023
1024 static gpointer
close_after_close_server_thread(gpointer user_data)1025 close_after_close_server_thread (gpointer user_data)
1026 {
1027 Test *test = user_data;
1028 gsize written;
1029 const char frames[] =
1030 "\x88\x09\x03\xe8""reason1"
1031 "\x88\x09\x03\xe8""reason2";
1032 GSocket *socket;
1033 GError *error = NULL;
1034
1035 g_mutex_lock (&test->mutex);
1036 g_mutex_unlock (&test->mutex);
1037
1038 g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
1039 frames, sizeof (frames) -1, &written, NULL, &error);
1040 g_assert_no_error (error);
1041 g_assert_cmpuint (written, ==, sizeof (frames) - 1);
1042 socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (test->raw_server));
1043 g_socket_shutdown (socket, FALSE, TRUE, &error);
1044 g_assert_no_error (error);
1045
1046 return NULL;
1047 }
1048
1049 static void
test_close_after_close(Test * test,gconstpointer data)1050 test_close_after_close (Test *test,
1051 gconstpointer data)
1052 {
1053 GThread *thread;
1054
1055 g_mutex_lock (&test->mutex);
1056
1057 thread = g_thread_new ("close-after-close-thread", close_after_close_server_thread, test);
1058
1059 soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_NORMAL, "reason1");
1060 g_mutex_unlock (&test->mutex);
1061
1062 g_thread_join (thread);
1063
1064 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1065 g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
1066 g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
1067 g_io_stream_close (test->raw_server, NULL, NULL);
1068 }
1069
1070 static gboolean
on_close_unref_connection(SoupWebsocketConnection * ws,gpointer user_data)1071 on_close_unref_connection (SoupWebsocketConnection *ws,
1072 gpointer user_data)
1073 {
1074 Test *test = user_data;
1075
1076 g_assert_true (test->server == ws);
1077 g_clear_object (&test->server);
1078 return TRUE;
1079 }
1080
1081 static void
test_server_unref_connection_on_close(Test * test,gconstpointer data)1082 test_server_unref_connection_on_close (Test *test,
1083 gconstpointer data)
1084 {
1085 gboolean close_event_client = FALSE;
1086
1087 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client);
1088 g_signal_connect (test->server, "closed", G_CALLBACK (on_close_unref_connection), test);
1089 soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "client closed");
1090 g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING);
1091
1092 WAIT_UNTIL (test->server == NULL);
1093 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1094
1095 g_assert_true (close_event_client);
1096 }
1097
1098 static gpointer
timeout_server_thread(gpointer user_data)1099 timeout_server_thread (gpointer user_data)
1100 {
1101 Test *test = user_data;
1102 GError *error = NULL;
1103
1104 /* don't close until the client has timed out */
1105 g_mutex_lock (&test->mutex);
1106 g_mutex_unlock (&test->mutex);
1107
1108 g_io_stream_close (test->raw_server, NULL, &error);
1109 g_assert_no_error (error);
1110
1111 return NULL;
1112 }
1113
1114 static void
test_close_after_timeout(Test * test,gconstpointer data)1115 test_close_after_timeout (Test *test,
1116 gconstpointer data)
1117 {
1118 gboolean close_event = FALSE;
1119 GThread *thread;
1120
1121 g_mutex_lock (&test->mutex);
1122
1123 /* Note that no real server is around in this test, so no close happens */
1124 thread = g_thread_new ("timeout-thread", timeout_server_thread, test);
1125
1126 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
1127 g_signal_connect (test->client, "error", G_CALLBACK (on_error_not_reached), NULL);
1128
1129 /* Now try and close things */
1130 soup_websocket_connection_close (test->client, 0, NULL);
1131 g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING);
1132
1133 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1134
1135 g_assert (close_event == TRUE);
1136
1137 /* Now actually close the server side stream */
1138 g_mutex_unlock (&test->mutex);
1139 g_thread_join (thread);
1140 }
1141
1142 static gpointer
send_fragments_server_thread(gpointer user_data)1143 send_fragments_server_thread (gpointer user_data)
1144 {
1145 Test *test = user_data;
1146 gsize written;
1147 const char fragments[] = "\x01\x04""one " /* !fin | opcode */
1148 "\x00\x04""two " /* !fin | no opcode */
1149 "\x80\x05""three"; /* fin | no opcode */
1150 GError *error = NULL;
1151
1152 g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
1153 fragments, sizeof (fragments) -1, &written, NULL, &error);
1154 g_assert_no_error (error);
1155 g_assert_cmpuint (written, ==, sizeof (fragments) - 1);
1156 g_io_stream_close (test->raw_server, NULL, &error);
1157 g_assert_no_error (error);
1158
1159 return NULL;
1160 }
1161
1162 static void
do_deflate(z_stream * zstream,const char * str,guint8 * buffer,gsize * length)1163 do_deflate (z_stream *zstream,
1164 const char *str,
1165 guint8 *buffer,
1166 gsize *length)
1167 {
1168 zstream->next_in = (void *)str;
1169 zstream->avail_in = strlen (str);
1170 zstream->next_out = buffer;
1171 zstream->avail_out = 512;
1172
1173 g_assert_cmpint (deflate(zstream, Z_NO_FLUSH), ==, Z_OK);
1174 g_assert_cmpint (zstream->avail_in, ==, 0);
1175 g_assert_cmpint (deflate(zstream, Z_SYNC_FLUSH), ==, Z_OK);
1176 g_assert_cmpint (deflate(zstream, Z_SYNC_FLUSH), ==, Z_BUF_ERROR);
1177
1178 *length = 512 - zstream->avail_out;
1179 g_assert_cmpuint (*length, <, 126);
1180 }
1181
1182 static gpointer
send_compressed_fragments_server_thread(gpointer user_data)1183 send_compressed_fragments_server_thread (gpointer user_data)
1184 {
1185 Test *test = user_data;
1186 gsize written;
1187 z_stream zstream;
1188 GByteArray *data;
1189 guint8 byte;
1190 guint8 buffer[512];
1191 gsize buffer_length;
1192 GError *error = NULL;
1193
1194 memset (&zstream, 0, sizeof(z_stream));
1195 g_assert (deflateInit2 (&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) == Z_OK);
1196
1197 data = g_byte_array_new ();
1198
1199 do_deflate (&zstream, "one ", buffer, &buffer_length);
1200 byte = 0x00 | 0x01 | 0x40; /* !fin | opcode | compressed */
1201 data = g_byte_array_append (data, &byte, 1);
1202 byte = (0xFF & buffer_length); /* mask | 7-bit-len */
1203 data = g_byte_array_append (data, &byte, 1);
1204 data = g_byte_array_append (data, buffer, buffer_length);
1205
1206 do_deflate (&zstream, "two ", buffer, &buffer_length);
1207 byte = 0x00; /* !fin | no opcode */
1208 data = g_byte_array_append (data, &byte, 1);
1209 byte = (0xFF & buffer_length); /* mask | 7-bit-len */
1210 data = g_byte_array_append (data, &byte, 1);
1211 data = g_byte_array_append (data, buffer, buffer_length);
1212
1213 do_deflate (&zstream, "three", buffer, &buffer_length);
1214 g_assert_cmpuint (buffer_length, >=, 4);
1215 buffer_length -= 4;
1216 byte = 0x80; /* fin | no opcode */
1217 data = g_byte_array_append (data, &byte, 1);
1218 byte = (0xFF & buffer_length); /* mask | 7-bit-len */
1219 data = g_byte_array_append (data, &byte, 1);
1220 data = g_byte_array_append (data, buffer, buffer_length);
1221
1222 g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
1223 data->data, data->len, &written, NULL, &error);
1224 g_assert_no_error (error);
1225 g_assert_cmpuint (written, ==, data->len);
1226 g_io_stream_close (test->raw_server, NULL, &error);
1227 g_assert_no_error (error);
1228
1229 deflateEnd (&zstream);
1230 g_byte_array_free (data, TRUE);
1231
1232 return NULL;
1233 }
1234
1235 static void
test_receive_fragmented(Test * test,gconstpointer data)1236 test_receive_fragmented (Test *test,
1237 gconstpointer data)
1238 {
1239 GThread *thread;
1240 GBytes *received = NULL;
1241 GBytes *expect;
1242
1243 thread = g_thread_new ("fragment-thread",
1244 test->enable_extensions ?
1245 send_compressed_fragments_server_thread :
1246 send_fragments_server_thread,
1247 test);
1248
1249 g_signal_connect (test->client, "error", G_CALLBACK (on_error_not_reached), NULL);
1250 g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
1251
1252 WAIT_UNTIL (received != NULL);
1253 expect = g_bytes_new ("one two three", 13);
1254 g_assert (g_bytes_equal (expect, received));
1255 g_bytes_unref (expect);
1256 g_bytes_unref (received);
1257
1258 g_thread_join (thread);
1259
1260 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1261 }
1262
1263 typedef struct {
1264 Test *test;
1265 const char *header;
1266 GString *payload;
1267 } InvalidEncodeLengthTest;
1268
1269 static gpointer
send_invalid_encode_length_server_thread(gpointer user_data)1270 send_invalid_encode_length_server_thread (gpointer user_data)
1271 {
1272 InvalidEncodeLengthTest *test = user_data;
1273 gsize header_size;
1274 gsize written;
1275 GError *error = NULL;
1276
1277 header_size = test->payload->len == 125 ? 6 : 10;
1278 g_output_stream_write_all (g_io_stream_get_output_stream (test->test->raw_server),
1279 test->header, header_size, &written, NULL, &error);
1280 g_assert_no_error (error);
1281 g_assert_cmpuint (written, ==, header_size);
1282
1283 g_output_stream_write_all (g_io_stream_get_output_stream (test->test->raw_server),
1284 test->payload->str, test->payload->len, &written, NULL, &error);
1285 g_assert_no_error (error);
1286 g_assert_cmpuint (written, ==, test->payload->len);
1287
1288 g_io_stream_close (test->test->raw_server, NULL, &error);
1289 g_assert_no_error (error);
1290
1291 return NULL;
1292 }
1293
1294 static void
test_receive_invalid_encode_length_16(Test * test,gconstpointer data)1295 test_receive_invalid_encode_length_16 (Test *test,
1296 gconstpointer data)
1297 {
1298 GThread *thread;
1299 GBytes *received = NULL;
1300 GError *error = NULL;
1301 InvalidEncodeLengthTest context = { test, NULL };
1302 guint i;
1303
1304 g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
1305 g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
1306
1307 /* We use 126(~) as payload length with 125 extended length */
1308 context.header = "\x82~\x00}";
1309 context.payload = g_string_new (NULL);
1310 for (i = 0; i < 125; i++)
1311 g_string_append (context.payload, "X");
1312 thread = g_thread_new ("invalid-encode-length-thread", send_invalid_encode_length_server_thread, &context);
1313
1314 WAIT_UNTIL (error != NULL || received != NULL);
1315 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
1316 g_clear_error (&error);
1317 g_assert_null (received);
1318
1319 g_thread_join (thread);
1320 g_string_free (context.payload, TRUE);
1321
1322 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1323 }
1324
1325 static void
test_receive_invalid_encode_length_64(Test * test,gconstpointer data)1326 test_receive_invalid_encode_length_64 (Test *test,
1327 gconstpointer data)
1328 {
1329 GThread *thread;
1330 GBytes *received = NULL;
1331 GError *error = NULL;
1332 InvalidEncodeLengthTest context = { test, NULL };
1333 guint i;
1334
1335 g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
1336 g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
1337
1338 /* We use 127(\x7f) as payload length with 65535 extended length */
1339 context.header = "\x82\x7f\x00\x00\x00\x00\x00\x00\xff\xff";
1340 context.payload = g_string_new (NULL);
1341 for (i = 0; i < 65535; i++)
1342 g_string_append (context.payload, "X");
1343 thread = g_thread_new ("invalid-encode-length-thread", send_invalid_encode_length_server_thread, &context);
1344
1345 WAIT_UNTIL (error != NULL || received != NULL);
1346 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
1347 g_clear_error (&error);
1348 g_assert_null (received);
1349
1350 g_thread_join (thread);
1351 g_string_free (context.payload, TRUE);
1352
1353 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1354 }
1355
1356 static gpointer
send_masked_frame_server_thread(gpointer user_data)1357 send_masked_frame_server_thread (gpointer user_data)
1358 {
1359 Test *test = user_data;
1360 const char frame[] = "\x82\x8e\x9a";
1361 gsize written;
1362 GError *error = NULL;
1363
1364 g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
1365 frame, sizeof (frame), &written, NULL, &error);
1366 g_assert_no_error (error);
1367 g_assert_cmpuint (written, ==, sizeof (frame));
1368
1369 g_io_stream_close (test->raw_server, NULL, &error);
1370 g_assert_no_error (error);
1371
1372 return NULL;
1373 }
1374
1375 static void
test_client_receive_masked_frame(Test * test,gconstpointer data)1376 test_client_receive_masked_frame (Test *test,
1377 gconstpointer data)
1378 {
1379 GThread *thread;
1380 GBytes *received = NULL;
1381 GError *error = NULL;
1382
1383 g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
1384 g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
1385
1386 thread = g_thread_new ("send-masked-frame-thread", send_masked_frame_server_thread, test);
1387
1388 WAIT_UNTIL (error != NULL || received != NULL);
1389 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
1390 g_clear_error (&error);
1391 g_assert_null (received);
1392
1393 g_thread_join (thread);
1394
1395 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1396 }
1397
1398 static void
test_server_receive_unmasked_frame(Test * test,gconstpointer data)1399 test_server_receive_unmasked_frame (Test *test,
1400 gconstpointer data)
1401 {
1402 GError *error = NULL;
1403 GIOStream *io;
1404 gsize written;
1405 const char *frame;
1406 gboolean close_event = FALSE;
1407
1408 g_signal_handlers_disconnect_by_func (test->server, on_error_not_reached, NULL);
1409 g_signal_connect (test->server, "error", G_CALLBACK (on_error_copy), &error);
1410 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
1411
1412 io = soup_websocket_connection_get_io_stream (test->client);
1413
1414 /* Unmasked frame */
1415 frame = "\x81\x0bHello World";
1416 if (!g_output_stream_write_all (g_io_stream_get_output_stream (io),
1417 frame, 13, &written, NULL, NULL))
1418 g_assert_not_reached ();
1419 g_assert_cmpuint (written, ==, 13);
1420
1421 WAIT_UNTIL (error != NULL);
1422 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
1423 g_clear_error (&error);
1424
1425 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1426 g_assert (close_event);
1427
1428 g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
1429
1430 }
1431
1432 static void
test_client_context_got_server_connection(SoupServer * server,SoupWebsocketConnection * connection,const char * path,SoupClientContext * client,gpointer user_data)1433 test_client_context_got_server_connection (SoupServer *server,
1434 SoupWebsocketConnection *connection,
1435 const char *path,
1436 SoupClientContext *client,
1437 gpointer user_data)
1438 {
1439 Test *test = user_data;
1440 GSocketAddress *addr;
1441 GInetAddress *iaddr;
1442 char *str;
1443 const char *remote_ip;
1444
1445 addr = soup_client_context_get_local_address (client);
1446 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
1447 str = g_inet_address_to_string (iaddr);
1448 if (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4)
1449 g_assert_cmpstr (str, ==, "127.0.0.1");
1450 else
1451 g_assert_cmpstr (str, ==, "::1");
1452 g_free (str);
1453
1454 addr = soup_client_context_get_remote_address (client);
1455 iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
1456 str = g_inet_address_to_string (iaddr);
1457 if (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4)
1458 g_assert_cmpstr (str, ==, "127.0.0.1");
1459 else
1460 g_assert_cmpstr (str, ==, "::1");
1461
1462 remote_ip = soup_client_context_get_host (client);
1463 g_assert_cmpstr (remote_ip, ==, str);
1464 g_free (str);
1465
1466 test->server = g_object_ref (connection);
1467 }
1468
1469 static void
test_client_context(Test * test,gconstpointer unused)1470 test_client_context (Test *test,
1471 gconstpointer unused)
1472 {
1473 setup_soup_server (test, NULL, NULL, test_client_context_got_server_connection, test);
1474 client_connect (test, NULL, NULL, got_client_connection, test);
1475 WAIT_UNTIL (test->server != NULL);
1476 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
1477 g_assert_no_error (test->client_error);
1478 }
1479
1480 static struct {
1481 const char *client_extension;
1482 gboolean expected_prepare_result;
1483 gboolean server_supports_extensions;
1484 gboolean expected_check_result;
1485 gboolean expected_accepted_extension;
1486 gboolean expected_verify_result;
1487 const char *server_extension;
1488 } deflate_negotiate_tests[] = {
1489 { "permessage-deflate",
1490 /* prepare supported check accepted verify */
1491 TRUE, TRUE, TRUE, TRUE, TRUE,
1492 "permessage-deflate"
1493 },
1494 { "permessage-deflate",
1495 /* prepare supported check accepted verify */
1496 TRUE, FALSE, TRUE, FALSE, TRUE,
1497 "permessage-deflate"
1498 },
1499 { "permessage-deflate; server_no_context_takeover",
1500 /* prepare supported check accepted verify */
1501 TRUE, TRUE, TRUE, TRUE, TRUE,
1502 "permessage-deflate; server_no_context_takeover"
1503 },
1504 { "permessage-deflate; client_no_context_takeover",
1505 /* prepare supported check accepted verify */
1506 TRUE, TRUE, TRUE, TRUE, TRUE,
1507 "permessage-deflate; client_no_context_takeover"
1508 },
1509 { "permessage-deflate; server_max_window_bits=8",
1510 /* prepare supported check accepted verify */
1511 TRUE, TRUE, TRUE, TRUE, TRUE,
1512 "permessage-deflate; server_max_window_bits=8"
1513 },
1514 { "permessage-deflate; client_max_window_bits",
1515 /* prepare supported check accepted verify */
1516 TRUE, TRUE, TRUE, TRUE, TRUE,
1517 "permessage-deflate; client_max_window_bits=15"
1518 },
1519 { "permessage-deflate; client_max_window_bits=10",
1520 /* prepare supported check accepted verify */
1521 TRUE, TRUE, TRUE, TRUE, TRUE,
1522 "permessage-deflate; client_max_window_bits=10"
1523 },
1524 { "permessage-deflate; client_no_context_takeover; server_max_window_bits=10",
1525 /* prepare supported check accepted verify */
1526 TRUE, TRUE, TRUE, TRUE, TRUE,
1527 "permessage-deflate; client_no_context_takeover; server_max_window_bits=10"
1528 },
1529 { "permessage-deflate; unknown_parameter",
1530 /* prepare supported check accepted verify */
1531 TRUE, TRUE, FALSE, FALSE, FALSE,
1532 NULL
1533 },
1534 { "permessage-deflate; client_no_context_takeover; client_no_context_takeover",
1535 /* prepare supported check accepted verify */
1536 TRUE, TRUE, FALSE, FALSE, FALSE,
1537 NULL
1538 },
1539 { "permessage-deflate; server_max_window_bits=10; server_max_window_bits=15",
1540 /* prepare supported check accepted verify */
1541 TRUE, TRUE, FALSE, FALSE, FALSE,
1542 NULL
1543 },
1544 { "permessage-deflate; client_no_context_takeover=15",
1545 /* prepare supported check accepted verify */
1546 TRUE, TRUE, FALSE, FALSE, FALSE,
1547 NULL
1548 },
1549 { "permessage-deflate; server_no_context_takeover=15",
1550 /* prepare supported check accepted verify */
1551 TRUE, TRUE, FALSE, FALSE, FALSE,
1552 NULL
1553 },
1554 { "permessage-deflate; server_max_window_bits",
1555 /* prepare supported check accepted verify */
1556 TRUE, TRUE, FALSE, FALSE, FALSE,
1557 NULL
1558 },
1559 { "permessage-deflate; server_max_window_bits=7",
1560 /* prepare supported check accepted verify */
1561 TRUE, TRUE, FALSE, FALSE, FALSE,
1562 NULL
1563 },
1564 { "permessage-deflate; server_max_window_bits=16",
1565 /* prepare supported check accepted verify */
1566 TRUE, TRUE, FALSE, FALSE, FALSE,
1567 NULL
1568 },
1569 { "permessage-deflate; client_max_window_bits=7",
1570 /* prepare supported check accepted verify */
1571 TRUE, TRUE, FALSE, FALSE, FALSE,
1572 NULL
1573 },
1574 { "permessage-deflate; client_max_window_bits=16",
1575 /* prepare supported check accepted verify */
1576 TRUE, TRUE, FALSE, FALSE, FALSE,
1577 NULL
1578 },
1579 { "permessage-deflate; client_max_window_bits=foo",
1580 /* prepare supported check accepted verify */
1581 TRUE, TRUE, FALSE, FALSE, FALSE,
1582 NULL
1583 },
1584 { "permessage-deflate; server_max_window_bits=bar",
1585 /* prepare supported check accepted verify */
1586 TRUE, TRUE, FALSE, FALSE, FALSE,
1587 NULL
1588 },
1589 { "permessage-deflate; client_max_window_bits=15foo",
1590 /* prepare supported check accepted verify */
1591 TRUE, TRUE, FALSE, FALSE, FALSE,
1592 NULL
1593 },
1594 { "permessage-deflate; server_max_window_bits=10bar",
1595 /* prepare supported check accepted verify */
1596 TRUE, TRUE, FALSE, FALSE, FALSE,
1597 NULL
1598 },
1599 };
1600
1601 static void
test_deflate_negotiate_direct(Test * test,gconstpointer unused)1602 test_deflate_negotiate_direct (Test *test,
1603 gconstpointer unused)
1604 {
1605 GPtrArray *supported_extensions;
1606 guint i;
1607
1608 supported_extensions = g_ptr_array_new_full (1, g_type_class_unref);
1609 g_ptr_array_add (supported_extensions, g_type_class_ref (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE));
1610
1611 for (i = 0; i < G_N_ELEMENTS (deflate_negotiate_tests); i++) {
1612 SoupMessage *msg;
1613 gboolean result;
1614 GList *accepted_extensions = NULL;
1615 GError *error = NULL;
1616
1617 msg = soup_message_new ("GET", "http://127.0.0.1");
1618
1619 soup_websocket_client_prepare_handshake (msg, NULL, NULL);
1620 soup_message_headers_append (msg->request_headers, "Sec-WebSocket-Extensions", deflate_negotiate_tests[i].client_extension);
1621 result = soup_websocket_server_check_handshake_with_extensions (msg, NULL, NULL,
1622 deflate_negotiate_tests[i].server_supports_extensions ?
1623 supported_extensions : NULL,
1624 &error);
1625 g_assert (result == deflate_negotiate_tests[i].expected_check_result);
1626 if (result) {
1627 g_assert_no_error (error);
1628 } else {
1629 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE);
1630 g_clear_error (&error);
1631 }
1632
1633 result = soup_websocket_server_process_handshake_with_extensions (msg, NULL, NULL,
1634 deflate_negotiate_tests[i].server_supports_extensions ?
1635 supported_extensions : NULL,
1636 &accepted_extensions);
1637 g_assert (result == deflate_negotiate_tests[i].expected_check_result);
1638 if (deflate_negotiate_tests[i].expected_accepted_extension) {
1639 const char *extension;
1640
1641 extension = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Extensions");
1642 g_assert_cmpstr (extension, ==, deflate_negotiate_tests[i].server_extension);
1643 g_assert_nonnull (accepted_extensions);
1644 g_assert_cmpuint (g_list_length (accepted_extensions), ==, 1);
1645 g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (accepted_extensions->data));
1646 g_list_free_full (accepted_extensions, g_object_unref);
1647 accepted_extensions = NULL;
1648 } else {
1649 g_assert_null (accepted_extensions);
1650 }
1651
1652 result = soup_websocket_client_verify_handshake_with_extensions (msg, supported_extensions, &accepted_extensions, &error);
1653 g_assert (result == deflate_negotiate_tests[i].expected_verify_result);
1654 if (result) {
1655 g_assert_no_error (error);
1656 } else {
1657 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE);
1658 g_clear_error (&error);
1659 }
1660 if (deflate_negotiate_tests[i].expected_accepted_extension) {
1661 g_assert_nonnull (accepted_extensions);
1662 g_assert_cmpuint (g_list_length (accepted_extensions), ==, 1);
1663 g_assert (SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE (accepted_extensions->data));
1664 g_list_free_full (accepted_extensions, g_object_unref);
1665 accepted_extensions = NULL;
1666 } else {
1667 g_assert_null (accepted_extensions);
1668 }
1669
1670 g_object_unref (msg);
1671 }
1672
1673 g_ptr_array_unref (supported_extensions);
1674 }
1675
1676 static void
test_deflate_disabled_in_message_direct(Test * test,gconstpointer unused)1677 test_deflate_disabled_in_message_direct (Test *test,
1678 gconstpointer unused)
1679 {
1680 SoupMessage *msg;
1681 GPtrArray *supported_extensions;
1682 GList *accepted_extensions = NULL;
1683 GError *error = NULL;
1684
1685 supported_extensions = g_ptr_array_new_full (1, g_type_class_unref);
1686 g_ptr_array_add (supported_extensions, g_type_class_ref (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE));
1687
1688 msg = soup_message_new ("GET", "http://127.0.0.1");
1689 soup_message_disable_feature (msg, SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE);
1690 soup_websocket_client_prepare_handshake_with_extensions (msg, NULL, NULL, supported_extensions);
1691 g_assert_cmpstr (soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Extensions"), ==, NULL);
1692
1693 g_assert_true (soup_websocket_server_check_handshake_with_extensions (msg, NULL, NULL, supported_extensions, &error));
1694 g_assert_no_error (error);
1695
1696 g_assert_true (soup_websocket_server_process_handshake_with_extensions (msg, NULL, NULL, supported_extensions, &accepted_extensions));
1697 g_assert_null (accepted_extensions);
1698 g_assert_cmpstr (soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Extensions"), ==, NULL);
1699
1700 g_assert_true (soup_websocket_client_verify_handshake_with_extensions (msg, supported_extensions, &accepted_extensions, &error));
1701 g_assert_no_error (error);
1702 g_assert_null (accepted_extensions);
1703
1704 g_object_unref (msg);
1705 g_ptr_array_unref (supported_extensions);
1706 }
1707
1708 static void
test_deflate_disabled_in_message_soup(Test * test,gconstpointer unused)1709 test_deflate_disabled_in_message_soup (Test *test,
1710 gconstpointer unused)
1711 {
1712 test->enable_extensions = TRUE;
1713 test->disable_deflate_in_message = TRUE;
1714 setup_soup_server (test, NULL, NULL, got_server_connection, test);
1715 client_connect (test, NULL, NULL, got_client_connection, test);
1716 WAIT_UNTIL (test->server != NULL);
1717 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
1718 g_assert_no_error (test->client_error);
1719
1720 g_assert_cmpstr (soup_message_headers_get_one (test->msg->request_headers, "Sec-WebSocket-Extensions"), ==, NULL);
1721 g_assert_cmpstr (soup_message_headers_get_one (test->msg->response_headers, "Sec-WebSocket-Extensions"), ==, NULL);
1722 }
1723
1724 static gpointer
send_compressed_fragments_error_server_thread(gpointer user_data)1725 send_compressed_fragments_error_server_thread (gpointer user_data)
1726 {
1727 Test *test = user_data;
1728 gsize written;
1729 z_stream zstream;
1730 GByteArray *data;
1731 guint8 byte;
1732 guint8 buffer[512];
1733 gsize buffer_length;
1734 GError *error = NULL;
1735
1736 memset (&zstream, 0, sizeof(z_stream));
1737 g_assert (deflateInit2 (&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) == Z_OK);
1738
1739 data = g_byte_array_new ();
1740
1741 do_deflate (&zstream, "one ", buffer, &buffer_length);
1742 byte = 0x00 | 0x01 | 0x40; /* !fin | opcode | compressed */
1743 data = g_byte_array_append (data, &byte, 1);
1744 byte = (0xFF & buffer_length); /* mask | 7-bit-len */
1745 data = g_byte_array_append (data, &byte, 1);
1746 data = g_byte_array_append (data, buffer, buffer_length);
1747
1748 /* Only the first fragment should include the compressed bit set. */
1749 do_deflate (&zstream, "two ", buffer, &buffer_length);
1750 byte = 0x00 | 0x00 | 0x40; /* !fin | no opcode | compressed */
1751 data = g_byte_array_append (data, &byte, 1);
1752 byte = (0xFF & buffer_length); /* mask | 7-bit-len */
1753 data = g_byte_array_append (data, &byte, 1);
1754 data = g_byte_array_append (data, buffer, buffer_length);
1755
1756 do_deflate (&zstream, "three", buffer, &buffer_length);
1757 g_assert_cmpuint (buffer_length, >=, 4);
1758 buffer_length -= 4;
1759 byte = 0x80; /* fin | no opcode */
1760 data = g_byte_array_append (data, &byte, 1);
1761 byte = (0xFF & buffer_length); /* mask | 7-bit-len */
1762 data = g_byte_array_append (data, &byte, 1);
1763 data = g_byte_array_append (data, buffer, buffer_length);
1764
1765 g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
1766 data->data, data->len, &written, NULL, &error);
1767 g_assert_no_error (error);
1768 g_assert_cmpuint (written, ==, data->len);
1769 g_io_stream_close (test->raw_server, NULL, &error);
1770 g_assert_no_error (error);
1771
1772 deflateEnd (&zstream);
1773 g_byte_array_free (data, TRUE);
1774
1775 return NULL;
1776 }
1777
1778 static void
test_deflate_receive_fragmented_error(Test * test,gconstpointer data)1779 test_deflate_receive_fragmented_error (Test *test,
1780 gconstpointer data)
1781 {
1782 GThread *thread;
1783 GBytes *received = NULL;
1784 gboolean close_event = FALSE;
1785 GError *error = NULL;
1786
1787 thread = g_thread_new ("deflate-fragment-error-thread",
1788 send_compressed_fragments_error_server_thread,
1789 test);
1790
1791 g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
1792 g_signal_connect (test->client, "message", G_CALLBACK (on_text_message), &received);
1793 g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event);
1794
1795 WAIT_UNTIL (error != NULL || received != NULL);
1796 g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
1797 g_clear_error (&error);
1798 g_assert_null (received);
1799
1800 g_thread_join (thread);
1801
1802 WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
1803 g_assert (close_event);
1804 }
1805
1806 static void
test_cookies_in_request(Test * test,gconstpointer data)1807 test_cookies_in_request (Test *test,
1808 gconstpointer data)
1809 {
1810 SoupCookie *cookie;
1811 const char *cookie_header;
1812 SoupCookie *requested_cookie;
1813
1814 cookie = soup_cookie_new ("foo", "bar", "127.0.0.1", "/", -1);
1815 test->initial_cookies = g_list_prepend (test->initial_cookies, soup_cookie_copy (cookie));
1816
1817 setup_soup_server (test, NULL, NULL, got_server_connection, test);
1818 client_connect (test, NULL, NULL, got_client_connection, test);
1819 WAIT_UNTIL (test->server != NULL);
1820 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
1821 g_assert_no_error (test->client_error);
1822
1823 cookie_header = soup_message_headers_get_one (test->msg->request_headers, "Cookie");
1824 requested_cookie = soup_cookie_parse (cookie_header, NULL);
1825 g_assert_true (soup_cookie_equal (cookie, requested_cookie));
1826 soup_cookie_free (cookie);
1827 soup_cookie_free (requested_cookie);
1828 }
1829
1830 static void
cookies_test_websocket_server_request_started(SoupServer * server,SoupMessage * msg,SoupClientContext * client,gpointer user_data)1831 cookies_test_websocket_server_request_started (SoupServer *server, SoupMessage *msg,
1832 SoupClientContext *client, gpointer user_data)
1833 {
1834 soup_message_headers_append (msg->response_headers, "Set-Cookie", "foo=bar; Path=/");
1835 }
1836
1837 static void
test_cookies_in_response(Test * test,gconstpointer data)1838 test_cookies_in_response (Test *test,
1839 gconstpointer data)
1840 {
1841 SoupCookieJar *jar;
1842 GSList *cookies;
1843 SoupCookie *cookie;
1844
1845 setup_soup_server (test, NULL, NULL, got_server_connection, test);
1846 g_signal_connect (test->soup_server, "request-started",
1847 G_CALLBACK (cookies_test_websocket_server_request_started),
1848 NULL);
1849 client_connect (test, NULL, NULL, got_client_connection, test);
1850 WAIT_UNTIL (test->server != NULL);
1851 WAIT_UNTIL (test->client != NULL || test->client_error != NULL);
1852 g_assert_no_error (test->client_error);
1853
1854 jar = SOUP_COOKIE_JAR (soup_session_get_feature (test->session, SOUP_TYPE_COOKIE_JAR));
1855 cookies = soup_cookie_jar_all_cookies (jar);
1856 g_assert_nonnull (cookies);
1857 g_assert_cmpuint (g_slist_length (cookies), ==, 1);
1858 cookie = soup_cookie_new ("foo", "bar", "127.0.0.1", "/", -1);
1859 g_assert_true (soup_cookie_equal (cookie, (SoupCookie *)cookies->data));
1860 g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
1861 soup_cookie_free (cookie);
1862 }
1863
1864 int
main(int argc,char * argv[])1865 main (int argc,
1866 char *argv[])
1867 {
1868 int ret;
1869
1870 test_init (argc, argv, NULL);
1871
1872 g_test_add ("/websocket/soup/handshake", Test, NULL,
1873 setup_soup_connection,
1874 test_handshake,
1875 teardown_soup_connection);
1876
1877 g_test_add ("/websocket/soup/handshake-error", Test, NULL, NULL,
1878 test_handshake_unsupported_extension,
1879 teardown_soup_connection);
1880
1881 g_test_add ("/websocket/direct/send-client-to-server", Test, NULL,
1882 setup_direct_connection,
1883 test_send_client_to_server,
1884 teardown_direct_connection);
1885 g_test_add ("/websocket/soup/send-client-to-server", Test, NULL,
1886 setup_soup_connection,
1887 test_send_client_to_server,
1888 teardown_soup_connection);
1889
1890 g_test_add ("/websocket/direct/send-server-to-client", Test, NULL,
1891 setup_direct_connection,
1892 test_send_server_to_client,
1893 teardown_direct_connection);
1894 g_test_add ("/websocket/soup/send-server-to-client", Test, NULL,
1895 setup_soup_connection,
1896 test_send_server_to_client,
1897 teardown_soup_connection);
1898
1899 g_test_add ("/websocket/direct/send-big-packets", Test, NULL,
1900 setup_direct_connection,
1901 test_send_big_packets,
1902 teardown_direct_connection);
1903 g_test_add ("/websocket/soup/send-big-packets", Test, NULL,
1904 setup_soup_connection,
1905 test_send_big_packets,
1906 teardown_soup_connection);
1907
1908 g_test_add ("/websocket/direct/send-empty-packets", Test, NULL,
1909 setup_direct_connection,
1910 test_send_empty_packets,
1911 teardown_direct_connection);
1912 g_test_add ("/websocket/soup/send-empty-packets", Test, NULL,
1913 setup_soup_connection,
1914 test_send_empty_packets,
1915 teardown_soup_connection);
1916
1917 g_test_add ("/websocket/direct/send-bad-data", Test, NULL,
1918 setup_direct_connection,
1919 test_send_bad_data,
1920 teardown_direct_connection);
1921 g_test_add ("/websocket/soup/send-bad-data", Test, NULL,
1922 setup_soup_connection,
1923 test_send_bad_data,
1924 teardown_soup_connection);
1925
1926 g_test_add ("/websocket/direct/close-clean-client", Test, NULL, NULL,
1927 test_close_clean_client_direct,
1928 NULL);
1929 g_test_add ("/websocket/soup/close-clean-client", Test, NULL, NULL,
1930 test_close_clean_client_soup,
1931 NULL);
1932
1933 g_test_add ("/websocket/direct/close-clean-server", Test, NULL, NULL,
1934 test_close_clean_server_direct,
1935 NULL);
1936 g_test_add ("/websocket/soup/close-clean-server", Test, NULL, NULL,
1937 test_close_clean_server_soup,
1938 NULL);
1939
1940 g_test_add ("/websocket/direct/message-after-closing", Test, NULL,
1941 setup_direct_connection,
1942 test_message_after_closing,
1943 teardown_direct_connection);
1944 g_test_add ("/websocket/soup/message-after-closing", Test, NULL,
1945 setup_soup_connection,
1946 test_message_after_closing,
1947 teardown_soup_connection);
1948
1949 g_test_add ("/websocket/direct/close-after-close", Test, NULL,
1950 setup_half_direct_connection,
1951 test_close_after_close,
1952 teardown_direct_connection);
1953
1954 g_test_add ("/websocket/soup/server-unref-connection-on-close", Test, NULL,
1955 setup_soup_connection,
1956 test_server_unref_connection_on_close,
1957 teardown_soup_connection);
1958
1959
1960 g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL,
1961 test_protocol_negotiate_direct,
1962 NULL);
1963 g_test_add ("/websocket/soup/protocol-negotiate", Test, NULL, NULL,
1964 test_protocol_negotiate_soup,
1965 teardown_soup_connection);
1966
1967 g_test_add ("/websocket/direct/protocol-mismatch", Test, NULL, NULL,
1968 test_protocol_mismatch_direct,
1969 NULL);
1970 g_test_add ("/websocket/soup/protocol-mismatch", Test, NULL, NULL,
1971 test_protocol_mismatch_soup,
1972 teardown_soup_connection);
1973
1974 g_test_add ("/websocket/direct/protocol-server-any", Test, NULL, NULL,
1975 test_protocol_server_any_direct,
1976 NULL);
1977 g_test_add ("/websocket/soup/protocol-server-any", Test, NULL, NULL,
1978 test_protocol_server_any_soup,
1979 teardown_soup_connection);
1980
1981 g_test_add ("/websocket/direct/protocol-client-any", Test, NULL, NULL,
1982 test_protocol_client_any_direct,
1983 NULL);
1984 g_test_add ("/websocket/soup/protocol-client-any", Test, NULL, NULL,
1985 test_protocol_client_any_soup,
1986 teardown_soup_connection);
1987
1988
1989 g_test_add ("/websocket/direct/receive-fragmented", Test, NULL,
1990 setup_half_direct_connection,
1991 test_receive_fragmented,
1992 teardown_direct_connection);
1993
1994 g_test_add ("/websocket/direct/receive-invalid-encode-length-16", Test, NULL,
1995 setup_half_direct_connection,
1996 test_receive_invalid_encode_length_16,
1997 teardown_direct_connection);
1998
1999 g_test_add ("/websocket/direct/receive-invalid-encode-length-64", Test, NULL,
2000 setup_half_direct_connection,
2001 test_receive_invalid_encode_length_64,
2002 teardown_direct_connection);
2003
2004 g_test_add ("/websocket/direct/client-receive-masked-frame", Test, NULL,
2005 setup_half_direct_connection,
2006 test_client_receive_masked_frame,
2007 teardown_direct_connection);
2008
2009 g_test_add ("/websocket/direct/server-receive-unmasked-frame", Test, NULL,
2010 setup_direct_connection,
2011 test_server_receive_unmasked_frame,
2012 teardown_direct_connection);
2013 g_test_add ("/websocket/soup/server-receive-unmasked-frame", Test, NULL,
2014 setup_soup_connection,
2015 test_server_receive_unmasked_frame,
2016 teardown_soup_connection);
2017
2018 g_test_add ("/websocket/soup/deflate-handshake", Test, NULL,
2019 setup_soup_connection_with_extensions,
2020 test_handshake,
2021 teardown_soup_connection);
2022
2023 g_test_add ("/websocket/direct/deflate-negotiate", Test, NULL, NULL,
2024 test_deflate_negotiate_direct,
2025 NULL);
2026
2027 g_test_add ("/websocket/direct/deflate-disabled-in-message", Test, NULL, NULL,
2028 test_deflate_disabled_in_message_direct,
2029 NULL);
2030 g_test_add ("/websocket/soup/deflate-disabled-in-message", Test, NULL, NULL,
2031 test_deflate_disabled_in_message_soup,
2032 teardown_soup_connection);
2033
2034 g_test_add ("/websocket/direct/deflate-send-client-to-server", Test, NULL,
2035 setup_direct_connection_with_extensions,
2036 test_send_client_to_server,
2037 teardown_direct_connection);
2038 g_test_add ("/websocket/soup/deflate-send-client-to-server", Test, NULL,
2039 setup_soup_connection_with_extensions,
2040 test_send_client_to_server,
2041 teardown_soup_connection);
2042
2043 g_test_add ("/websocket/direct/deflate-send-server-to-client", Test, NULL,
2044 setup_direct_connection_with_extensions,
2045 test_send_server_to_client,
2046 teardown_direct_connection);
2047 g_test_add ("/websocket/soup/deflate-send-server-to-client", Test, NULL,
2048 setup_soup_connection_with_extensions,
2049 test_send_server_to_client,
2050 teardown_soup_connection);
2051
2052 g_test_add ("/websocket/direct/deflate-send-big-packets", Test, NULL,
2053 setup_direct_connection_with_extensions,
2054 test_send_big_packets,
2055 teardown_direct_connection);
2056 g_test_add ("/websocket/soup/deflate-send-big-packets", Test, NULL,
2057 setup_soup_connection_with_extensions,
2058 test_send_big_packets,
2059 teardown_soup_connection);
2060
2061 g_test_add ("/websocket/direct/deflate-send-empty-packets", Test, NULL,
2062 setup_direct_connection_with_extensions,
2063 test_send_empty_packets,
2064 teardown_direct_connection);
2065 g_test_add ("/websocket/soup/deflate-send-empty-packets", Test, NULL,
2066 setup_soup_connection_with_extensions,
2067 test_send_empty_packets,
2068 teardown_soup_connection);
2069
2070 g_test_add ("/websocket/direct/deflate-receive-fragmented", Test, NULL,
2071 setup_half_direct_connection_with_extensions,
2072 test_receive_fragmented,
2073 teardown_direct_connection);
2074 g_test_add ("/websocket/direct/deflate-receive-fragmented-error", Test, NULL,
2075 setup_half_direct_connection_with_extensions,
2076 test_deflate_receive_fragmented_error,
2077 teardown_direct_connection);
2078
2079 if (g_test_slow ()) {
2080 g_test_add ("/websocket/direct/close-after-timeout", Test, NULL,
2081 setup_half_direct_connection,
2082 test_close_after_timeout,
2083 teardown_direct_connection);
2084 }
2085
2086 g_test_add ("/websocket/soup/client-context", Test, NULL, NULL,
2087 test_client_context,
2088 teardown_soup_connection);
2089
2090 g_test_add ("/websocket/soup/cookies-in-request", Test, NULL, NULL,
2091 test_cookies_in_request,
2092 teardown_soup_connection);
2093 g_test_add ("/websocket/soup/cookies-in-response", Test, NULL, NULL,
2094 test_cookies_in_response,
2095 teardown_soup_connection);
2096
2097 ret = g_test_run ();
2098
2099 test_cleanup ();
2100 return ret;
2101 }
2102