• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-websocket-connection.c: This file was originally part of Cockpit.
4  *
5  * Copyright 2013, 2014 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 this library; If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include "soup-websocket-connection.h"
26 #include "soup-enum-types.h"
27 #include "soup-io-stream.h"
28 #include "soup-uri.h"
29 #include "soup-websocket-extension.h"
30 
31 /*
32  * SECTION:websocketconnection
33  * @title: SoupWebsocketConnection
34  * @short_description: A WebSocket connection
35  *
36  * A #SoupWebsocketConnection is a WebSocket connection to a peer.
37  * This API is modeled after the W3C API for interacting with
38  * WebSockets.
39  *
40  * The #SoupWebsocketConnection:state property will indicate the
41  * state of the connection.
42  *
43  * Use soup_websocket_connection_send() to send a message to the peer.
44  * When a message is received the #SoupWebsocketConnection::message
45  * signal will fire.
46  *
47  * The soup_websocket_connection_close() function will perform an
48  * orderly close of the connection. The
49  * #SoupWebsocketConnection::closed signal will fire once the
50  * connection closes, whether it was initiated by this side or the
51  * peer.
52  *
53  * Connect to the #SoupWebsocketConnection::closing signal to detect
54  * when either peer begins closing the connection.
55  */
56 
57 /**
58  * SoupWebsocketConnection:
59  *
60  * A class representing a WebSocket connection.
61  *
62  * Since: 2.50
63  */
64 
65 /**
66  * SoupWebsocketConnectionClass:
67  * @message: default handler for the #SoupWebsocketConnection::message signal
68  * @error: default handler for the #SoupWebsocketConnection::error signal
69  * @closing: the default handler for the #SoupWebsocketConnection:closing signal
70  * @closed: default handler for the #SoupWebsocketConnection::closed signal
71  * @pong: default handler for the #SoupWebsocketConnection::pong signal
72  *
73  * The abstract base class for #SoupWebsocketConnection
74  *
75  * Since: 2.50
76  */
77 
78 enum {
79 	PROP_0,
80 	PROP_IO_STREAM,
81 	PROP_CONNECTION_TYPE,
82 	PROP_URI,
83 	PROP_ORIGIN,
84 	PROP_PROTOCOL,
85 	PROP_STATE,
86 	PROP_MAX_INCOMING_PAYLOAD_SIZE,
87 	PROP_KEEPALIVE_INTERVAL,
88 	PROP_EXTENSIONS
89 };
90 
91 enum {
92 	MESSAGE,
93 	ERROR,
94 	CLOSING,
95 	CLOSED,
96 	PONG,
97 	NUM_SIGNALS
98 };
99 
100 static guint signals[NUM_SIGNALS] = { 0, };
101 
102 typedef enum {
103 	SOUP_WEBSOCKET_QUEUE_NORMAL = 0,
104 	SOUP_WEBSOCKET_QUEUE_URGENT = 1 << 0,
105 	SOUP_WEBSOCKET_QUEUE_LAST = 1 << 1,
106 } SoupWebsocketQueueFlags;
107 
108 typedef struct {
109 	GBytes *data;
110 	gsize sent;
111 	gsize amount;
112 	SoupWebsocketQueueFlags flags;
113 	gboolean pending;
114 } Frame;
115 
116 struct _SoupWebsocketConnectionPrivate {
117 	GIOStream *io_stream;
118 	SoupWebsocketConnectionType connection_type;
119 	SoupURI *uri;
120 	char *origin;
121 	char *protocol;
122 	guint64 max_incoming_payload_size;
123 	guint keepalive_interval;
124 
125 	gushort peer_close_code;
126 	char *peer_close_data;
127 	gboolean close_sent;
128 	gboolean close_received;
129 	gboolean dirty_close;
130 	GSource *close_timeout;
131 
132 	GMainContext *main_context;
133 
134 	gboolean io_closing;
135 	gboolean io_closed;
136 
137 	GPollableInputStream *input;
138 	GSource *input_source;
139 	GByteArray *incoming;
140 
141 	GPollableOutputStream *output;
142 	GSource *output_source;
143 	GQueue outgoing;
144 
145 	/* Current message being assembled */
146 	guint8 message_opcode;
147 	GByteArray *message_data;
148 
149 	GSource *keepalive_timeout;
150 
151 	GList *extensions;
152 };
153 
154 #define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT   128 * 1024
155 #define READ_BUFFER_SIZE 1024
156 #define MASK_LENGTH 4
157 
158 G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, soup_websocket_connection, G_TYPE_OBJECT)
159 
160 static void queue_frame (SoupWebsocketConnection *self, SoupWebsocketQueueFlags flags,
161 			 gpointer data, gsize len, gsize amount);
162 
163 static void emit_error_and_close (SoupWebsocketConnection *self,
164 				  GError *error, gboolean prejudice);
165 
166 static void protocol_error_and_close (SoupWebsocketConnection *self);
167 
168 static gboolean on_web_socket_input (GObject *pollable_stream,
169 				     gpointer user_data);
170 static gboolean on_web_socket_output (GObject *pollable_stream,
171 				      gpointer user_data);
172 
173 /* Code below is based on g_utf8_validate() implementation,
174  * but handling NULL characters as valid, as expected by
175  * WebSockets and compliant with RFC 3629.
176  */
177 #define VALIDATE_BYTE(mask, expect)                             \
178         G_STMT_START {                                          \
179           if (G_UNLIKELY((*(guchar *)p & (mask)) != (expect)))  \
180                   return FALSE;                                 \
181         } G_STMT_END
182 
183 /* see IETF RFC 3629 Section 4 */
184 static gboolean
utf8_validate(const char * str,gsize max_len)185 utf8_validate (const char *str,
186                gsize max_len)
187 
188 {
189         const gchar *p;
190 
191         for (p = str; ((p - str) < max_len); p++) {
192                 if (*(guchar *)p < 128)
193                         /* done */;
194                 else {
195                         if (*(guchar *)p < 0xe0) { /* 110xxxxx */
196                                 if (G_UNLIKELY (max_len - (p - str) < 2))
197                                         return FALSE;
198 
199                                 if (G_UNLIKELY (*(guchar *)p < 0xc2))
200                                         return FALSE;
201                         } else {
202                                 if (*(guchar *)p < 0xf0) { /* 1110xxxx */
203                                         if (G_UNLIKELY (max_len - (p - str) < 3))
204                                                 return FALSE;
205 
206                                         switch (*(guchar *)p++ & 0x0f) {
207                                         case 0:
208                                                 VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
209                                                 break;
210                                         case 0x0d:
211                                                 VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
212                                                 break;
213                                         default:
214                                                 VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
215                                         }
216                                 } else if (*(guchar *)p < 0xf5) { /* 11110xxx excluding out-of-range */
217                                         if (G_UNLIKELY (max_len - (p - str) < 4))
218                                                 return FALSE;
219 
220                                         switch (*(guchar *)p++ & 0x07) {
221                                         case 0:
222                                                 VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
223                                                 if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
224                                                         return FALSE;
225                                                 break;
226                                         case 4:
227                                                 VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
228                                                 break;
229                                         default:
230                                                 VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
231                                         }
232                                         p++;
233                                         VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
234                                 } else {
235                                         return FALSE;
236                                 }
237                         }
238 
239                         p++;
240                         VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
241                 }
242         }
243 
244         return TRUE;
245 }
246 
247 #undef VALIDATE_BYTE
248 
249 static void
frame_free(gpointer data)250 frame_free (gpointer data)
251 {
252 	Frame *frame = data;
253 
254 	if (frame) {
255 		g_bytes_unref (frame->data);
256 		g_slice_free (Frame, frame);
257 	}
258 }
259 
260 static void
soup_websocket_connection_init(SoupWebsocketConnection * self)261 soup_websocket_connection_init (SoupWebsocketConnection *self)
262 {
263 	SoupWebsocketConnectionPrivate *pv;
264 
265 	pv = self->pv = soup_websocket_connection_get_instance_private (self);
266 
267 	pv->incoming = g_byte_array_sized_new (1024);
268 	g_queue_init (&pv->outgoing);
269 	pv->main_context = g_main_context_ref_thread_default ();
270 }
271 
272 static void
on_iostream_closed(GObject * source,GAsyncResult * result,gpointer user_data)273 on_iostream_closed (GObject *source,
274                     GAsyncResult *result,
275                     gpointer user_data)
276 {
277 	SoupWebsocketConnection *self = user_data;
278 	SoupWebsocketConnectionPrivate *pv = self->pv;
279 	GError *error = NULL;
280 
281 	/* We treat connection as closed even if close fails */
282 	pv->io_closed = TRUE;
283 	g_io_stream_close_finish (pv->io_stream, result, &error);
284 
285 	if (error) {
286 		g_debug ("error closing web socket stream: %s", error->message);
287 		if (!pv->dirty_close)
288 			g_signal_emit (self, signals[ERROR], 0, error);
289 		pv->dirty_close = TRUE;
290 		g_error_free (error);
291 	}
292 
293 	g_assert (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED);
294 	g_debug ("closed: completed io stream close");
295 	g_signal_emit (self, signals[CLOSED], 0);
296 
297 	g_object_unref (self);
298 }
299 
300 static void
soup_websocket_connection_start_input_source(SoupWebsocketConnection * self)301 soup_websocket_connection_start_input_source (SoupWebsocketConnection *self)
302 {
303 	SoupWebsocketConnectionPrivate *pv = self->pv;
304 
305 	if (pv->input_source)
306 		return;
307 
308 	pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
309 	g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
310 	g_source_attach (pv->input_source, pv->main_context);
311 }
312 
313 static void
soup_websocket_connection_stop_input_source(SoupWebsocketConnection * self)314 soup_websocket_connection_stop_input_source (SoupWebsocketConnection *self)
315 {
316 	SoupWebsocketConnectionPrivate *pv = self->pv;
317 
318 	if (pv->input_source) {
319 		g_debug ("stopping input source");
320 		g_source_destroy (pv->input_source);
321 		g_source_unref (pv->input_source);
322 		pv->input_source = NULL;
323 	}
324 }
325 
326 static void
soup_websocket_connection_start_output_source(SoupWebsocketConnection * self)327 soup_websocket_connection_start_output_source (SoupWebsocketConnection *self)
328 {
329 	SoupWebsocketConnectionPrivate *pv = self->pv;
330 
331 	if (pv->output_source)
332 		return;
333 
334 	pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
335 	g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
336 	g_source_attach (pv->output_source, pv->main_context);
337 }
338 
339 static void
soup_websocket_connection_stop_output_source(SoupWebsocketConnection * self)340 soup_websocket_connection_stop_output_source (SoupWebsocketConnection *self)
341 {
342 	SoupWebsocketConnectionPrivate *pv = self->pv;
343 
344 	if (pv->output_source) {
345 		g_debug ("stopping output source");
346 		g_source_destroy (pv->output_source);
347 		g_source_unref (pv->output_source);
348 		pv->output_source = NULL;
349 	}
350 }
351 
352 static void
keepalive_stop_timeout(SoupWebsocketConnection * self)353 keepalive_stop_timeout (SoupWebsocketConnection *self)
354 {
355 	SoupWebsocketConnectionPrivate *pv = self->pv;
356 
357 	if (pv->keepalive_timeout) {
358 		g_source_destroy (pv->keepalive_timeout);
359 		g_source_unref (pv->keepalive_timeout);
360 		pv->keepalive_timeout = NULL;
361 	}
362 }
363 
364 static void
close_io_stop_timeout(SoupWebsocketConnection * self)365 close_io_stop_timeout (SoupWebsocketConnection *self)
366 {
367 	SoupWebsocketConnectionPrivate *pv = self->pv;
368 
369 	if (pv->close_timeout) {
370 		g_source_destroy (pv->close_timeout);
371 		g_source_unref (pv->close_timeout);
372 		pv->close_timeout = NULL;
373 	}
374 }
375 
376 static void
close_io_stream(SoupWebsocketConnection * self)377 close_io_stream (SoupWebsocketConnection *self)
378 {
379 	SoupWebsocketConnectionPrivate *pv = self->pv;
380 
381 	keepalive_stop_timeout (self);
382 	close_io_stop_timeout (self);
383 
384 	if (!pv->io_closing) {
385 		soup_websocket_connection_stop_input_source (self);
386 		soup_websocket_connection_stop_output_source (self);
387 		pv->io_closing = TRUE;
388 		g_debug ("closing io stream");
389 		g_io_stream_close_async (pv->io_stream, G_PRIORITY_DEFAULT,
390 					 NULL, on_iostream_closed, g_object_ref (self));
391 	}
392 
393 	g_object_notify (G_OBJECT (self), "state");
394 }
395 
396 static void
shutdown_wr_io_stream(SoupWebsocketConnection * self)397 shutdown_wr_io_stream (SoupWebsocketConnection *self)
398 {
399 	SoupWebsocketConnectionPrivate *pv = self->pv;
400 	GSocket *socket;
401 	GIOStream *base_iostream;
402 	GError *error = NULL;
403 
404 	soup_websocket_connection_stop_output_source (self);
405 
406 	base_iostream = SOUP_IS_IO_STREAM (pv->io_stream) ?
407 		soup_io_stream_get_base_iostream (SOUP_IO_STREAM (pv->io_stream)) :
408 		pv->io_stream;
409 
410 	if (G_IS_SOCKET_CONNECTION (base_iostream)) {
411 		socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (base_iostream));
412 		g_socket_shutdown (socket, FALSE, TRUE, &error);
413 		if (error != NULL) {
414 			g_debug ("error shutting down io stream: %s", error->message);
415 			g_error_free (error);
416 		}
417 	}
418 
419 	g_object_notify (G_OBJECT (self), "state");
420 }
421 
422 static gboolean
on_timeout_close_io(gpointer user_data)423 on_timeout_close_io (gpointer user_data)
424 {
425 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
426 	SoupWebsocketConnectionPrivate *pv = self->pv;
427 
428 	pv->close_timeout = 0;
429 
430 	g_debug ("peer did not close io when expected");
431 	close_io_stream (self);
432 
433 	return FALSE;
434 }
435 
436 static void
close_io_after_timeout(SoupWebsocketConnection * self)437 close_io_after_timeout (SoupWebsocketConnection *self)
438 {
439 	SoupWebsocketConnectionPrivate *pv = self->pv;
440 	const int timeout = 5;
441 
442 	if (pv->close_timeout)
443 		return;
444 
445 	g_debug ("waiting %d seconds for peer to close io", timeout);
446 	pv->close_timeout = g_timeout_source_new_seconds (timeout);
447 	g_source_set_callback (pv->close_timeout, on_timeout_close_io, self, NULL);
448 	g_source_attach (pv->close_timeout, pv->main_context);
449 }
450 
451 static void
xor_with_mask(const guint8 * mask,guint8 * data,gsize len)452 xor_with_mask (const guint8 *mask,
453 	       guint8 *data,
454 	       gsize len)
455 {
456 	gsize n;
457 
458 	/* Do the masking */
459 	for (n = 0; n < len; n++)
460 		data[n] ^= mask[n & 3];
461 }
462 
463 static void
send_message(SoupWebsocketConnection * self,SoupWebsocketQueueFlags flags,guint8 opcode,const guint8 * data,gsize length)464 send_message (SoupWebsocketConnection *self,
465 	      SoupWebsocketQueueFlags flags,
466 	      guint8 opcode,
467 	      const guint8 *data,
468 	      gsize length)
469 {
470 	gsize buffered_amount;
471 	GByteArray *bytes;
472 	gsize frame_len;
473 	guint8 *outer;
474 	guint8 mask_offset;
475 	GBytes *filtered_bytes;
476 	GList *l;
477 	GError *error = NULL;
478 
479 	if (!(soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN)) {
480 		g_debug ("Ignoring message since the connection is closed or is closing");
481 		return;
482 	}
483 
484 	bytes = g_byte_array_sized_new (14 + length);
485 	outer = bytes->data;
486 	outer[0] = 0x80 | opcode;
487 
488 	filtered_bytes = g_bytes_new_static (data, length);
489 	for (l = self->pv->extensions; l != NULL; l = g_list_next (l)) {
490 		SoupWebsocketExtension *extension;
491 
492 		extension = (SoupWebsocketExtension *)l->data;
493 		filtered_bytes = soup_websocket_extension_process_outgoing_message (extension, outer, filtered_bytes, &error);
494 		if (error) {
495 			g_byte_array_free (bytes, TRUE);
496 			emit_error_and_close (self, error, FALSE);
497 			return;
498 		}
499 	}
500 
501 	data = g_bytes_get_data (filtered_bytes, &length);
502 	buffered_amount = length;
503 
504 	/* If control message, check payload size */
505 	if (opcode & 0x08) {
506 		if (length > 125) {
507 			g_warning ("WebSocket control message payload exceeds size limit");
508 			protocol_error_and_close (self);
509 			g_byte_array_free (bytes, TRUE);
510 			g_bytes_unref (filtered_bytes);
511 			return;
512 		}
513 
514 		buffered_amount = 0;
515 	}
516 
517 	if (length < 126) {
518 		outer[1] = (0xFF & length); /* mask | 7-bit-len */
519 		bytes->len = 2;
520 	} else if (length < 65536) {
521 		outer[1] = 126; /* mask | 16-bit-len */
522 		outer[2] = (length >> 8) & 0xFF;
523 		outer[3] = (length >> 0) & 0xFF;
524 		bytes->len = 4;
525 	} else {
526 		outer[1] = 127; /* mask | 64-bit-len */
527 #if GLIB_SIZEOF_SIZE_T > 4
528 		outer[2] = (length >> 56) & 0xFF;
529 		outer[3] = (length >> 48) & 0xFF;
530 		outer[4] = (length >> 40) & 0xFF;
531 		outer[5] = (length >> 32) & 0xFF;
532 #else
533 		outer[2] = outer[3] = outer[4] = outer[5] = 0;
534 #endif
535 		outer[6] = (length >> 24) & 0xFF;
536 		outer[7] = (length >> 16) & 0xFF;
537 		outer[8] = (length >> 8) & 0xFF;
538 		outer[9] = (length >> 0) & 0xFF;
539 		bytes->len = 10;
540 	}
541 
542 	/* The server side doesn't need to mask, so we don't. There's
543 	 * probably a client somewhere that's not expecting it.
544 	 */
545 	if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_CLIENT) {
546 		guint32 rnd = g_random_int ();
547 		outer[1] |= 0x80;
548 		mask_offset = bytes->len;
549 		memcpy (outer + mask_offset, &rnd, sizeof (rnd));
550 		bytes->len += MASK_LENGTH;
551 	}
552 
553 	g_byte_array_append (bytes, data, length);
554 
555 	if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_CLIENT)
556 		xor_with_mask (bytes->data + mask_offset, bytes->data + mask_offset + MASK_LENGTH, length);
557 
558 	frame_len = bytes->len;
559 	queue_frame (self, flags, g_byte_array_free (bytes, FALSE),
560 		     frame_len, buffered_amount);
561 	g_bytes_unref (filtered_bytes);
562 	g_debug ("queued %d frame of len %u", (int)opcode, (guint)frame_len);
563 }
564 
565 static void
send_close(SoupWebsocketConnection * self,SoupWebsocketQueueFlags flags,gushort code,const char * reason)566 send_close (SoupWebsocketConnection *self,
567 	    SoupWebsocketQueueFlags flags,
568 	    gushort code,
569 	    const char *reason)
570 {
571 	/* Note that send_message truncates as expected */
572 	char buffer[128];
573 	gsize len = 0;
574 
575 	if (code != 0) {
576 		buffer[len++] = code >> 8;
577 		buffer[len++] = code & 0xFF;
578 		if (reason)
579 			len += g_strlcpy (buffer + len, reason, sizeof (buffer) - len);
580 	}
581 
582 	send_message (self, flags, 0x08, (guint8 *)buffer, len);
583 	self->pv->close_sent = TRUE;
584 
585 	keepalive_stop_timeout (self);
586 }
587 
588 static void
emit_error_and_close(SoupWebsocketConnection * self,GError * error,gboolean prejudice)589 emit_error_and_close (SoupWebsocketConnection *self,
590 		      GError *error,
591 		      gboolean prejudice)
592 {
593 	gboolean ignore = FALSE;
594 	gushort code;
595 
596 	if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
597 		g_error_free (error);
598 		return;
599 	}
600 
601 	if (error && error->domain == SOUP_WEBSOCKET_ERROR)
602 		code = error->code;
603 	else
604 		code = SOUP_WEBSOCKET_CLOSE_GOING_AWAY;
605 
606 	self->pv->dirty_close = TRUE;
607 	g_signal_emit (self, signals[ERROR], 0, error);
608 	g_error_free (error);
609 
610 	/* If already closing, just ignore this stuff */
611 	switch (soup_websocket_connection_get_state (self)) {
612 	case SOUP_WEBSOCKET_STATE_CLOSED:
613 		ignore = TRUE;
614 		break;
615 	case SOUP_WEBSOCKET_STATE_CLOSING:
616 		ignore = !prejudice;
617 		break;
618 	default:
619 		break;
620 	}
621 
622 	if (ignore) {
623 		g_debug ("already closing/closed, ignoring error");
624 	} else if (prejudice) {
625 		g_debug ("forcing close due to error");
626 		close_io_stream (self);
627 	} else {
628 		g_debug ("requesting close due to error");
629 		send_close (self, SOUP_WEBSOCKET_QUEUE_URGENT | SOUP_WEBSOCKET_QUEUE_LAST, code, NULL);
630 	}
631 }
632 
633 static void
protocol_error_and_close_full(SoupWebsocketConnection * self,gboolean prejudice)634 protocol_error_and_close_full (SoupWebsocketConnection *self,
635                                gboolean prejudice)
636 {
637 	GError *error;
638 
639 	error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
640 				     SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR,
641 				     self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
642 				     "Received invalid WebSocket response from the client" :
643 				     "Received invalid WebSocket response from the server");
644 	emit_error_and_close (self, error, prejudice);
645 }
646 
647 static void
protocol_error_and_close(SoupWebsocketConnection * self)648 protocol_error_and_close (SoupWebsocketConnection *self)
649 {
650 	protocol_error_and_close_full (self, FALSE);
651 }
652 
653 static void
bad_data_error_and_close(SoupWebsocketConnection * self)654 bad_data_error_and_close (SoupWebsocketConnection *self)
655 {
656 	GError *error;
657 
658 	error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
659 				     SOUP_WEBSOCKET_CLOSE_BAD_DATA,
660 				     self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
661 				     "Received invalid WebSocket data from the client" :
662 				     "Received invalid WebSocket data from the server");
663 	emit_error_and_close (self, error, FALSE);
664 }
665 
666 static void
too_big_error_and_close(SoupWebsocketConnection * self,guint64 payload_len)667 too_big_error_and_close (SoupWebsocketConnection *self,
668                          guint64 payload_len)
669 {
670 	GError *error;
671 
672 	error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
673 				     SOUP_WEBSOCKET_CLOSE_TOO_BIG,
674 				     self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
675 				     "Received extremely large WebSocket data from the client" :
676 				     "Received extremely large WebSocket data from the server");
677 	g_debug ("%s is trying to frame of size %" G_GUINT64_FORMAT " or greater, but max supported size is %" G_GUINT64_FORMAT,
678 		 self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ? "server" : "client",
679 	         payload_len, self->pv->max_incoming_payload_size);
680 	emit_error_and_close (self, error, TRUE);
681 }
682 
683 static void
close_connection(SoupWebsocketConnection * self,gushort code,const char * data)684 close_connection (SoupWebsocketConnection *self,
685                   gushort                  code,
686                   const char              *data)
687 {
688 	SoupWebsocketQueueFlags flags;
689 	SoupWebsocketConnectionPrivate *pv;
690 
691 	pv = self->pv;
692 
693 	if (pv->close_sent) {
694 		g_debug ("close code already sent");
695 		return;
696 	}
697 
698 	/* Validate the closing code received by the peer */
699 	switch (code) {
700 	case SOUP_WEBSOCKET_CLOSE_NORMAL:
701 	case SOUP_WEBSOCKET_CLOSE_GOING_AWAY:
702 	case SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR:
703 	case SOUP_WEBSOCKET_CLOSE_UNSUPPORTED_DATA:
704 	case SOUP_WEBSOCKET_CLOSE_BAD_DATA:
705 	case SOUP_WEBSOCKET_CLOSE_POLICY_VIOLATION:
706 	case SOUP_WEBSOCKET_CLOSE_TOO_BIG:
707 		break;
708 	case SOUP_WEBSOCKET_CLOSE_NO_EXTENSION:
709 		if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER) {
710 			g_debug ("Wrong closing code %d received for a server connection",
711 			         code);
712 		}
713 		break;
714 	case SOUP_WEBSOCKET_CLOSE_SERVER_ERROR:
715 		if (pv->connection_type != SOUP_WEBSOCKET_CONNECTION_SERVER) {
716 			g_debug ("Wrong closing code %d received for a non server connection",
717 			         code);
718 		}
719 		break;
720 	case SOUP_WEBSOCKET_CLOSE_NO_STATUS:
721 		/* This is special case to send a close message with no body */
722 		code = 0;
723 		break;
724 	default:
725 		if (code < 3000) {
726 			g_debug ("Wrong closing code %d received", code);
727 			protocol_error_and_close (self);
728 			return;
729 		}
730 	}
731 
732 	g_signal_emit (self, signals[CLOSING], 0);
733 
734 	if (pv->close_received)
735 		g_debug ("responding to close request");
736 
737 	flags = 0;
738 	if (pv->close_received)
739 		flags |= SOUP_WEBSOCKET_QUEUE_LAST;
740 	send_close (self, flags, code, data);
741 	close_io_after_timeout (self);
742 }
743 
744 static void
receive_close(SoupWebsocketConnection * self,const guint8 * data,gsize len)745 receive_close (SoupWebsocketConnection *self,
746 	       const guint8 *data,
747 	       gsize len)
748 {
749 	SoupWebsocketConnectionPrivate *pv = self->pv;
750 
751 	pv->peer_close_code = 0;
752 	g_free (pv->peer_close_data);
753 	pv->peer_close_data = NULL;
754 	pv->close_received = TRUE;
755 
756 	switch (len) {
757 	case 0:
758 		/* Send a clean close when having an empty payload */
759 		pv->peer_close_code = SOUP_WEBSOCKET_CLOSE_NO_STATUS;
760 		close_connection (self, 1000, NULL);
761 		return;
762 	case 1:
763 		/* Send a protocol error since the close code is incomplete */
764 		protocol_error_and_close (self);
765 		return;
766 	default:
767 		/* Store the code/data payload */
768 		pv->peer_close_code = (guint16)data[0] << 8 | data[1];
769 		break;
770 	}
771 
772 	if (len > 2) {
773 		data += 2;
774 		len -= 2;
775 
776 		if (!utf8_validate ((const char *)data, len)) {
777 			g_debug ("received non-UTF8 close data: %d '%.*s' %d", (int)len, (int)len, (char *)data, (int)data[0]);
778 			protocol_error_and_close (self);
779 			return;
780 		}
781 
782 		pv->peer_close_data = g_strndup ((char *)data, len);
783 	}
784 
785 	/* Once we receive close response on server, close immediately */
786 	if (pv->close_sent) {
787 		shutdown_wr_io_stream (self);
788 		if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER)
789 			close_io_stream (self);
790 	} else {
791 		close_connection (self, pv->peer_close_code, pv->peer_close_data);
792 	}
793 }
794 
795 static void
receive_ping(SoupWebsocketConnection * self,const guint8 * data,gsize len)796 receive_ping (SoupWebsocketConnection *self,
797                       const guint8 *data,
798                       gsize len)
799 {
800 	/* Send back a pong with same data */
801 	g_debug ("received ping, responding");
802 	send_message (self, SOUP_WEBSOCKET_QUEUE_URGENT, 0x0A, data, len);
803 }
804 
805 static void
receive_pong(SoupWebsocketConnection * self,const guint8 * data,gsize len)806 receive_pong (SoupWebsocketConnection *self,
807                       const guint8 *data,
808                       gsize len)
809 {
810 	GByteArray *bytes;
811 
812 	g_debug ("received pong message");
813 
814 	bytes = g_byte_array_sized_new (len + 1);
815 	g_byte_array_append (bytes, data, len);
816 	/* Always null terminate, as a convenience */
817 	g_byte_array_append (bytes, (guchar *)"\0", 1);
818 	/* But don't include the null terminator in the byte count */
819 	bytes->len--;
820 
821 	g_signal_emit (self, signals[PONG], 0, bytes);
822 	g_byte_array_unref (bytes);
823 
824 }
825 
826 static void
process_contents(SoupWebsocketConnection * self,gboolean control,gboolean fin,guint8 opcode,GBytes * payload_data)827 process_contents (SoupWebsocketConnection *self,
828 		  gboolean control,
829 		  gboolean fin,
830 		  guint8 opcode,
831 		  GBytes *payload_data)
832 {
833 	SoupWebsocketConnectionPrivate *pv = self->pv;
834 	GBytes *message;
835 	gconstpointer payload;
836 	gsize payload_len;
837 
838 	payload = g_bytes_get_data (payload_data, &payload_len);
839 
840 	if (pv->close_sent && pv->close_received)
841 		return;
842 
843 	if (control) {
844 		/* Control frames must never be fragmented */
845 		if (!fin) {
846 			g_debug ("received fragmented control frame");
847 			protocol_error_and_close (self);
848 			return;
849 		}
850 
851 		g_debug ("received control frame %d with %d payload", (int)opcode, (int)payload_len);
852 
853 		switch (opcode) {
854 		case 0x08:
855 			receive_close (self, payload, payload_len);
856 			break;
857 		case 0x09:
858 			receive_ping (self, payload, payload_len);
859 			break;
860 		case 0x0A:
861 			receive_pong (self, payload, payload_len);
862 			break;
863 		default:
864 			g_debug ("received unsupported control frame: %d", (int)opcode);
865 			protocol_error_and_close (self);
866 			return;
867 		}
868 	} else if (pv->close_received) {
869 		g_debug ("received message after close was received");
870 	} else {
871 		/* A message frame */
872 
873 		if (!fin && opcode) {
874 			/* Initial fragment of a message */
875 			if (pv->message_data) {
876 				g_debug ("received out of order initial message fragment");
877 				protocol_error_and_close (self);
878 				return;
879 			}
880 			g_debug ("received initial fragment frame %d with %d payload", (int)opcode, (int)payload_len);
881 		} else if (!fin && !opcode) {
882 			/* Middle fragment of a message */
883 			if (!pv->message_data) {
884 				g_debug ("received out of order middle message fragment");
885 				protocol_error_and_close (self);
886 				return;
887 			}
888 			g_debug ("received middle fragment frame with %d payload", (int)payload_len);
889 		} else if (fin && !opcode) {
890 			/* Last fragment of a message */
891 			if (!pv->message_data) {
892 				g_debug ("received out of order ending message fragment");
893 				protocol_error_and_close (self);
894 				return;
895 			}
896 			g_debug ("received last fragment frame with %d payload", (int)payload_len);
897 		} else {
898 			/* An unfragmented message */
899 			g_assert (opcode != 0);
900 			if (pv->message_data) {
901 				g_debug ("received unfragmented message when fragment was expected");
902 				protocol_error_and_close (self);
903 				return;
904 			}
905 			g_debug ("received frame %d with %d payload", (int)opcode, (int)payload_len);
906 		}
907 
908 		if (opcode) {
909 			pv->message_opcode = opcode;
910 			pv->message_data = g_byte_array_sized_new (payload_len + 1);
911 		}
912 
913 		switch (pv->message_opcode) {
914 		case 0x01:
915 		case 0x02:
916 			g_byte_array_append (pv->message_data, payload, payload_len);
917 			break;
918 		default:
919 			g_debug ("received unknown data frame: %d", (int)opcode);
920 			protocol_error_and_close (self);
921 			return;
922 		}
923 
924 		/* Actually deliver the message? */
925 		if (fin) {
926 			if (pv->message_opcode == 0x01 &&
927 			    !utf8_validate((const char *)pv->message_data->data,
928 					   pv->message_data->len)) {
929 
930 				g_debug ("received invalid non-UTF8 text data");
931 
932 				/* Discard the entire message */
933 				g_byte_array_unref (pv->message_data);
934 				pv->message_data = NULL;
935 				pv->message_opcode = 0;
936 
937 				bad_data_error_and_close (self);
938 				return;
939 			}
940 
941 			/* Always null terminate, as a convenience */
942 			g_byte_array_append (pv->message_data, (guchar *)"\0", 1);
943 
944 			/* But don't include the null terminator in the byte count */
945 			pv->message_data->len--;
946 
947 			opcode = pv->message_opcode;
948 			message = g_byte_array_free_to_bytes (pv->message_data);
949 			pv->message_data = NULL;
950 			pv->message_opcode = 0;
951 			g_debug ("message: delivering %d with %d length",
952 				 (int)opcode, (int)g_bytes_get_size (message));
953 			g_signal_emit (self, signals[MESSAGE], 0, (int)opcode, message);
954 			g_bytes_unref (message);
955 		}
956 	}
957 }
958 
959 static gboolean
process_frame(SoupWebsocketConnection * self)960 process_frame (SoupWebsocketConnection *self)
961 {
962 	guint8 *header;
963 	guint8 *payload;
964 	guint64 payload_len;
965 	guint8 *mask;
966 	gboolean fin;
967 	gboolean control;
968 	gboolean masked;
969 	guint8 opcode;
970 	gsize len;
971 	gsize at;
972 	GBytes *filtered_bytes;
973 	GList *l;
974 	GError *error = NULL;
975 
976 	len = self->pv->incoming->len;
977 	if (len < 2)
978 		return FALSE; /* need more data */
979 
980 	header = self->pv->incoming->data;
981 	fin = ((header[0] & 0x80) != 0);
982 	control = header[0] & 0x08;
983 	opcode = header[0] & 0x0f;
984 	masked = ((header[1] & 0x80) != 0);
985 
986 	if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_CLIENT && masked) {
987 		/* A server MUST NOT mask any frames that it sends to the client.
988 		 * A client MUST close a connection if it detects a masked frame.
989 		 */
990 		g_debug ("A server must not mask any frames that it sends to the client.");
991 		protocol_error_and_close (self);
992 		return FALSE;
993 	}
994 
995 	if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER && !masked) {
996 		/* The server MUST close the connection upon receiving a frame
997 		 * that is not masked.
998 		 */
999 		g_debug ("The client should always mask frames");
1000 		protocol_error_and_close (self);
1001                 return FALSE;
1002         }
1003 
1004 	switch (header[1] & 0x7f) {
1005 	case 126:
1006 		/* If 126, the following 2 bytes interpreted as a 16-bit
1007 		 * unsigned integer are the payload length.
1008 		 */
1009 		at = 4;
1010 		if (len < at)
1011 			return FALSE; /* need more data */
1012 		payload_len = (((guint16)header[2] << 8) |
1013 			       ((guint16)header[3] << 0));
1014 
1015 		/* The minimal number of bytes MUST be used to encode the length. */
1016 		if (payload_len <= 125) {
1017 			protocol_error_and_close (self);
1018 			return FALSE;
1019 		}
1020 		break;
1021 	case 127:
1022 		/* If 127, the following 8 bytes interpreted as a 64-bit
1023 		 * unsigned integer (the most significant bit MUST be 0)
1024 		 * are the payload length.
1025 		 */
1026 		at = 10;
1027 		if (len < at)
1028 			return FALSE; /* need more data */
1029 		payload_len = (((guint64)header[2] << 56) |
1030 			       ((guint64)header[3] << 48) |
1031 			       ((guint64)header[4] << 40) |
1032 			       ((guint64)header[5] << 32) |
1033 			       ((guint64)header[6] << 24) |
1034 			       ((guint64)header[7] << 16) |
1035 			       ((guint64)header[8] << 8) |
1036 			       ((guint64)header[9] << 0));
1037 
1038 		/* The minimal number of bytes MUST be used to encode the length. */
1039 		if (payload_len <= G_MAXUINT16) {
1040 			protocol_error_and_close (self);
1041 			return FALSE;
1042 		}
1043 		break;
1044 	default:
1045 		payload_len = header[1] & 0x7f;
1046 		at = 2;
1047 		break;
1048 	}
1049 
1050 	/* Safety valve */
1051 	if (self->pv->max_incoming_payload_size > 0 &&
1052 	    payload_len >= self->pv->max_incoming_payload_size) {
1053 		too_big_error_and_close (self, payload_len);
1054 		return FALSE;
1055 	}
1056 
1057 	if (len < at + payload_len)
1058 		return FALSE; /* need more data */
1059 
1060 	payload = header + at;
1061 
1062 	if (masked) {
1063 		mask = header + at;
1064 		payload += 4;
1065 		at += 4;
1066 
1067 		if (len < at + payload_len)
1068 			return FALSE; /* need more data */
1069 
1070 		xor_with_mask (mask, payload, payload_len);
1071 	}
1072 
1073 	filtered_bytes = g_bytes_new_static (payload, payload_len);
1074 	for (l = self->pv->extensions; l != NULL; l = g_list_next (l)) {
1075 		SoupWebsocketExtension *extension;
1076 
1077 		extension = (SoupWebsocketExtension *)l->data;
1078 		filtered_bytes = soup_websocket_extension_process_incoming_message (extension, self->pv->incoming->data, filtered_bytes, &error);
1079 		if (error) {
1080 			emit_error_and_close (self, error, FALSE);
1081 			return FALSE;
1082 		}
1083 	}
1084 
1085 	/* After being processed by extensions reserved bits must be 0 */
1086 	if (header[0] & 0x70) {
1087 		protocol_error_and_close (self);
1088 		g_bytes_unref (filtered_bytes);
1089 
1090 		return FALSE;
1091 	}
1092 
1093 	/* Note that now that we've unmasked, we've modified the buffer, we can
1094 	 * only return below via discarding or processing the message
1095 	 */
1096 	process_contents (self, control, fin, opcode, filtered_bytes);
1097 	g_bytes_unref (filtered_bytes);
1098 
1099 	/* Move past the parsed frame */
1100 	g_byte_array_remove_range (self->pv->incoming, 0, at + payload_len);
1101 
1102 	return TRUE;
1103 }
1104 
1105 static void
process_incoming(SoupWebsocketConnection * self)1106 process_incoming (SoupWebsocketConnection *self)
1107 {
1108 	while (process_frame (self))
1109 		;
1110 }
1111 
1112 static void
soup_websocket_connection_read(SoupWebsocketConnection * self)1113 soup_websocket_connection_read (SoupWebsocketConnection *self)
1114 {
1115 	SoupWebsocketConnectionPrivate *pv = self->pv;
1116 	GError *error = NULL;
1117 	gboolean end = FALSE;
1118 	gssize count;
1119 	gsize len;
1120 
1121 	soup_websocket_connection_stop_input_source (self);
1122 
1123 	do {
1124 		len = pv->incoming->len;
1125 		g_byte_array_set_size (pv->incoming, len + READ_BUFFER_SIZE);
1126 
1127 		count = g_pollable_input_stream_read_nonblocking (pv->input,
1128 								  pv->incoming->data + len,
1129 								  READ_BUFFER_SIZE, NULL, &error);
1130 		if (count < 0) {
1131 			if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1132 				g_error_free (error);
1133 				count = 0;
1134 			} else {
1135 				emit_error_and_close (self, error, TRUE);
1136 				return;
1137 			}
1138 		} else if (count == 0) {
1139 			end = TRUE;
1140 		}
1141 
1142 		pv->incoming->len = len + count;
1143 	} while (count > 0);
1144 
1145 	process_incoming (self);
1146 
1147 	if (end) {
1148 		if (!pv->close_sent || !pv->close_received) {
1149 			pv->dirty_close = TRUE;
1150 			g_debug ("connection unexpectedly closed by peer");
1151 		} else {
1152 			g_debug ("peer has closed socket");
1153 		}
1154 
1155 		close_io_stream (self);
1156 		return;
1157 	}
1158 
1159 	if (!pv->io_closing)
1160 		soup_websocket_connection_start_input_source (self);
1161 }
1162 
1163 static gboolean
on_web_socket_input(GObject * pollable_stream,gpointer user_data)1164 on_web_socket_input (GObject *pollable_stream,
1165 		     gpointer user_data)
1166 {
1167 	soup_websocket_connection_read (SOUP_WEBSOCKET_CONNECTION (user_data));
1168 
1169 	return G_SOURCE_REMOVE;
1170 }
1171 
1172 static void
soup_websocket_connection_write(SoupWebsocketConnection * self)1173 soup_websocket_connection_write (SoupWebsocketConnection *self)
1174 {
1175 	SoupWebsocketConnectionPrivate *pv = self->pv;
1176 	const guint8 *data;
1177 	GError *error = NULL;
1178 	Frame *frame;
1179 	gssize count;
1180 	gsize len;
1181 
1182 	soup_websocket_connection_stop_output_source (self);
1183 
1184 	if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
1185 		g_debug ("Ignoring message since the connection is closed");
1186 		return;
1187 	}
1188 
1189 	frame = g_queue_peek_head (&pv->outgoing);
1190 
1191 	/* No more frames to send */
1192 	if (frame == NULL)
1193 		return;
1194 
1195 	data = g_bytes_get_data (frame->data, &len);
1196 	g_assert (len > 0);
1197 	g_assert (len > frame->sent);
1198 
1199 	count = g_pollable_output_stream_write_nonblocking (pv->output,
1200 							    data + frame->sent,
1201 							    len - frame->sent,
1202 							    NULL, &error);
1203 
1204 	if (count < 0) {
1205 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
1206 			g_clear_error (&error);
1207 			count = 0;
1208 
1209 			g_debug ("failed to send frame because it would block, marking as pending");
1210 			frame->pending = TRUE;
1211 		} else {
1212 			emit_error_and_close (self, error, TRUE);
1213 			return;
1214 		}
1215 	}
1216 
1217 	frame->sent += count;
1218 	if (frame->sent >= len) {
1219 		g_debug ("sent frame");
1220 		g_queue_pop_head (&pv->outgoing);
1221 
1222 		if (frame->flags & SOUP_WEBSOCKET_QUEUE_LAST) {
1223 			if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER) {
1224 				close_io_stream (self);
1225 			} else {
1226 				shutdown_wr_io_stream (self);
1227 				close_io_after_timeout (self);
1228 			}
1229 		}
1230 		frame_free (frame);
1231 
1232 		if (g_queue_is_empty (&pv->outgoing))
1233 			return;
1234 	}
1235 
1236 	soup_websocket_connection_start_output_source (self);
1237 }
1238 
1239 static gboolean
on_web_socket_output(GObject * pollable_stream,gpointer user_data)1240 on_web_socket_output (GObject *pollable_stream,
1241 		      gpointer user_data)
1242 {
1243 	soup_websocket_connection_write (SOUP_WEBSOCKET_CONNECTION (user_data));
1244 
1245 	return G_SOURCE_REMOVE;
1246 }
1247 
1248 static void
queue_frame(SoupWebsocketConnection * self,SoupWebsocketQueueFlags flags,gpointer data,gsize len,gsize amount)1249 queue_frame (SoupWebsocketConnection *self,
1250 	     SoupWebsocketQueueFlags flags,
1251 	     gpointer data,
1252 	     gsize len,
1253 	     gsize amount)
1254 {
1255 	SoupWebsocketConnectionPrivate *pv = self->pv;
1256 	Frame *frame;
1257 
1258 	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
1259 	g_return_if_fail (pv->close_sent == FALSE);
1260 	g_return_if_fail (data != NULL);
1261 	g_return_if_fail (len > 0);
1262 
1263 	frame = g_slice_new0 (Frame);
1264 	frame->data = g_bytes_new_take (data, len);
1265 	frame->amount = amount;
1266 	frame->flags = flags;
1267 
1268 	/* If urgent put at front of queue */
1269 	if (flags & SOUP_WEBSOCKET_QUEUE_URGENT) {
1270 		GList *l;
1271 
1272 		/* Find out the first frame that is not urgent or partially sent or pending */
1273 		for (l = g_queue_peek_head_link (&pv->outgoing); l != NULL; l = l->next) {
1274 			Frame *prev = l->data;
1275 
1276 			if (!(prev->flags & SOUP_WEBSOCKET_QUEUE_URGENT) &&
1277 			    prev->sent == 0 && !prev->pending)
1278 				break;
1279 		}
1280 
1281 		g_queue_insert_before (&pv->outgoing, l, frame);
1282 	} else {
1283 		g_queue_push_tail (&pv->outgoing, frame);
1284 	}
1285 
1286 	soup_websocket_connection_write (self);
1287 }
1288 
1289 static void
soup_websocket_connection_constructed(GObject * object)1290 soup_websocket_connection_constructed (GObject *object)
1291 {
1292 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
1293 	SoupWebsocketConnectionPrivate *pv = self->pv;
1294 	GInputStream *is;
1295 	GOutputStream *os;
1296 
1297 	G_OBJECT_CLASS (soup_websocket_connection_parent_class)->constructed (object);
1298 
1299 	g_return_if_fail (pv->io_stream != NULL);
1300 
1301 	is = g_io_stream_get_input_stream (pv->io_stream);
1302 	g_return_if_fail (G_IS_POLLABLE_INPUT_STREAM (is));
1303 	pv->input = G_POLLABLE_INPUT_STREAM (is);
1304 	g_return_if_fail (g_pollable_input_stream_can_poll (pv->input));
1305 
1306 	os = g_io_stream_get_output_stream (pv->io_stream);
1307 	g_return_if_fail (G_IS_POLLABLE_OUTPUT_STREAM (os));
1308 	pv->output = G_POLLABLE_OUTPUT_STREAM (os);
1309 	g_return_if_fail (g_pollable_output_stream_can_poll (pv->output));
1310 
1311 	soup_websocket_connection_start_input_source (self);
1312 }
1313 
1314 static void
soup_websocket_connection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1315 soup_websocket_connection_get_property (GObject *object,
1316 					guint prop_id,
1317 					GValue *value,
1318 					GParamSpec *pspec)
1319 {
1320 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
1321 	SoupWebsocketConnectionPrivate *pv = self->pv;
1322 
1323 	switch (prop_id) {
1324 	case PROP_IO_STREAM:
1325 		g_value_set_object (value, soup_websocket_connection_get_io_stream (self));
1326 		break;
1327 
1328 	case PROP_CONNECTION_TYPE:
1329 		g_value_set_enum (value, soup_websocket_connection_get_connection_type (self));
1330 		break;
1331 
1332 	case PROP_URI:
1333 		g_value_set_boxed (value, soup_websocket_connection_get_uri (self));
1334 		break;
1335 
1336 	case PROP_ORIGIN:
1337 		g_value_set_string (value, soup_websocket_connection_get_origin (self));
1338 		break;
1339 
1340 	case PROP_PROTOCOL:
1341 		g_value_set_string (value, soup_websocket_connection_get_protocol (self));
1342 		break;
1343 
1344 	case PROP_STATE:
1345 		g_value_set_enum (value, soup_websocket_connection_get_state (self));
1346 		break;
1347 
1348 	case PROP_MAX_INCOMING_PAYLOAD_SIZE:
1349 		g_value_set_uint64 (value, pv->max_incoming_payload_size);
1350 		break;
1351 
1352 	case PROP_KEEPALIVE_INTERVAL:
1353 		g_value_set_uint (value, pv->keepalive_interval);
1354 		break;
1355 
1356 	case PROP_EXTENSIONS:
1357 		g_value_set_pointer (value, pv->extensions);
1358 		break;
1359 
1360 	default:
1361 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1362 		break;
1363 	}
1364 }
1365 
1366 static void
soup_websocket_connection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1367 soup_websocket_connection_set_property (GObject *object,
1368 					guint prop_id,
1369 					const GValue *value,
1370 					GParamSpec *pspec)
1371 {
1372 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
1373 	SoupWebsocketConnectionPrivate *pv = self->pv;
1374 
1375 	switch (prop_id) {
1376 	case PROP_IO_STREAM:
1377 		g_return_if_fail (pv->io_stream == NULL);
1378 		pv->io_stream = g_value_dup_object (value);
1379 		break;
1380 
1381 	case PROP_CONNECTION_TYPE:
1382 		pv->connection_type = g_value_get_enum (value);
1383 		break;
1384 
1385 	case PROP_URI:
1386 		g_return_if_fail (pv->uri == NULL);
1387 		pv->uri = g_value_dup_boxed (value);
1388 		break;
1389 
1390 	case PROP_ORIGIN:
1391 		g_return_if_fail (pv->origin == NULL);
1392 		pv->origin = g_value_dup_string (value);
1393 		break;
1394 
1395 	case PROP_PROTOCOL:
1396 		g_return_if_fail (pv->protocol == NULL);
1397 		pv->protocol = g_value_dup_string (value);
1398 		break;
1399 
1400 	case PROP_MAX_INCOMING_PAYLOAD_SIZE:
1401 		pv->max_incoming_payload_size = g_value_get_uint64 (value);
1402 		break;
1403 
1404 	case PROP_KEEPALIVE_INTERVAL:
1405 		soup_websocket_connection_set_keepalive_interval (self,
1406 		                                                  g_value_get_uint (value));
1407 		break;
1408 
1409 	case PROP_EXTENSIONS:
1410 		pv->extensions = g_value_get_pointer (value);
1411 		break;
1412 
1413 	default:
1414 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1415 		break;
1416 	}
1417 }
1418 
1419 static void
soup_websocket_connection_dispose(GObject * object)1420 soup_websocket_connection_dispose (GObject *object)
1421 {
1422 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
1423 
1424 	self->pv->dirty_close = TRUE;
1425 	close_io_stream (self);
1426 
1427 	G_OBJECT_CLASS (soup_websocket_connection_parent_class)->dispose (object);
1428 }
1429 
1430 static void
soup_websocket_connection_finalize(GObject * object)1431 soup_websocket_connection_finalize (GObject *object)
1432 {
1433 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
1434 	SoupWebsocketConnectionPrivate *pv = self->pv;
1435 
1436 	g_free (pv->peer_close_data);
1437 
1438 	g_main_context_unref (pv->main_context);
1439 
1440 	if (pv->incoming)
1441 		g_byte_array_free (pv->incoming, TRUE);
1442 	while (!g_queue_is_empty (&pv->outgoing))
1443 		frame_free (g_queue_pop_head (&pv->outgoing));
1444 
1445 	g_clear_object (&pv->io_stream);
1446 	g_assert (!pv->input_source);
1447 	g_assert (!pv->output_source);
1448 	g_assert (pv->io_closing);
1449 	g_assert (pv->io_closed);
1450 	g_assert (!pv->close_timeout);
1451 	g_assert (!pv->keepalive_timeout);
1452 
1453 	if (pv->message_data)
1454 		g_byte_array_free (pv->message_data, TRUE);
1455 
1456 	if (pv->uri)
1457 		soup_uri_free (pv->uri);
1458 	g_free (pv->origin);
1459 	g_free (pv->protocol);
1460 
1461 	g_list_free_full (pv->extensions, g_object_unref);
1462 
1463 	G_OBJECT_CLASS (soup_websocket_connection_parent_class)->finalize (object);
1464 }
1465 
1466 static void
soup_websocket_connection_class_init(SoupWebsocketConnectionClass * klass)1467 soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
1468 {
1469 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1470 
1471 	gobject_class->constructed = soup_websocket_connection_constructed;
1472 	gobject_class->get_property = soup_websocket_connection_get_property;
1473 	gobject_class->set_property = soup_websocket_connection_set_property;
1474 	gobject_class->dispose = soup_websocket_connection_dispose;
1475 	gobject_class->finalize = soup_websocket_connection_finalize;
1476 
1477 	/**
1478 	 * SoupWebsocketConnection:io-stream:
1479 	 *
1480 	 * The underlying IO stream the WebSocket is communicating
1481 	 * over.
1482 	 *
1483 	 * The input and output streams must be pollable streams.
1484 	 *
1485 	 * Since: 2.50
1486 	 */
1487 	g_object_class_install_property (gobject_class, PROP_IO_STREAM,
1488 					 g_param_spec_object ("io-stream",
1489 							      "I/O Stream",
1490 							      "Underlying I/O stream",
1491 							      G_TYPE_IO_STREAM,
1492 							      G_PARAM_READWRITE |
1493 							      G_PARAM_CONSTRUCT_ONLY |
1494 							      G_PARAM_STATIC_STRINGS));
1495 
1496 	/**
1497 	 * SoupWebsocketConnection:connection-type:
1498 	 *
1499 	 * The type of connection (client/server).
1500 	 *
1501 	 * Since: 2.50
1502 	 */
1503 	g_object_class_install_property (gobject_class, PROP_CONNECTION_TYPE,
1504 					 g_param_spec_enum ("connection-type",
1505 							    "Connection type",
1506 							    "Connection type (client/server)",
1507 							    SOUP_TYPE_WEBSOCKET_CONNECTION_TYPE,
1508 							    SOUP_WEBSOCKET_CONNECTION_UNKNOWN,
1509 							    G_PARAM_READWRITE |
1510 							    G_PARAM_CONSTRUCT_ONLY |
1511 							    G_PARAM_STATIC_STRINGS));
1512 
1513 	/**
1514 	 * SoupWebsocketConnection:uri:
1515 	 *
1516 	 * The URI of the WebSocket.
1517 	 *
1518 	 * For servers this represents the address of the WebSocket,
1519 	 * and for clients it is the address connected to.
1520 	 *
1521 	 * Since: 2.50
1522 	 */
1523 	g_object_class_install_property (gobject_class, PROP_URI,
1524 					 g_param_spec_boxed ("uri",
1525 							     "URI",
1526 							     "The WebSocket URI",
1527 							     SOUP_TYPE_URI,
1528 							     G_PARAM_READWRITE |
1529 							     G_PARAM_CONSTRUCT_ONLY |
1530 							     G_PARAM_STATIC_STRINGS));
1531 
1532 	/**
1533 	 * SoupWebsocketConnection:origin:
1534 	 *
1535 	 * The client's Origin.
1536 	 *
1537 	 * Since: 2.50
1538 	 */
1539 	g_object_class_install_property (gobject_class, PROP_ORIGIN,
1540 					 g_param_spec_string ("origin",
1541 							      "Origin",
1542 							      "The WebSocket origin",
1543 							      NULL,
1544 							      G_PARAM_READWRITE |
1545 							      G_PARAM_CONSTRUCT_ONLY |
1546 							      G_PARAM_STATIC_STRINGS));
1547 
1548 	/**
1549 	 * SoupWebsocketConnection:protocol:
1550 	 *
1551 	 * The chosen protocol, or %NULL if a protocol was not agreed
1552 	 * upon.
1553 	 *
1554 	 * Since: 2.50
1555 	 */
1556 	g_object_class_install_property (gobject_class, PROP_PROTOCOL,
1557 					 g_param_spec_string ("protocol",
1558 							      "Protocol",
1559 							      "The chosen WebSocket protocol",
1560 							      NULL,
1561 							      G_PARAM_READWRITE |
1562 							      G_PARAM_CONSTRUCT_ONLY |
1563 							      G_PARAM_STATIC_STRINGS));
1564 
1565 	/**
1566 	 * SoupWebsocketConnection:state:
1567 	 *
1568 	 * The current state of the WebSocket.
1569 	 *
1570 	 * Since: 2.50
1571 	 */
1572 	g_object_class_install_property (gobject_class, PROP_STATE,
1573 					 g_param_spec_enum ("state",
1574 							    "State",
1575 							    "State ",
1576 							    SOUP_TYPE_WEBSOCKET_STATE,
1577 							    SOUP_WEBSOCKET_STATE_OPEN,
1578 							    G_PARAM_READABLE |
1579 							    G_PARAM_STATIC_STRINGS));
1580 
1581 	/**
1582 	 * SoupWebsocketConnection:max-incoming-payload-size:
1583 	 *
1584 	 * The maximum payload size for incoming packets the protocol expects
1585 	 * or 0 to not limit it.
1586 	 *
1587 	 * Since: 2.56
1588 	 */
1589 	g_object_class_install_property (gobject_class, PROP_MAX_INCOMING_PAYLOAD_SIZE,
1590 					 g_param_spec_uint64 ("max-incoming-payload-size",
1591 							      "Max incoming payload size",
1592 							      "Max incoming payload size ",
1593 							      0,
1594 							      G_MAXUINT64,
1595 							      MAX_INCOMING_PAYLOAD_SIZE_DEFAULT,
1596 							      G_PARAM_READWRITE |
1597 							      G_PARAM_CONSTRUCT |
1598 							      G_PARAM_STATIC_STRINGS));
1599 
1600 	/**
1601 	 * SoupWebsocketConnection:keepalive-interval:
1602 	 *
1603 	 * Interval in seconds on when to send a ping message which will
1604 	 * serve as a keepalive message. If set to 0 the keepalive message is
1605 	 * disabled.
1606 	 *
1607 	 * Since: 2.58
1608 	 */
1609 	g_object_class_install_property (gobject_class, PROP_KEEPALIVE_INTERVAL,
1610 					 g_param_spec_uint ("keepalive-interval",
1611 					                    "Keepalive interval",
1612 					                    "Keepalive interval",
1613 					                    0,
1614 					                    G_MAXUINT,
1615 					                    0,
1616 					                    G_PARAM_READWRITE |
1617 					                    G_PARAM_CONSTRUCT |
1618 					                    G_PARAM_STATIC_STRINGS));
1619 
1620         /**
1621          * SoupWebsocketConnection:extensions:
1622          *
1623          * List of #SoupWebsocketExtension objects that are active in the connection.
1624          *
1625          * Since: 2.68
1626          */
1627         g_object_class_install_property (gobject_class, PROP_EXTENSIONS,
1628                                          g_param_spec_pointer ("extensions",
1629                                                                "Active extensions",
1630                                                                "The list of active extensions",
1631                                                                G_PARAM_READWRITE |
1632                                                                G_PARAM_CONSTRUCT_ONLY |
1633                                                                G_PARAM_STATIC_STRINGS));
1634 
1635 	/**
1636 	 * SoupWebsocketConnection::message:
1637 	 * @self: the WebSocket
1638 	 * @type: the type of message contents
1639 	 * @message: the message data
1640 	 *
1641 	 * Emitted when we receive a message from the peer.
1642 	 *
1643 	 * As a convenience, the @message data will always be
1644 	 * NUL-terminated, but the NUL byte will not be included in
1645 	 * the length count.
1646 	 *
1647 	 * Since: 2.50
1648 	 */
1649 	signals[MESSAGE] = g_signal_new ("message",
1650 					 SOUP_TYPE_WEBSOCKET_CONNECTION,
1651 					 G_SIGNAL_RUN_FIRST,
1652 					 G_STRUCT_OFFSET (SoupWebsocketConnectionClass, message),
1653 					 NULL, NULL, g_cclosure_marshal_generic,
1654 					 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_BYTES);
1655 
1656 	/**
1657 	 * SoupWebsocketConnection::error:
1658 	 * @self: the WebSocket
1659 	 * @error: the error that occured
1660 	 *
1661 	 * Emitted when an error occurred on the WebSocket. This may
1662 	 * be fired multiple times. Fatal errors will be followed by
1663 	 * the #SoupWebsocketConnection::closed signal being emitted.
1664 	 *
1665 	 * Since: 2.50
1666 	 */
1667 	signals[ERROR] = g_signal_new ("error",
1668 				       SOUP_TYPE_WEBSOCKET_CONNECTION,
1669 				       G_SIGNAL_RUN_FIRST,
1670 				       G_STRUCT_OFFSET (SoupWebsocketConnectionClass, error),
1671 				       NULL, NULL, g_cclosure_marshal_generic,
1672 				       G_TYPE_NONE, 1, G_TYPE_ERROR);
1673 
1674 	/**
1675 	 * SoupWebsocketConnection::closing:
1676 	 * @self: the WebSocket
1677 	 *
1678 	 * This signal will be emitted during an orderly close.
1679 	 *
1680 	 * Since: 2.50
1681 	 */
1682 	signals[CLOSING] = g_signal_new ("closing",
1683 					 SOUP_TYPE_WEBSOCKET_CONNECTION,
1684 					 G_SIGNAL_RUN_LAST,
1685 					 G_STRUCT_OFFSET (SoupWebsocketConnectionClass, closing),
1686 					 NULL, NULL, g_cclosure_marshal_generic,
1687 					 G_TYPE_NONE, 0);
1688 
1689 	/**
1690 	 * SoupWebsocketConnection::closed:
1691 	 * @self: the WebSocket
1692 	 *
1693 	 * Emitted when the connection has completely closed, either
1694 	 * due to an orderly close from the peer, one initiated via
1695 	 * soup_websocket_connection_close() or a fatal error
1696 	 * condition that caused a close.
1697 	 *
1698 	 * This signal will be emitted once.
1699 	 *
1700 	 * Since: 2.50
1701 	 */
1702 	signals[CLOSED] = g_signal_new ("closed",
1703 					SOUP_TYPE_WEBSOCKET_CONNECTION,
1704 					G_SIGNAL_RUN_FIRST,
1705 					G_STRUCT_OFFSET (SoupWebsocketConnectionClass, closed),
1706 					NULL, NULL, g_cclosure_marshal_generic,
1707 					G_TYPE_NONE, 0);
1708 
1709 	/**
1710 	 * SoupWebsocketConnection::pong:
1711 	 * @self: the WebSocket
1712 	 * @message: the application data (if any)
1713 	 *
1714 	 * Emitted when we receive a Pong frame (solicited or
1715 	 * unsolicited) from the peer.
1716 	 *
1717 	 * As a convenience, the @message data will always be
1718 	 * NUL-terminated, but the NUL byte will not be included in
1719 	 * the length count.
1720 	 *
1721 	 * Since: 2.60
1722 	 */
1723 	signals[PONG] = g_signal_new ("pong",
1724 				      SOUP_TYPE_WEBSOCKET_CONNECTION,
1725 				      G_SIGNAL_RUN_FIRST,
1726 				      G_STRUCT_OFFSET (SoupWebsocketConnectionClass, pong),
1727 				      NULL, NULL, g_cclosure_marshal_generic,
1728 				      G_TYPE_NONE, 1, G_TYPE_BYTES);
1729 }
1730 
1731 /**
1732  * soup_websocket_connection_new:
1733  * @stream: a #GIOStream connected to the WebSocket server
1734  * @uri: the URI of the connection
1735  * @type: the type of connection (client/side)
1736  * @origin: (allow-none): the Origin of the client
1737  * @protocol: (allow-none): the subprotocol in use
1738  *
1739  * Creates a #SoupWebsocketConnection on @stream. This should be
1740  * called after completing the handshake to begin using the WebSocket
1741  * protocol.
1742  *
1743  * Returns: a new #SoupWebsocketConnection
1744  *
1745  * Since: 2.50
1746  */
1747 SoupWebsocketConnection *
soup_websocket_connection_new(GIOStream * stream,SoupURI * uri,SoupWebsocketConnectionType type,const char * origin,const char * protocol)1748 soup_websocket_connection_new (GIOStream                    *stream,
1749 			       SoupURI                      *uri,
1750 			       SoupWebsocketConnectionType   type,
1751 			       const char                   *origin,
1752 			       const char                   *protocol)
1753 {
1754 	return soup_websocket_connection_new_with_extensions (stream, uri, type, origin, protocol, NULL);
1755 }
1756 
1757 /**
1758  * soup_websocket_connection_new_with_extensions:
1759  * @stream: a #GIOStream connected to the WebSocket server
1760  * @uri: the URI of the connection
1761  * @type: the type of connection (client/side)
1762  * @origin: (allow-none): the Origin of the client
1763  * @protocol: (allow-none): the subprotocol in use
1764  * @extensions: (element-type SoupWebsocketExtension) (transfer full): a #GList of #SoupWebsocketExtension objects
1765  *
1766  * Creates a #SoupWebsocketConnection on @stream with the given active @extensions.
1767  * This should be called after completing the handshake to begin using the WebSocket
1768  * protocol.
1769  *
1770  * Returns: a new #SoupWebsocketConnection
1771  *
1772  * Since: 2.68
1773  */
1774 SoupWebsocketConnection *
soup_websocket_connection_new_with_extensions(GIOStream * stream,SoupURI * uri,SoupWebsocketConnectionType type,const char * origin,const char * protocol,GList * extensions)1775 soup_websocket_connection_new_with_extensions (GIOStream                    *stream,
1776                                                SoupURI                      *uri,
1777                                                SoupWebsocketConnectionType   type,
1778                                                const char                   *origin,
1779                                                const char                   *protocol,
1780                                                GList                        *extensions)
1781 {
1782         g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL);
1783         g_return_val_if_fail (uri != NULL, NULL);
1784         g_return_val_if_fail (type != SOUP_WEBSOCKET_CONNECTION_UNKNOWN, NULL);
1785 
1786         return g_object_new (SOUP_TYPE_WEBSOCKET_CONNECTION,
1787                              "io-stream", stream,
1788                              "uri", uri,
1789                              "connection-type", type,
1790                              "origin", origin,
1791                              "protocol", protocol,
1792                              "extensions", extensions,
1793                              NULL);
1794 }
1795 
1796 /**
1797  * soup_websocket_connection_get_io_stream:
1798  * @self: the WebSocket
1799  *
1800  * Get the I/O stream the WebSocket is communicating over.
1801  *
1802  * Returns: (transfer none): the WebSocket's I/O stream.
1803  *
1804  * Since: 2.50
1805  */
1806 GIOStream *
soup_websocket_connection_get_io_stream(SoupWebsocketConnection * self)1807 soup_websocket_connection_get_io_stream (SoupWebsocketConnection *self)
1808 {
1809 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
1810 
1811 	return self->pv->io_stream;
1812 }
1813 
1814 /**
1815  * soup_websocket_connection_get_connection_type:
1816  * @self: the WebSocket
1817  *
1818  * Get the connection type (client/server) of the connection.
1819  *
1820  * Returns: the connection type
1821  *
1822  * Since: 2.50
1823  */
1824 SoupWebsocketConnectionType
soup_websocket_connection_get_connection_type(SoupWebsocketConnection * self)1825 soup_websocket_connection_get_connection_type (SoupWebsocketConnection *self)
1826 {
1827 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), SOUP_WEBSOCKET_CONNECTION_UNKNOWN);
1828 
1829 	return self->pv->connection_type;
1830 }
1831 
1832 /**
1833  * soup_websocket_connection_get_uri:
1834  * @self: the WebSocket
1835  *
1836  * Get the URI of the WebSocket.
1837  *
1838  * For servers this represents the address of the WebSocket, and
1839  * for clients it is the address connected to.
1840  *
1841  * Returns: (transfer none): the URI
1842  *
1843  * Since: 2.50
1844  */
1845 SoupURI *
soup_websocket_connection_get_uri(SoupWebsocketConnection * self)1846 soup_websocket_connection_get_uri (SoupWebsocketConnection *self)
1847 {
1848 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
1849 
1850 	return self->pv->uri;
1851 }
1852 
1853 /**
1854  * soup_websocket_connection_get_origin:
1855  * @self: the WebSocket
1856  *
1857  * Get the origin of the WebSocket.
1858  *
1859  * Returns: (nullable): the origin, or %NULL
1860  *
1861  * Since: 2.50
1862  */
1863 const char *
soup_websocket_connection_get_origin(SoupWebsocketConnection * self)1864 soup_websocket_connection_get_origin (SoupWebsocketConnection *self)
1865 {
1866 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
1867 
1868 	return self->pv->origin;
1869 }
1870 
1871 /**
1872  * soup_websocket_connection_get_protocol:
1873  * @self: the WebSocket
1874  *
1875  * Get the protocol chosen via negotiation with the peer.
1876  *
1877  * Returns: (nullable): the chosen protocol, or %NULL
1878  *
1879  * Since: 2.50
1880  */
1881 const char *
soup_websocket_connection_get_protocol(SoupWebsocketConnection * self)1882 soup_websocket_connection_get_protocol (SoupWebsocketConnection *self)
1883 {
1884 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
1885 
1886 	return self->pv->protocol;
1887 }
1888 
1889 /**
1890  * soup_websocket_connection_get_extensions:
1891  * @self: the WebSocket
1892  *
1893  * Get the extensions chosen via negotiation with the peer.
1894  *
1895  * Returns: (element-type SoupWebsocketExtension) (transfer none): a #GList of #SoupWebsocketExtension objects
1896  *
1897  * Since: 2.68
1898  */
1899 GList *
soup_websocket_connection_get_extensions(SoupWebsocketConnection * self)1900 soup_websocket_connection_get_extensions (SoupWebsocketConnection *self)
1901 {
1902         g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
1903 
1904         return self->pv->extensions;
1905 }
1906 
1907 /**
1908  * soup_websocket_connection_get_state:
1909  * @self: the WebSocket
1910  *
1911  * Get the current state of the WebSocket.
1912  *
1913  * Returns: the state
1914  *
1915  * Since: 2.50
1916  */
1917 SoupWebsocketState
soup_websocket_connection_get_state(SoupWebsocketConnection * self)1918 soup_websocket_connection_get_state (SoupWebsocketConnection *self)
1919 {
1920 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
1921 
1922 	if (self->pv->io_closed)
1923 		return SOUP_WEBSOCKET_STATE_CLOSED;
1924 	else if (self->pv->io_closing || self->pv->close_sent)
1925 		return SOUP_WEBSOCKET_STATE_CLOSING;
1926 	else
1927 		return SOUP_WEBSOCKET_STATE_OPEN;
1928 }
1929 
1930 /**
1931  * soup_websocket_connection_get_close_code:
1932  * @self: the WebSocket
1933  *
1934  * Get the close code received from the WebSocket peer.
1935  *
1936  * This only becomes valid once the WebSocket is in the
1937  * %SOUP_WEBSOCKET_STATE_CLOSED state. The value will often be in the
1938  * #SoupWebsocketCloseCode enumeration, but may also be an application
1939  * defined close code.
1940  *
1941  * Returns: the close code or zero.
1942  *
1943  * Since: 2.50
1944  */
1945 gushort
soup_websocket_connection_get_close_code(SoupWebsocketConnection * self)1946 soup_websocket_connection_get_close_code (SoupWebsocketConnection *self)
1947 {
1948 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
1949 
1950 	return self->pv->peer_close_code;
1951 }
1952 
1953 /**
1954  * soup_websocket_connection_get_close_data:
1955  * @self: the WebSocket
1956  *
1957  * Get the close data received from the WebSocket peer.
1958  *
1959  * This only becomes valid once the WebSocket is in the
1960  * %SOUP_WEBSOCKET_STATE_CLOSED state. The data may be freed once
1961  * the main loop is run, so copy it if you need to keep it around.
1962  *
1963  * Returns: the close data or %NULL
1964  *
1965  * Since: 2.50
1966  */
1967 const char *
soup_websocket_connection_get_close_data(SoupWebsocketConnection * self)1968 soup_websocket_connection_get_close_data (SoupWebsocketConnection *self)
1969 {
1970 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
1971 
1972 	return self->pv->peer_close_data;
1973 }
1974 
1975 /**
1976  * soup_websocket_connection_send_text:
1977  * @self: the WebSocket
1978  * @text: the message contents
1979  *
1980  * Send a %NULL-terminated text (UTF-8) message to the peer. If you need
1981  * to send text messages containing %NULL characters use
1982  * soup_websocket_connection_send_message() instead.
1983  *
1984  * The message is queued to be sent and will be sent when the main loop
1985  * is run.
1986  *
1987  * Since: 2.50
1988  */
1989 void
soup_websocket_connection_send_text(SoupWebsocketConnection * self,const char * text)1990 soup_websocket_connection_send_text (SoupWebsocketConnection *self,
1991 				     const char *text)
1992 {
1993 	gsize length;
1994 
1995 	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
1996 	g_return_if_fail (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN);
1997 	g_return_if_fail (text != NULL);
1998 
1999 	length = strlen (text);
2000         g_return_if_fail (utf8_validate (text, length));
2001 
2002 	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x01, (const guint8 *) text, length);
2003 }
2004 
2005 /**
2006  * soup_websocket_connection_send_binary:
2007  * @self: the WebSocket
2008  * @data: (array length=length) (element-type guint8) (nullable): the message contents
2009  * @length: the length of @data
2010  *
2011  * Send a binary message to the peer. If @length is 0, @data may be %NULL.
2012  *
2013  * The message is queued to be sent and will be sent when the main loop
2014  * is run.
2015  *
2016  * Since: 2.50
2017  */
2018 void
soup_websocket_connection_send_binary(SoupWebsocketConnection * self,gconstpointer data,gsize length)2019 soup_websocket_connection_send_binary (SoupWebsocketConnection *self,
2020 				       gconstpointer data,
2021 				       gsize length)
2022 {
2023 	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
2024 	g_return_if_fail (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN);
2025 	g_return_if_fail (data != NULL || length == 0);
2026 
2027 	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x02, data, length);
2028 }
2029 
2030 /**
2031  * soup_websocket_connection_send_message:
2032  * @self: the WebSocket
2033  * @type: the type of message contents
2034  * @message: the message data as #GBytes
2035  *
2036  * Send a message of the given @type to the peer. Note that this method,
2037  * allows to send text messages containing %NULL characters.
2038  *
2039  * The message is queued to be sent and will be sent when the main loop
2040  * is run.
2041  *
2042  * Since: 2.68
2043  */
2044 void
soup_websocket_connection_send_message(SoupWebsocketConnection * self,SoupWebsocketDataType type,GBytes * message)2045 soup_websocket_connection_send_message (SoupWebsocketConnection *self,
2046                                         SoupWebsocketDataType type,
2047                                         GBytes *message)
2048 {
2049         gconstpointer data;
2050         gsize length;
2051 
2052         g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
2053         g_return_if_fail (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN);
2054         g_return_if_fail (message != NULL);
2055 
2056         data = g_bytes_get_data (message, &length);
2057         g_return_if_fail (type != SOUP_WEBSOCKET_DATA_TEXT || utf8_validate ((const char *)data, length));
2058 
2059         send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, (int)type, data, length);
2060 }
2061 
2062 /**
2063  * soup_websocket_connection_close:
2064  * @self: the WebSocket
2065  * @code: close code
2066  * @data: (allow-none): close data
2067  *
2068  * Close the connection in an orderly fashion.
2069  *
2070  * Note that until the #SoupWebsocketConnection::closed signal fires, the connection
2071  * is not yet completely closed. The close message is not even sent until the
2072  * main loop runs.
2073  *
2074  * The @code and @data are sent to the peer along with the close request.
2075  * If @code is %SOUP_WEBSOCKET_CLOSE_NO_STATUS a close message with no body
2076  * (without code and data) is sent.
2077  * Note that the @data must be UTF-8 valid.
2078  *
2079  * Since: 2.50
2080  */
2081 void
soup_websocket_connection_close(SoupWebsocketConnection * self,gushort code,const char * data)2082 soup_websocket_connection_close (SoupWebsocketConnection *self,
2083 				 gushort code,
2084 				 const char *data)
2085 {
2086 	SoupWebsocketConnectionPrivate *pv;
2087 
2088 	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
2089 	pv = self->pv;
2090 	g_return_if_fail (!pv->close_sent);
2091 
2092 	g_return_if_fail (code != SOUP_WEBSOCKET_CLOSE_ABNORMAL &&
2093 			  code != SOUP_WEBSOCKET_CLOSE_TLS_HANDSHAKE);
2094 	if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER)
2095 		g_return_if_fail (code != SOUP_WEBSOCKET_CLOSE_NO_EXTENSION);
2096 	else
2097 		g_return_if_fail (code != SOUP_WEBSOCKET_CLOSE_SERVER_ERROR);
2098 
2099 	close_connection (self, code, data);
2100 }
2101 
2102 /**
2103  * soup_websocket_connection_get_max_incoming_payload_size:
2104  * @self: the WebSocket
2105  *
2106  * Gets the maximum payload size allowed for incoming packets.
2107  *
2108  * Returns: the maximum payload size.
2109  *
2110  * Since: 2.56
2111  */
2112 guint64
soup_websocket_connection_get_max_incoming_payload_size(SoupWebsocketConnection * self)2113 soup_websocket_connection_get_max_incoming_payload_size (SoupWebsocketConnection *self)
2114 {
2115 	SoupWebsocketConnectionPrivate *pv;
2116 
2117 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), MAX_INCOMING_PAYLOAD_SIZE_DEFAULT);
2118 	pv = self->pv;
2119 
2120 	return pv->max_incoming_payload_size;
2121 }
2122 
2123 /**
2124  * soup_websocket_connection_set_max_incoming_payload_size:
2125  * @self: the WebSocket
2126  * @max_incoming_payload_size: the maximum payload size
2127  *
2128  * Sets the maximum payload size allowed for incoming packets. It
2129  * does not limit the outgoing packet size.
2130  *
2131  * Since: 2.56
2132  */
2133 void
soup_websocket_connection_set_max_incoming_payload_size(SoupWebsocketConnection * self,guint64 max_incoming_payload_size)2134 soup_websocket_connection_set_max_incoming_payload_size (SoupWebsocketConnection *self,
2135                                                          guint64                  max_incoming_payload_size)
2136 {
2137 	SoupWebsocketConnectionPrivate *pv;
2138 
2139 	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
2140 	pv = self->pv;
2141 
2142 	if (pv->max_incoming_payload_size != max_incoming_payload_size) {
2143 		pv->max_incoming_payload_size = max_incoming_payload_size;
2144 		g_object_notify (G_OBJECT (self), "max-incoming-payload-size");
2145 	}
2146 }
2147 
2148 /**
2149  * soup_websocket_connection_get_keepalive_interval:
2150  * @self: the WebSocket
2151  *
2152  * Gets the keepalive interval in seconds or 0 if disabled.
2153  *
2154  * Returns: the keepalive interval.
2155  *
2156  * Since: 2.58
2157  */
2158 guint
soup_websocket_connection_get_keepalive_interval(SoupWebsocketConnection * self)2159 soup_websocket_connection_get_keepalive_interval (SoupWebsocketConnection *self)
2160 {
2161 	SoupWebsocketConnectionPrivate *pv;
2162 
2163 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
2164 	pv = self->pv;
2165 
2166 	return pv->keepalive_interval;
2167 }
2168 
2169 static gboolean
on_queue_ping(gpointer user_data)2170 on_queue_ping (gpointer user_data)
2171 {
2172 	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
2173 	static const char ping_payload[] = "libsoup";
2174 
2175 	g_debug ("sending ping message");
2176 
2177 	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x09,
2178 		      (guint8 *) ping_payload, strlen(ping_payload));
2179 
2180 	return G_SOURCE_CONTINUE;
2181 }
2182 
2183 /**
2184  * soup_websocket_connection_set_keepalive_interval:
2185  * @self: the WebSocket
2186  * @interval: the interval to send a ping message or 0 to disable it
2187  *
2188  * Sets the interval in seconds on when to send a ping message which will serve
2189  * as a keepalive message. If set to 0 the keepalive message is disabled.
2190  *
2191  * Since: 2.58
2192  */
2193 void
soup_websocket_connection_set_keepalive_interval(SoupWebsocketConnection * self,guint interval)2194 soup_websocket_connection_set_keepalive_interval (SoupWebsocketConnection *self,
2195                                                   guint                    interval)
2196 {
2197 	SoupWebsocketConnectionPrivate *pv;
2198 
2199 	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
2200 	pv = self->pv;
2201 
2202 	if (pv->keepalive_interval != interval) {
2203 		pv->keepalive_interval = interval;
2204 		g_object_notify (G_OBJECT (self), "keepalive-interval");
2205 
2206 		keepalive_stop_timeout (self);
2207 
2208 		if (interval > 0) {
2209 			pv->keepalive_timeout = g_timeout_source_new_seconds (interval);
2210 			g_source_set_callback (pv->keepalive_timeout, on_queue_ping, self, NULL);
2211 			g_source_attach (pv->keepalive_timeout, pv->main_context);
2212 		}
2213 	}
2214 }
2215