• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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