Blame libsoup/soup-websocket-connection.c

Packit Service ca3877
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Packit Service ca3877
/*
Packit Service ca3877
 * soup-websocket-connection.c: This file was originally part of Cockpit.
Packit Service ca3877
 *
Packit Service ca3877
 * Copyright 2013, 2014 Red Hat, Inc.
Packit Service ca3877
 *
Packit Service ca3877
 * Cockpit is free software; you can redistribute it and/or modify it
Packit Service ca3877
 * under the terms of the GNU Lesser General Public License as published by
Packit Service ca3877
 * the Free Software Foundation; either version 2.1 of the License, or
Packit Service ca3877
 * (at your option) any later version.
Packit Service ca3877
 *
Packit Service ca3877
 * Cockpit is distributed in the hope that it will be useful, but
Packit Service ca3877
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service ca3877
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit Service ca3877
 * Lesser General Public License for more details.
Packit Service ca3877
 *
Packit Service ca3877
 * You should have received a copy of the GNU Lesser General Public License
Packit Service ca3877
 * along with this library; If not, see <http://www.gnu.org/licenses/>.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
#include "config.h"
Packit Service ca3877
Packit Service ca3877
#include <string.h>
Packit Service ca3877
Packit Service ca3877
#include "soup-websocket-connection.h"
Packit Service ca3877
#include "soup-enum-types.h"
Packit Service ca3877
#include "soup-uri.h"
Packit Service ca3877
Packit Service ca3877
/*
Packit Service ca3877
 * SECTION:websocketconnection
Packit Service ca3877
 * @title: SoupWebsocketConnection
Packit Service ca3877
 * @short_description: A WebSocket connection
Packit Service ca3877
 *
Packit Service ca3877
 * A #SoupWebsocketConnection is a WebSocket connection to a peer.
Packit Service ca3877
 * This API is modeled after the W3C API for interacting with
Packit Service ca3877
 * WebSockets.
Packit Service ca3877
 *
Packit Service ca3877
 * The #SoupWebsocketConnection:state property will indicate the
Packit Service ca3877
 * state of the connection.
Packit Service ca3877
 *
Packit Service ca3877
 * Use soup_websocket_connection_send() to send a message to the peer.
Packit Service ca3877
 * When a message is received the #SoupWebsocketConnection::message
Packit Service ca3877
 * signal will fire.
Packit Service ca3877
 *
Packit Service ca3877
 * The soup_websocket_connection_close() function will perform an
Packit Service ca3877
 * orderly close of the connection. The
Packit Service ca3877
 * #SoupWebsocketConnection::closed signal will fire once the
Packit Service ca3877
 * connection closes, whether it was initiated by this side or the
Packit Service ca3877
 * peer.
Packit Service ca3877
 *
Packit Service ca3877
 * Connect to the #SoupWebsocketConnection::closing signal to detect
Packit Service ca3877
 * when either peer begins closing the connection.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SoupWebsocketConnection:
Packit Service ca3877
 *
Packit Service ca3877
 * A class representing a WebSocket connection.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SoupWebsocketConnectionClass:
Packit Service ca3877
 * @message: default handler for the #SoupWebsocketConnection::message signal
Packit Service ca3877
 * @error: default handler for the #SoupWebsocketConnection::error signal
Packit Service ca3877
 * @closing: the default handler for the #SoupWebsocketConnection:closing signal
Packit Service ca3877
 * @closed: default handler for the #SoupWebsocketConnection::closed signal
Packit Service ca3877
 * @pong: default handler for the #SoupWebsocketConnection::pong signal
Packit Service ca3877
 *
Packit Service ca3877
 * The abstract base class for #SoupWebsocketConnection
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
enum {
Packit Service ca3877
	PROP_0,
Packit Service ca3877
	PROP_IO_STREAM,
Packit Service ca3877
	PROP_CONNECTION_TYPE,
Packit Service ca3877
	PROP_URI,
Packit Service ca3877
	PROP_ORIGIN,
Packit Service ca3877
	PROP_PROTOCOL,
Packit Service ca3877
	PROP_STATE,
Packit Service ca3877
	PROP_MAX_INCOMING_PAYLOAD_SIZE,
Packit Service ca3877
	PROP_KEEPALIVE_INTERVAL,
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
enum {
Packit Service ca3877
	MESSAGE,
Packit Service ca3877
	ERROR,
Packit Service ca3877
	CLOSING,
Packit Service ca3877
	CLOSED,
Packit Service ca3877
	PONG,
Packit Service ca3877
	NUM_SIGNALS
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
static guint signals[NUM_SIGNALS] = { 0, };
Packit Service ca3877
Packit Service ca3877
typedef enum {
Packit Service ca3877
	SOUP_WEBSOCKET_QUEUE_NORMAL = 0,
Packit Service ca3877
	SOUP_WEBSOCKET_QUEUE_URGENT = 1 << 0,
Packit Service ca3877
	SOUP_WEBSOCKET_QUEUE_LAST = 1 << 1,
Packit Service ca3877
} SoupWebsocketQueueFlags;
Packit Service ca3877
Packit Service ca3877
typedef struct {
Packit Service ca3877
	GBytes *data;
Packit Service ca3877
	gsize sent;
Packit Service ca3877
	gsize amount;
Packit Service ca3877
	SoupWebsocketQueueFlags flags;
Packit Service ca3877
	gboolean pending;
Packit Service ca3877
} Frame;
Packit Service ca3877
Packit Service ca3877
struct _SoupWebsocketConnectionPrivate {
Packit Service ca3877
	GIOStream *io_stream;
Packit Service ca3877
	SoupWebsocketConnectionType connection_type;
Packit Service ca3877
	SoupURI *uri;
Packit Service ca3877
	char *origin;
Packit Service ca3877
	char *protocol;
Packit Service ca3877
	guint64 max_incoming_payload_size;
Packit Service ca3877
	guint keepalive_interval;
Packit Service ca3877
Packit Service ca3877
	gushort peer_close_code;
Packit Service ca3877
	char *peer_close_data;
Packit Service ca3877
	gboolean close_sent;
Packit Service ca3877
	gboolean close_received;
Packit Service ca3877
	gboolean dirty_close;
Packit Service ca3877
	GSource *close_timeout;
Packit Service ca3877
Packit Service ca3877
	GMainContext *main_context;
Packit Service ca3877
Packit Service ca3877
	gboolean io_closing;
Packit Service ca3877
	gboolean io_closed;
Packit Service ca3877
Packit Service ca3877
	GPollableInputStream *input;
Packit Service ca3877
	GSource *input_source;
Packit Service ca3877
	GByteArray *incoming;
Packit Service ca3877
Packit Service ca3877
	GPollableOutputStream *output;
Packit Service ca3877
	GSource *output_source;
Packit Service ca3877
	GQueue outgoing;
Packit Service ca3877
Packit Service ca3877
	/* Current message being assembled */
Packit Service ca3877
	guint8 message_opcode;
Packit Service ca3877
	GByteArray *message_data;
Packit Service ca3877
Packit Service ca3877
	GSource *keepalive_timeout;
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
#define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT   128 * 1024
Packit Service f07534
#define READ_BUFFER_SIZE 1024
Packit Service ca3877
Packit Service ca3877
G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, soup_websocket_connection, G_TYPE_OBJECT)
Packit Service ca3877
Packit Service ca3877
static void queue_frame (SoupWebsocketConnection *self, SoupWebsocketQueueFlags flags,
Packit Service ca3877
			 gpointer data, gsize len, gsize amount);
Packit Service ca3877
Packit Service ca3877
static void protocol_error_and_close (SoupWebsocketConnection *self);
Packit Service ca3877
Packit Service f07534
static gboolean on_web_socket_input (GObject *pollable_stream,
Packit Service f07534
				     gpointer user_data);
Packit Service f07534
static gboolean on_web_socket_output (GObject *pollable_stream,
Packit Service f07534
				      gpointer user_data);
Packit Service f07534
Packit Service e0d935
/* Code below is based on g_utf8_validate() implementation,
Packit Service e0d935
 * but handling NULL characters as valid, as expected by
Packit Service e0d935
 * WebSockets and compliant with RFC 3629.
Packit Service e0d935
 */
Packit Service e0d935
#define VALIDATE_BYTE(mask, expect)                             \
Packit Service e0d935
        G_STMT_START {                                          \
Packit Service e0d935
          if (G_UNLIKELY((*(guchar *)p & (mask)) != (expect)))  \
Packit Service e0d935
                  return FALSE;                                 \
Packit Service e0d935
        } G_STMT_END
Packit Service e0d935
Packit Service e0d935
/* see IETF RFC 3629 Section 4 */
Packit Service e0d935
static gboolean
Packit Service e0d935
utf8_validate (const char *str,
Packit Service e0d935
               gsize max_len)
Packit Service e0d935
Packit Service e0d935
{
Packit Service e0d935
        const gchar *p;
Packit Service e0d935
Packit Service e0d935
        for (p = str; ((p - str) < max_len); p++) {
Packit Service e0d935
                if (*(guchar *)p < 128)
Packit Service e0d935
                        /* done */;
Packit Service e0d935
                else {
Packit Service e0d935
                        if (*(guchar *)p < 0xe0) { /* 110xxxxx */
Packit Service e0d935
                                if (G_UNLIKELY (max_len - (p - str) < 2))
Packit Service e0d935
                                        return FALSE;
Packit Service e0d935
Packit Service e0d935
                                if (G_UNLIKELY (*(guchar *)p < 0xc2))
Packit Service e0d935
                                        return FALSE;
Packit Service e0d935
                        } else {
Packit Service e0d935
                                if (*(guchar *)p < 0xf0) { /* 1110xxxx */
Packit Service e0d935
                                        if (G_UNLIKELY (max_len - (p - str) < 3))
Packit Service e0d935
                                                return FALSE;
Packit Service e0d935
Packit Service e0d935
                                        switch (*(guchar *)p++ & 0x0f) {
Packit Service e0d935
                                        case 0:
Packit Service e0d935
                                                VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
Packit Service e0d935
                                                break;
Packit Service e0d935
                                        case 0x0d:
Packit Service e0d935
                                                VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
Packit Service e0d935
                                                break;
Packit Service e0d935
                                        default:
Packit Service e0d935
                                                VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
Packit Service e0d935
                                        }
Packit Service e0d935
                                } else if (*(guchar *)p < 0xf5) { /* 11110xxx excluding out-of-range */
Packit Service e0d935
                                        if (G_UNLIKELY (max_len - (p - str) < 4))
Packit Service e0d935
                                                return FALSE;
Packit Service e0d935
Packit Service e0d935
                                        switch (*(guchar *)p++ & 0x07) {
Packit Service e0d935
                                        case 0:
Packit Service e0d935
                                                VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
Packit Service e0d935
                                                if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
Packit Service e0d935
                                                        return FALSE;
Packit Service e0d935
                                                break;
Packit Service e0d935
                                        case 4:
Packit Service e0d935
                                                VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
Packit Service e0d935
                                                break;
Packit Service e0d935
                                        default:
Packit Service e0d935
                                                VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
Packit Service e0d935
                                        }
Packit Service e0d935
                                        p++;
Packit Service e0d935
                                        VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
Packit Service e0d935
                                } else {
Packit Service e0d935
                                        return FALSE;
Packit Service e0d935
                                }
Packit Service e0d935
                        }
Packit Service e0d935
Packit Service e0d935
                        p++;
Packit Service e0d935
                        VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
Packit Service e0d935
                }
Packit Service e0d935
        }
Packit Service e0d935
Packit Service e0d935
        return TRUE;
Packit Service e0d935
}
Packit Service e0d935
Packit Service e0d935
#undef VALIDATE_BYTE
Packit Service e0d935
Packit Service ca3877
static void
Packit Service ca3877
frame_free (gpointer data)
Packit Service ca3877
{
Packit Service ca3877
	Frame *frame = data;
Packit Service ca3877
Packit Service ca3877
	if (frame) {
Packit Service ca3877
		g_bytes_unref (frame->data);
Packit Service ca3877
		g_slice_free (Frame, frame);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_init (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	pv = self->pv = soup_websocket_connection_get_instance_private (self);
Packit Service ca3877
Packit Service ca3877
	pv->incoming = g_byte_array_sized_new (1024);
Packit Service ca3877
	g_queue_init (&pv->outgoing);
Packit Service ca3877
	pv->main_context = g_main_context_ref_thread_default ();
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
on_iostream_closed (GObject *source,
Packit Service ca3877
                    GAsyncResult *result,
Packit Service ca3877
                    gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = user_data;
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	GError *error = NULL;
Packit Service ca3877
Packit Service ca3877
	/* We treat connection as closed even if close fails */
Packit Service ca3877
	pv->io_closed = TRUE;
Packit Service ca3877
	g_io_stream_close_finish (pv->io_stream, result, &error);
Packit Service ca3877
Packit Service ca3877
	if (error) {
Packit Service ca3877
		g_debug ("error closing web socket stream: %s", error->message);
Packit Service ca3877
		if (!pv->dirty_close)
Packit Service ca3877
			g_signal_emit (self, signals[ERROR], 0, error);
Packit Service ca3877
		pv->dirty_close = TRUE;
Packit Service ca3877
		g_error_free (error);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_assert (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED);
Packit Service ca3877
	g_debug ("closed: completed io stream close");
Packit Service ca3877
	g_signal_emit (self, signals[CLOSED], 0);
Packit Service ca3877
Packit Service ca3877
	g_object_unref (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service f07534
soup_websocket_connection_start_input_source (SoupWebsocketConnection *self)
Packit Service f07534
{
Packit Service f07534
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service f07534
Packit Service f07534
	if (pv->input_source)
Packit Service f07534
		return;
Packit Service f07534
Packit Service f07534
	pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
Packit Service f07534
	g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
Packit Service f07534
	g_source_attach (pv->input_source, pv->main_context);
Packit Service f07534
}
Packit Service f07534
Packit Service f07534
static void
Packit Service f07534
soup_websocket_connection_stop_input_source (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->input_source) {
Packit Service ca3877
		g_debug ("stopping input source");
Packit Service ca3877
		g_source_destroy (pv->input_source);
Packit Service ca3877
		g_source_unref (pv->input_source);
Packit Service ca3877
		pv->input_source = NULL;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service f07534
soup_websocket_connection_start_output_source (SoupWebsocketConnection *self)
Packit Service f07534
{
Packit Service f07534
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service f07534
Packit Service f07534
	if (pv->output_source)
Packit Service f07534
		return;
Packit Service f07534
Packit Service f07534
	pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
Packit Service f07534
	g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
Packit Service f07534
	g_source_attach (pv->output_source, pv->main_context);
Packit Service f07534
}
Packit Service f07534
Packit Service f07534
static void
Packit Service f07534
soup_websocket_connection_stop_output_source (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->output_source) {
Packit Service ca3877
		g_debug ("stopping output source");
Packit Service ca3877
		g_source_destroy (pv->output_source);
Packit Service ca3877
		g_source_unref (pv->output_source);
Packit Service ca3877
		pv->output_source = NULL;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
keepalive_stop_timeout (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->keepalive_timeout) {
Packit Service ca3877
		g_source_destroy (pv->keepalive_timeout);
Packit Service ca3877
		g_source_unref (pv->keepalive_timeout);
Packit Service ca3877
		pv->keepalive_timeout = NULL;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
close_io_stop_timeout (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->close_timeout) {
Packit Service ca3877
		g_source_destroy (pv->close_timeout);
Packit Service ca3877
		g_source_unref (pv->close_timeout);
Packit Service ca3877
		pv->close_timeout = NULL;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
close_io_stream (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	keepalive_stop_timeout (self);
Packit Service ca3877
	close_io_stop_timeout (self);
Packit Service ca3877
Packit Service ca3877
	if (!pv->io_closing) {
Packit Service f07534
		soup_websocket_connection_stop_input_source (self);
Packit Service f07534
		soup_websocket_connection_stop_output_source (self);
Packit Service ca3877
		pv->io_closing = TRUE;
Packit Service ca3877
		g_debug ("closing io stream");
Packit Service ca3877
		g_io_stream_close_async (pv->io_stream, G_PRIORITY_DEFAULT,
Packit Service ca3877
					 NULL, on_iostream_closed, g_object_ref (self));
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_object_notify (G_OBJECT (self), "state");
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
shutdown_wr_io_stream (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	GSocket *socket;
Packit Service ca3877
	GError *error = NULL;
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_stop_output_source (self);
Packit Service ca3877
Packit Service ca3877
	if (G_IS_SOCKET_CONNECTION (pv->io_stream)) {
Packit Service ca3877
		socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (pv->io_stream));
Packit Service ca3877
		g_socket_shutdown (socket, FALSE, TRUE, &error);
Packit Service ca3877
		if (error != NULL) {
Packit Service ca3877
			g_debug ("error shutting down io stream: %s", error->message);
Packit Service ca3877
			g_error_free (error);
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_object_notify (G_OBJECT (self), "state");
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
on_timeout_close_io (gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	pv->close_timeout = 0;
Packit Service ca3877
Packit Service ca3877
	g_debug ("peer did not close io when expected");
Packit Service ca3877
	close_io_stream (self);
Packit Service ca3877
Packit Service ca3877
	return FALSE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
close_io_after_timeout (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	const int timeout = 5;
Packit Service ca3877
Packit Service ca3877
	if (pv->close_timeout)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	g_debug ("waiting %d seconds for peer to close io", timeout);
Packit Service ca3877
	pv->close_timeout = g_timeout_source_new_seconds (timeout);
Packit Service ca3877
	g_source_set_callback (pv->close_timeout, on_timeout_close_io, self, NULL);
Packit Service ca3877
	g_source_attach (pv->close_timeout, pv->main_context);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
xor_with_mask (const guint8 *mask,
Packit Service ca3877
	       guint8 *data,
Packit Service ca3877
	       gsize len)
Packit Service ca3877
{
Packit Service ca3877
	gsize n;
Packit Service ca3877
Packit Service ca3877
	/* Do the masking */
Packit Service ca3877
	for (n = 0; n < len; n++)
Packit Service ca3877
		data[n] ^= mask[n & 3];
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
send_message (SoupWebsocketConnection *self,
Packit Service ca3877
	      SoupWebsocketQueueFlags flags,
Packit Service ca3877
	      guint8 opcode,
Packit Service ca3877
	      const guint8 *data,
Packit Service ca3877
	      gsize length)
Packit Service ca3877
{
Packit Service ca3877
	gsize buffered_amount = length;
Packit Service ca3877
	GByteArray *bytes;
Packit Service ca3877
	gsize frame_len;
Packit Service ca3877
	guint8 *outer;
Packit Service ca3877
	guint8 *mask = 0;
Packit Service ca3877
	guint8 *at;
Packit Service ca3877
Packit Service ca3877
	if (!(soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN)) {
Packit Service ca3877
		g_debug ("Ignoring message since the connection is closed or is closing");
Packit Service ca3877
		return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	bytes = g_byte_array_sized_new (14 + length);
Packit Service ca3877
	outer = bytes->data;
Packit Service ca3877
	outer[0] = 0x80 | opcode;
Packit Service ca3877
Packit Service ca3877
	/* If control message, check payload size */
Packit Service ca3877
	if (opcode & 0x08) {
Packit Service ca3877
		if (length > 125) {
Packit Service ca3877
			g_warning ("WebSocket control message payload exceeds size limit");
Packit Service ca3877
			protocol_error_and_close (self);
Packit Service ca3877
			return;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		buffered_amount = 0;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (length < 126) {
Packit Service ca3877
		outer[1] = (0xFF & length); /* mask | 7-bit-len */
Packit Service ca3877
		bytes->len = 2;
Packit Service ca3877
	} else if (length < 65536) {
Packit Service ca3877
		outer[1] = 126; /* mask | 16-bit-len */
Packit Service ca3877
		outer[2] = (length >> 8) & 0xFF;
Packit Service ca3877
		outer[3] = (length >> 0) & 0xFF;
Packit Service ca3877
		bytes->len = 4;
Packit Service ca3877
	} else {
Packit Service ca3877
		outer[1] = 127; /* mask | 64-bit-len */
Packit Service ca3877
#if GLIB_SIZEOF_SIZE_T > 4
Packit Service ca3877
		outer[2] = (length >> 56) & 0xFF;
Packit Service ca3877
		outer[3] = (length >> 48) & 0xFF;
Packit Service ca3877
		outer[4] = (length >> 40) & 0xFF;
Packit Service ca3877
		outer[5] = (length >> 32) & 0xFF;
Packit Service ca3877
#else
Packit Service ca3877
		outer[2] = outer[3] = outer[4] = outer[5] = 0;
Packit Service ca3877
#endif
Packit Service ca3877
		outer[6] = (length >> 24) & 0xFF;
Packit Service ca3877
		outer[7] = (length >> 16) & 0xFF;
Packit Service ca3877
		outer[8] = (length >> 8) & 0xFF;
Packit Service ca3877
		outer[9] = (length >> 0) & 0xFF;
Packit Service ca3877
		bytes->len = 10;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* The server side doesn't need to mask, so we don't. There's
Packit Service ca3877
	 * probably a client somewhere that's not expecting it.
Packit Service ca3877
	 */
Packit Service ca3877
	if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_CLIENT) {
Packit Service ca3877
		guint32 rnd = g_random_int ();
Packit Service ca3877
		outer[1] |= 0x80;
Packit Service ca3877
		mask = outer + bytes->len;
Packit Service ca3877
		memcpy (mask, &rnd, sizeof (rnd));
Packit Service ca3877
		bytes->len += 4;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	at = bytes->data + bytes->len;
Packit Service ca3877
	g_byte_array_append (bytes, data, length);
Packit Service ca3877
Packit Service ca3877
	if (self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_CLIENT)
Packit Service ca3877
		xor_with_mask (mask, at, length);
Packit Service ca3877
Packit Service ca3877
	frame_len = bytes->len;
Packit Service ca3877
	queue_frame (self, flags, g_byte_array_free (bytes, FALSE),
Packit Service ca3877
		     frame_len, buffered_amount);
Packit Service ca3877
	g_debug ("queued %d frame of len %u", (int)opcode, (guint)frame_len);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
send_close (SoupWebsocketConnection *self,
Packit Service ca3877
	    SoupWebsocketQueueFlags flags,
Packit Service ca3877
	    gushort code,
Packit Service ca3877
	    const char *reason)
Packit Service ca3877
{
Packit Service ca3877
	/* Note that send_message truncates as expected */
Packit Service ca3877
	char buffer[128];
Packit Service ca3877
	gsize len = 0;
Packit Service ca3877
Packit Service ca3877
	if (code != 0) {
Packit Service ca3877
		buffer[len++] = code >> 8;
Packit Service ca3877
		buffer[len++] = code & 0xFF;
Packit Service ca3877
		if (reason)
Packit Service ca3877
			len += g_strlcpy (buffer + len, reason, sizeof (buffer) - len);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	send_message (self, flags, 0x08, (guint8 *)buffer, len);
Packit Service ca3877
	self->pv->close_sent = TRUE;
Packit Service ca3877
Packit Service ca3877
	keepalive_stop_timeout (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
emit_error_and_close (SoupWebsocketConnection *self,
Packit Service ca3877
		      GError *error,
Packit Service ca3877
		      gboolean prejudice)
Packit Service ca3877
{
Packit Service ca3877
	gboolean ignore = FALSE;
Packit Service ca3877
	gushort code;
Packit Service ca3877
Packit Service ca3877
	if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
Packit Service ca3877
		g_error_free (error);
Packit Service ca3877
		return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (error && error->domain == SOUP_WEBSOCKET_ERROR)
Packit Service ca3877
		code = error->code;
Packit Service ca3877
	else
Packit Service ca3877
		code = SOUP_WEBSOCKET_CLOSE_GOING_AWAY;
Packit Service ca3877
Packit Service ca3877
	self->pv->dirty_close = TRUE;
Packit Service ca3877
	g_signal_emit (self, signals[ERROR], 0, error);
Packit Service ca3877
	g_error_free (error);
Packit Service ca3877
Packit Service ca3877
	/* If already closing, just ignore this stuff */
Packit Service ca3877
	switch (soup_websocket_connection_get_state (self)) {
Packit Service ca3877
	case SOUP_WEBSOCKET_STATE_CLOSED:
Packit Service ca3877
		ignore = TRUE;
Packit Service ca3877
		break;
Packit Service ca3877
	case SOUP_WEBSOCKET_STATE_CLOSING:
Packit Service ca3877
		ignore = !prejudice;
Packit Service ca3877
		break;
Packit Service ca3877
	default:
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (ignore) {
Packit Service ca3877
		g_debug ("already closing/closed, ignoring error");
Packit Service ca3877
	} else if (prejudice) {
Packit Service ca3877
		g_debug ("forcing close due to error");
Packit Service ca3877
		close_io_stream (self);
Packit Service ca3877
	} else {
Packit Service ca3877
		g_debug ("requesting close due to error");
Packit Service ca3877
		send_close (self, SOUP_WEBSOCKET_QUEUE_URGENT | SOUP_WEBSOCKET_QUEUE_LAST, code, NULL);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
protocol_error_and_close_full (SoupWebsocketConnection *self,
Packit Service ca3877
                               gboolean prejudice)
Packit Service ca3877
{
Packit Service ca3877
	GError *error;
Packit Service ca3877
Packit Service ca3877
	error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
Packit Service ca3877
				     SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR,
Packit Service ca3877
				     self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
Packit Service ca3877
				     "Received invalid WebSocket response from the client" :
Packit Service ca3877
				     "Received invalid WebSocket response from the server");
Packit Service ca3877
	emit_error_and_close (self, error, prejudice);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
protocol_error_and_close (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	protocol_error_and_close_full (self, FALSE);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
bad_data_error_and_close (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	GError *error;
Packit Service ca3877
Packit Service ca3877
	error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
Packit Service ca3877
				     SOUP_WEBSOCKET_CLOSE_BAD_DATA,
Packit Service ca3877
				     self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
Packit Service ca3877
				     "Received invalid WebSocket data from the client" :
Packit Service ca3877
				     "Received invalid WebSocket data from the server");
Packit Service ca3877
	emit_error_and_close (self, error, FALSE);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
too_big_error_and_close (SoupWebsocketConnection *self,
Packit Service ca3877
                         guint64 payload_len)
Packit Service ca3877
{
Packit Service ca3877
	GError *error;
Packit Service ca3877
Packit Service ca3877
	error = g_error_new_literal (SOUP_WEBSOCKET_ERROR,
Packit Service ca3877
				     SOUP_WEBSOCKET_CLOSE_TOO_BIG,
Packit Service ca3877
				     self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ?
Packit Service ca3877
				     "Received extremely large WebSocket data from the client" :
Packit Service ca3877
				     "Received extremely large WebSocket data from the server");
Packit Service ca3877
	g_debug ("%s is trying to frame of size %" G_GUINT64_FORMAT " or greater, but max supported size is %" G_GUINT64_FORMAT,
Packit Service ca3877
		 self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ? "server" : "client",
Packit Service ca3877
	         payload_len, self->pv->max_incoming_payload_size);
Packit Service ca3877
	emit_error_and_close (self, error, TRUE);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
close_connection (SoupWebsocketConnection *self,
Packit Service ca3877
                  gushort                  code,
Packit Service ca3877
                  const char              *data)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketQueueFlags flags;
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->close_sent) {
Packit Service ca3877
		g_debug ("close code already sent");
Packit Service ca3877
		return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* Validate the closing code received by the peer */
Packit Service ca3877
	switch (code) {
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_NORMAL:
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_GOING_AWAY:
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR:
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_UNSUPPORTED_DATA:
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_BAD_DATA:
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_POLICY_VIOLATION:
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_TOO_BIG:
Packit Service ca3877
		break;
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_NO_EXTENSION:
Packit Service ca3877
		if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER) {
Packit Service ca3877
			g_debug ("Wrong closing code %d received for a server connection",
Packit Service ca3877
			         code);
Packit Service ca3877
		}
Packit Service ca3877
		break;
Packit Service ca3877
	case SOUP_WEBSOCKET_CLOSE_SERVER_ERROR:
Packit Service ca3877
		if (pv->connection_type != SOUP_WEBSOCKET_CONNECTION_SERVER) {
Packit Service ca3877
			g_debug ("Wrong closing code %d received for a non server connection",
Packit Service ca3877
			         code);
Packit Service ca3877
		}
Packit Service ca3877
		break;
Packit Service ca3877
	default:
Packit Service ca3877
		if (code < 3000) {
Packit Service ca3877
			g_debug ("Wrong closing code %d received", code);
Packit Service ca3877
			protocol_error_and_close (self);
Packit Service ca3877
			return;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_signal_emit (self, signals[CLOSING], 0);
Packit Service ca3877
Packit Service ca3877
	if (pv->close_received)
Packit Service ca3877
		g_debug ("responding to close request");
Packit Service ca3877
Packit Service ca3877
	flags = 0;
Packit Service ca3877
	if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER && pv->close_received)
Packit Service ca3877
		flags |= SOUP_WEBSOCKET_QUEUE_LAST;
Packit Service ca3877
	send_close (self, flags, code, data);
Packit Service ca3877
	close_io_after_timeout (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
receive_close (SoupWebsocketConnection *self,
Packit Service ca3877
	       const guint8 *data,
Packit Service ca3877
	       gsize len)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	pv->peer_close_code = 0;
Packit Service ca3877
	g_free (pv->peer_close_data);
Packit Service ca3877
	pv->peer_close_data = NULL;
Packit Service ca3877
	pv->close_received = TRUE;
Packit Service ca3877
Packit Service ca3877
	switch (len) {
Packit Service ca3877
	case 0:
Packit Service ca3877
		/* Send a clean close when having an empty payload */
Packit Service ca3877
		close_connection (self, 1000, NULL);
Packit Service ca3877
		return;
Packit Service ca3877
	case 1:
Packit Service ca3877
		/* Send a protocol error since the close code is incomplete */
Packit Service ca3877
		protocol_error_and_close (self);
Packit Service ca3877
		return;
Packit Service ca3877
	default:
Packit Service ca3877
		/* Store the code/data payload */
Packit Service ca3877
		pv->peer_close_code = (guint16)data[0] << 8 | data[1];
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (len > 2) {
Packit Service ca3877
		data += 2;
Packit Service ca3877
		len -= 2;
Packit Service ca3877
		
Packit Service e0d935
		if (!utf8_validate ((const char *)data, len)) {
Packit Service ca3877
			g_debug ("received non-UTF8 close data: %d '%.*s' %d", (int)len, (int)len, (char *)data, (int)data[0]);
Packit Service ca3877
			protocol_error_and_close (self);
Packit Service ca3877
			return;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		pv->peer_close_data = g_strndup ((char *)data, len);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* Once we receive close response on server, close immediately */
Packit Service ca3877
	if (pv->close_sent) {
Packit Service ca3877
		shutdown_wr_io_stream (self);
Packit Service ca3877
		if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER)
Packit Service ca3877
			close_io_stream (self);
Packit Service ca3877
	} else {
Packit Service ca3877
		close_connection (self, pv->peer_close_code, pv->peer_close_data);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
receive_ping (SoupWebsocketConnection *self,
Packit Service ca3877
                      const guint8 *data,
Packit Service ca3877
                      gsize len)
Packit Service ca3877
{
Packit Service ca3877
	/* Send back a pong with same data */
Packit Service ca3877
	g_debug ("received ping, responding");
Packit Service ca3877
	send_message (self, SOUP_WEBSOCKET_QUEUE_URGENT, 0x0A, data, len);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
receive_pong (SoupWebsocketConnection *self,
Packit Service ca3877
                      const guint8 *data,
Packit Service ca3877
                      gsize len)
Packit Service ca3877
{
Packit Service ca3877
	GByteArray *bytes;
Packit Service ca3877
Packit Service ca3877
	g_debug ("received pong message");
Packit Service ca3877
Packit Service ca3877
	bytes = g_byte_array_sized_new (len + 1);
Packit Service ca3877
	g_byte_array_append (bytes, data, len);
Packit Service ca3877
	/* Always null terminate, as a convenience */
Packit Service ca3877
	g_byte_array_append (bytes, (guchar *)"\0", 1);
Packit Service ca3877
	/* But don't include the null terminator in the byte count */
Packit Service ca3877
	bytes->len--;
Packit Service ca3877
Packit Service ca3877
	g_signal_emit (self, signals[PONG], 0, bytes);
Packit Service ca3877
	g_byte_array_unref (bytes);
Packit Service ca3877
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
process_contents (SoupWebsocketConnection *self,
Packit Service ca3877
		  gboolean control,
Packit Service ca3877
		  gboolean fin,
Packit Service ca3877
		  guint8 opcode,
Packit Service ca3877
		  gconstpointer payload,
Packit Service ca3877
		  gsize payload_len)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	GBytes *message;
Packit Service ca3877
Packit Service f69f0c
	if (pv->close_sent && pv->close_received)
Packit Service f69f0c
		return;
Packit Service f69f0c
Packit Service ca3877
	if (control) {
Packit Service ca3877
		/* Control frames must never be fragmented */
Packit Service ca3877
		if (!fin) {
Packit Service ca3877
			g_debug ("received fragmented control frame");
Packit Service ca3877
			protocol_error_and_close (self);
Packit Service ca3877
			return;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		g_debug ("received control frame %d with %d payload", (int)opcode, (int)payload_len);
Packit Service ca3877
Packit Service ca3877
		switch (opcode) {
Packit Service ca3877
		case 0x08:
Packit Service ca3877
			receive_close (self, payload, payload_len);
Packit Service ca3877
			break;
Packit Service ca3877
		case 0x09:
Packit Service ca3877
			receive_ping (self, payload, payload_len);
Packit Service ca3877
			break;
Packit Service ca3877
		case 0x0A:
Packit Service ca3877
			receive_pong (self, payload, payload_len);
Packit Service ca3877
			break;
Packit Service ca3877
		default:
Packit Service ca3877
			g_debug ("received unsupported control frame: %d", (int)opcode);
Packit Service ca3877
			protocol_error_and_close (self);
Packit Service ca3877
			return;
Packit Service ca3877
		}
Packit Service ca3877
	} else if (pv->close_received) {
Packit Service ca3877
		g_debug ("received message after close was received");
Packit Service ca3877
	} else {
Packit Service ca3877
		/* A message frame */
Packit Service ca3877
Packit Service ca3877
		if (!fin && opcode) {
Packit Service ca3877
			/* Initial fragment of a message */
Packit Service ca3877
			if (pv->message_data) {
Packit Service ca3877
				g_debug ("received out of order initial message fragment");
Packit Service ca3877
				protocol_error_and_close (self);
Packit Service ca3877
				return;
Packit Service ca3877
			}
Packit Service ca3877
			g_debug ("received initial fragment frame %d with %d payload", (int)opcode, (int)payload_len);
Packit Service ca3877
		} else if (!fin && !opcode) {
Packit Service ca3877
			/* Middle fragment of a message */
Packit Service ca3877
			if (!pv->message_data) {
Packit Service ca3877
				g_debug ("received out of order middle message fragment");
Packit Service ca3877
				protocol_error_and_close (self);
Packit Service ca3877
				return;
Packit Service ca3877
			}
Packit Service ca3877
			g_debug ("received middle fragment frame with %d payload", (int)payload_len);
Packit Service ca3877
		} else if (fin && !opcode) {
Packit Service ca3877
			/* Last fragment of a message */
Packit Service ca3877
			if (!pv->message_data) {
Packit Service ca3877
				g_debug ("received out of order ending message fragment");
Packit Service ca3877
				protocol_error_and_close (self);
Packit Service ca3877
				return;
Packit Service ca3877
			}
Packit Service ca3877
			g_debug ("received last fragment frame with %d payload", (int)payload_len);
Packit Service ca3877
		} else {
Packit Service ca3877
			/* An unfragmented message */
Packit Service ca3877
			g_assert (opcode != 0);
Packit Service ca3877
			if (pv->message_data) {
Packit Service ca3877
				g_debug ("received unfragmented message when fragment was expected");
Packit Service ca3877
				protocol_error_and_close (self);
Packit Service ca3877
				return;
Packit Service ca3877
			}
Packit Service ca3877
			g_debug ("received frame %d with %d payload", (int)opcode, (int)payload_len);
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		if (opcode) {
Packit Service ca3877
			pv->message_opcode = opcode;
Packit Service ca3877
			pv->message_data = g_byte_array_sized_new (payload_len + 1);
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		switch (pv->message_opcode) {
Packit Service ca3877
		case 0x01:
Packit Service ca3877
		case 0x02:
Packit Service ca3877
			g_byte_array_append (pv->message_data, payload, payload_len);
Packit Service ca3877
			break;
Packit Service ca3877
		default:
Packit Service ca3877
			g_debug ("received unknown data frame: %d", (int)opcode);
Packit Service ca3877
			protocol_error_and_close (self);
Packit Service ca3877
			return;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		/* Actually deliver the message? */
Packit Service ca3877
		if (fin) {
Packit Service ca3877
			if (pv->message_opcode == 0x01 &&
Packit Service e0d935
			    !utf8_validate((const char *)pv->message_data->data,
Packit Service e0d935
					   pv->message_data->len)) {
Packit Service ca3877
Packit Service ca3877
				g_debug ("received invalid non-UTF8 text data");
Packit Service ca3877
Packit Service ca3877
				/* Discard the entire message */
Packit Service ca3877
				g_byte_array_unref (pv->message_data);
Packit Service ca3877
				pv->message_data = NULL;
Packit Service ca3877
				pv->message_opcode = 0;
Packit Service ca3877
Packit Service ca3877
				bad_data_error_and_close (self);
Packit Service ca3877
				return;
Packit Service ca3877
			}
Packit Service ca3877
Packit Service ca3877
			/* Always null terminate, as a convenience */
Packit Service ca3877
			g_byte_array_append (pv->message_data, (guchar *)"\0", 1);
Packit Service ca3877
Packit Service ca3877
			/* But don't include the null terminator in the byte count */
Packit Service ca3877
			pv->message_data->len--;
Packit Service ca3877
Packit Service ca3877
			opcode = pv->message_opcode;
Packit Service ca3877
			message = g_byte_array_free_to_bytes (pv->message_data);
Packit Service ca3877
			pv->message_data = NULL;
Packit Service ca3877
			pv->message_opcode = 0;
Packit Service ca3877
			g_debug ("message: delivering %d with %d length",
Packit Service ca3877
				 (int)opcode, (int)g_bytes_get_size (message));
Packit Service ca3877
			g_signal_emit (self, signals[MESSAGE], 0, (int)opcode, message);
Packit Service ca3877
			g_bytes_unref (message);
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
process_frame (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	guint8 *header;
Packit Service ca3877
	guint8 *payload;
Packit Service ca3877
	guint64 payload_len;
Packit Service ca3877
	guint8 *mask;
Packit Service ca3877
	gboolean fin;
Packit Service ca3877
	gboolean control;
Packit Service ca3877
	gboolean masked;
Packit Service ca3877
	guint8 opcode;
Packit Service ca3877
	gsize len;
Packit Service ca3877
	gsize at;
Packit Service ca3877
Packit Service ca3877
	len = self->pv->incoming->len;
Packit Service ca3877
	if (len < 2)
Packit Service ca3877
		return FALSE; /* need more data */
Packit Service ca3877
Packit Service ca3877
	header = self->pv->incoming->data;
Packit Service ca3877
	fin = ((header[0] & 0x80) != 0);
Packit Service ca3877
	control = header[0] & 0x08;
Packit Service ca3877
	opcode = header[0] & 0x0f;
Packit Service ca3877
	masked = ((header[1] & 0x80) != 0);
Packit Service ca3877
Packit Service ca3877
	/* We do not support extensions, reserved bits must be 0 */
Packit Service ca3877
	if (header[0] & 0x70) {
Packit Service ca3877
		protocol_error_and_close (self);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	switch (header[1] & 0x7f) {
Packit Service ca3877
	case 126:
Packit Service ca3877
		at = 4;
Packit Service ca3877
		if (len < at)
Packit Service ca3877
			return FALSE; /* need more data */
Packit Service ca3877
		payload_len = (((guint16)header[2] << 8) |
Packit Service ca3877
			       ((guint16)header[3] << 0));
Packit Service ca3877
		break;
Packit Service ca3877
	case 127:
Packit Service ca3877
		at = 10;
Packit Service ca3877
		if (len < at)
Packit Service ca3877
			return FALSE; /* need more data */
Packit Service ca3877
		payload_len = (((guint64)header[2] << 56) |
Packit Service ca3877
			       ((guint64)header[3] << 48) |
Packit Service ca3877
			       ((guint64)header[4] << 40) |
Packit Service ca3877
			       ((guint64)header[5] << 32) |
Packit Service ca3877
			       ((guint64)header[6] << 24) |
Packit Service ca3877
			       ((guint64)header[7] << 16) |
Packit Service ca3877
			       ((guint64)header[8] << 8) |
Packit Service ca3877
			       ((guint64)header[9] << 0));
Packit Service ca3877
		break;
Packit Service ca3877
	default:
Packit Service ca3877
		payload_len = header[1] & 0x7f;
Packit Service ca3877
		at = 2;
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* Safety valve */
Packit Service ca3877
	if (self->pv->max_incoming_payload_size > 0 &&
Packit Service ca3877
	    payload_len >= self->pv->max_incoming_payload_size) {
Packit Service ca3877
		too_big_error_and_close (self, payload_len);
Packit Service ca3877
		return FALSE;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (len < at + payload_len)
Packit Service ca3877
		return FALSE; /* need more data */
Packit Service ca3877
Packit Service ca3877
	payload = header + at;
Packit Service ca3877
Packit Service ca3877
	if (masked) {
Packit Service ca3877
		mask = header + at;
Packit Service ca3877
		payload += 4;
Packit Service ca3877
		at += 4;
Packit Service ca3877
Packit Service ca3877
		if (len < at + payload_len)
Packit Service ca3877
			return FALSE; /* need more data */
Packit Service ca3877
Packit Service ca3877
		xor_with_mask (mask, payload, payload_len);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* Note that now that we've unmasked, we've modified the buffer, we can
Packit Service ca3877
	 * only return below via discarding or processing the message
Packit Service ca3877
	 */
Packit Service ca3877
	process_contents (self, control, fin, opcode, payload, payload_len);
Packit Service ca3877
Packit Service ca3877
	/* Move past the parsed frame */
Packit Service ca3877
	g_byte_array_remove_range (self->pv->incoming, 0, at + payload_len);
Packit Service ca3877
	return TRUE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
process_incoming (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	while (process_frame (self))
Packit Service ca3877
		;
Packit Service ca3877
}
Packit Service ca3877
Packit Service f07534
static void
Packit Service f07534
soup_websocket_connection_read (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	GError *error = NULL;
Packit Service ca3877
	gboolean end = FALSE;
Packit Service ca3877
	gssize count;
Packit Service ca3877
	gsize len;
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_stop_input_source (self);
Packit Service f07534
Packit Service ca3877
	do {
Packit Service ca3877
		len = pv->incoming->len;
Packit Service f07534
		g_byte_array_set_size (pv->incoming, len + READ_BUFFER_SIZE);
Packit Service ca3877
Packit Service ca3877
		count = g_pollable_input_stream_read_nonblocking (pv->input,
Packit Service ca3877
								  pv->incoming->data + len,
Packit Service f07534
								  READ_BUFFER_SIZE, NULL, &error);
Packit Service ca3877
		if (count < 0) {
Packit Service ca3877
			if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
Packit Service ca3877
				g_error_free (error);
Packit Service ca3877
				count = 0;
Packit Service ca3877
			} else {
Packit Service ca3877
				emit_error_and_close (self, error, TRUE);
Packit Service f07534
				return;
Packit Service ca3877
			}
Packit Service ca3877
		} else if (count == 0) {
Packit Service ca3877
			end = TRUE;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		pv->incoming->len = len + count;
Packit Service ca3877
	} while (count > 0);
Packit Service ca3877
Packit Service ca3877
	process_incoming (self);
Packit Service ca3877
Packit Service ca3877
	if (end) {
Packit Service ca3877
		if (!pv->close_sent || !pv->close_received) {
Packit Service ca3877
			pv->dirty_close = TRUE;
Packit Service ca3877
			g_debug ("connection unexpectedly closed by peer");
Packit Service ca3877
		} else {
Packit Service ca3877
			g_debug ("peer has closed socket");
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		close_io_stream (self);
Packit Service f07534
		return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_start_input_source (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service f07534
on_web_socket_input (GObject *pollable_stream,
Packit Service f07534
		     gpointer user_data)
Packit Service f07534
{
Packit Service f07534
	soup_websocket_connection_read (SOUP_WEBSOCKET_CONNECTION (user_data));
Packit Service f07534
Packit Service f07534
	return G_SOURCE_REMOVE;
Packit Service f07534
}
Packit Service f07534
Packit Service f07534
static void
Packit Service f07534
soup_websocket_connection_write (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	const guint8 *data;
Packit Service ca3877
	GError *error = NULL;
Packit Service ca3877
	Frame *frame;
Packit Service ca3877
	gssize count;
Packit Service ca3877
	gsize len;
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_stop_output_source (self);
Packit Service f07534
Packit Service ca3877
	if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
Packit Service ca3877
		g_debug ("Ignoring message since the connection is closed");
Packit Service f07534
		return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	frame = g_queue_peek_head (&pv->outgoing);
Packit Service ca3877
Packit Service ca3877
	/* No more frames to send */
Packit Service f07534
	if (frame == NULL)
Packit Service f07534
		return;
Packit Service ca3877
Packit Service ca3877
	data = g_bytes_get_data (frame->data, &len;;
Packit Service ca3877
	g_assert (len > 0);
Packit Service ca3877
	g_assert (len > frame->sent);
Packit Service ca3877
Packit Service ca3877
	count = g_pollable_output_stream_write_nonblocking (pv->output,
Packit Service ca3877
							    data + frame->sent,
Packit Service ca3877
							    len - frame->sent,
Packit Service ca3877
							    NULL, &error);
Packit Service ca3877
Packit Service ca3877
	if (count < 0) {
Packit Service ca3877
		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
Packit Service ca3877
			g_clear_error (&error);
Packit Service ca3877
			count = 0;
Packit Service ca3877
Packit Service ca3877
			g_debug ("failed to send frame because it would block, marking as pending");
Packit Service ca3877
			frame->pending = TRUE;
Packit Service ca3877
		} else {
Packit Service ca3877
			emit_error_and_close (self, error, TRUE);
Packit Service f07534
			return;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	frame->sent += count;
Packit Service ca3877
	if (frame->sent >= len) {
Packit Service ca3877
		g_debug ("sent frame");
Packit Service ca3877
		g_queue_pop_head (&pv->outgoing);
Packit Service ca3877
Packit Service ca3877
		if (frame->flags & SOUP_WEBSOCKET_QUEUE_LAST) {
Packit Service ca3877
			if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER) {
Packit Service ca3877
				close_io_stream (self);
Packit Service ca3877
			} else {
Packit Service ca3877
				shutdown_wr_io_stream (self);
Packit Service ca3877
				close_io_after_timeout (self);
Packit Service ca3877
			}
Packit Service ca3877
		}
Packit Service ca3877
		frame_free (frame);
Packit Service f07534
Packit Service f07534
		if (g_queue_is_empty (&pv->outgoing))
Packit Service f07534
			return;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_start_output_source (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service f07534
static gboolean
Packit Service f07534
on_web_socket_output (GObject *pollable_stream,
Packit Service f07534
		      gpointer user_data)
Packit Service ca3877
{
Packit Service f07534
	soup_websocket_connection_write (SOUP_WEBSOCKET_CONNECTION (user_data));
Packit Service ca3877
Packit Service f07534
	return G_SOURCE_REMOVE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
queue_frame (SoupWebsocketConnection *self,
Packit Service ca3877
	     SoupWebsocketQueueFlags flags,
Packit Service ca3877
	     gpointer data,
Packit Service ca3877
	     gsize len,
Packit Service ca3877
	     gsize amount)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	Frame *frame;
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
Packit Service ca3877
	g_return_if_fail (pv->close_sent == FALSE);
Packit Service ca3877
	g_return_if_fail (data != NULL);
Packit Service ca3877
	g_return_if_fail (len > 0);
Packit Service ca3877
Packit Service ca3877
	frame = g_slice_new0 (Frame);
Packit Service ca3877
	frame->data = g_bytes_new_take (data, len);
Packit Service ca3877
	frame->amount = amount;
Packit Service ca3877
	frame->flags = flags;
Packit Service ca3877
Packit Service ca3877
	/* If urgent put at front of queue */
Packit Service ca3877
	if (flags & SOUP_WEBSOCKET_QUEUE_URGENT) {
Packit Service ca3877
		GList *l;
Packit Service ca3877
Packit Service ca3877
		/* Find out the first frame that is not urgent or partially sent or pending */
Packit Service ca3877
		for (l = g_queue_peek_head_link (&pv->outgoing); l != NULL; l = l->next) {
Packit Service ca3877
			Frame *prev = l->data;
Packit Service ca3877
Packit Service ca3877
			if (!(prev->flags & SOUP_WEBSOCKET_QUEUE_URGENT) &&
Packit Service ca3877
			    prev->sent == 0 && !prev->pending)
Packit Service ca3877
				break;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		g_queue_insert_before (&pv->outgoing, l, frame);
Packit Service ca3877
	} else {
Packit Service ca3877
		g_queue_push_tail (&pv->outgoing, frame);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_write (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_constructed (GObject *object)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
	GInputStream *is;
Packit Service ca3877
	GOutputStream *os;
Packit Service ca3877
Packit Service ca3877
	G_OBJECT_CLASS (soup_websocket_connection_parent_class)->constructed (object);
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (pv->io_stream != NULL);
Packit Service ca3877
Packit Service ca3877
	is = g_io_stream_get_input_stream (pv->io_stream);
Packit Service ca3877
	g_return_if_fail (G_IS_POLLABLE_INPUT_STREAM (is));
Packit Service ca3877
	pv->input = G_POLLABLE_INPUT_STREAM (is);
Packit Service ca3877
	g_return_if_fail (g_pollable_input_stream_can_poll (pv->input));
Packit Service ca3877
Packit Service ca3877
	os = g_io_stream_get_output_stream (pv->io_stream);
Packit Service ca3877
	g_return_if_fail (G_IS_POLLABLE_OUTPUT_STREAM (os));
Packit Service ca3877
	pv->output = G_POLLABLE_OUTPUT_STREAM (os);
Packit Service ca3877
	g_return_if_fail (g_pollable_output_stream_can_poll (pv->output));
Packit Service ca3877
Packit Service f07534
	soup_websocket_connection_start_input_source (self);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_get_property (GObject *object,
Packit Service ca3877
					guint prop_id,
Packit Service ca3877
					GValue *value,
Packit Service ca3877
					GParamSpec *pspec)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	switch (prop_id) {
Packit Service ca3877
	case PROP_IO_STREAM:
Packit Service ca3877
		g_value_set_object (value, soup_websocket_connection_get_io_stream (self));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_CONNECTION_TYPE:
Packit Service ca3877
		g_value_set_enum (value, soup_websocket_connection_get_connection_type (self));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_URI:
Packit Service ca3877
		g_value_set_boxed (value, soup_websocket_connection_get_uri (self));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_ORIGIN:
Packit Service ca3877
		g_value_set_string (value, soup_websocket_connection_get_origin (self));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_PROTOCOL:
Packit Service ca3877
		g_value_set_string (value, soup_websocket_connection_get_protocol (self));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_STATE:
Packit Service ca3877
		g_value_set_enum (value, soup_websocket_connection_get_state (self));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_MAX_INCOMING_PAYLOAD_SIZE:
Packit Service ca3877
		g_value_set_uint64 (value, pv->max_incoming_payload_size);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_KEEPALIVE_INTERVAL:
Packit Service ca3877
		g_value_set_uint (value, pv->keepalive_interval);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	default:
Packit Service ca3877
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_set_property (GObject *object,
Packit Service ca3877
					guint prop_id,
Packit Service ca3877
					const GValue *value,
Packit Service ca3877
					GParamSpec *pspec)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	switch (prop_id) {
Packit Service ca3877
	case PROP_IO_STREAM:
Packit Service ca3877
		g_return_if_fail (pv->io_stream == NULL);
Packit Service ca3877
		pv->io_stream = g_value_dup_object (value);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_CONNECTION_TYPE:
Packit Service ca3877
		pv->connection_type = g_value_get_enum (value);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_URI:
Packit Service ca3877
		g_return_if_fail (pv->uri == NULL);
Packit Service ca3877
		pv->uri = g_value_dup_boxed (value);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_ORIGIN:
Packit Service ca3877
		g_return_if_fail (pv->origin == NULL);
Packit Service ca3877
		pv->origin = g_value_dup_string (value);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_PROTOCOL:
Packit Service ca3877
		g_return_if_fail (pv->protocol == NULL);
Packit Service ca3877
		pv->protocol = g_value_dup_string (value);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_MAX_INCOMING_PAYLOAD_SIZE:
Packit Service ca3877
		pv->max_incoming_payload_size = g_value_get_uint64 (value);
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	case PROP_KEEPALIVE_INTERVAL:
Packit Service ca3877
		soup_websocket_connection_set_keepalive_interval (self,
Packit Service ca3877
		                                                  g_value_get_uint (value));
Packit Service ca3877
		break;
Packit Service ca3877
Packit Service ca3877
	default:
Packit Service ca3877
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_dispose (GObject *object)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
Packit Service ca3877
Packit Service ca3877
	self->pv->dirty_close = TRUE;
Packit Service ca3877
	close_io_stream (self);
Packit Service ca3877
Packit Service ca3877
	G_OBJECT_CLASS (soup_websocket_connection_parent_class)->dispose (object);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_finalize (GObject *object)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (object);
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	g_free (pv->peer_close_data);
Packit Service ca3877
Packit Service ca3877
	g_main_context_unref (pv->main_context);
Packit Service ca3877
Packit Service ca3877
	if (pv->incoming)
Packit Service ca3877
		g_byte_array_free (pv->incoming, TRUE);
Packit Service ca3877
	while (!g_queue_is_empty (&pv->outgoing))
Packit Service ca3877
		frame_free (g_queue_pop_head (&pv->outgoing));
Packit Service ca3877
Packit Service ca3877
	g_clear_object (&pv->io_stream);
Packit Service ca3877
	g_assert (!pv->input_source);
Packit Service ca3877
	g_assert (!pv->output_source);
Packit Service ca3877
	g_assert (pv->io_closing);
Packit Service ca3877
	g_assert (pv->io_closed);
Packit Service ca3877
	g_assert (!pv->close_timeout);
Packit Service ca3877
	g_assert (!pv->keepalive_timeout);
Packit Service ca3877
Packit Service ca3877
	if (pv->message_data)
Packit Service ca3877
		g_byte_array_free (pv->message_data, TRUE);
Packit Service ca3877
Packit Service ca3877
	if (pv->uri)
Packit Service ca3877
		soup_uri_free (pv->uri);
Packit Service ca3877
	g_free (pv->origin);
Packit Service ca3877
	g_free (pv->protocol);
Packit Service ca3877
Packit Service ca3877
	G_OBJECT_CLASS (soup_websocket_connection_parent_class)->finalize (object);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_websocket_connection_class_init (SoupWebsocketConnectionClass *klass)
Packit Service ca3877
{
Packit Service ca3877
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit Service ca3877
Packit Service ca3877
	gobject_class->constructed = soup_websocket_connection_constructed;
Packit Service ca3877
	gobject_class->get_property = soup_websocket_connection_get_property;
Packit Service ca3877
	gobject_class->set_property = soup_websocket_connection_set_property;
Packit Service ca3877
	gobject_class->dispose = soup_websocket_connection_dispose;
Packit Service ca3877
	gobject_class->finalize = soup_websocket_connection_finalize;
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:io-stream:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The underlying IO stream the WebSocket is communicating
Packit Service ca3877
	 * over.
Packit Service ca3877
	 *
Packit Service ca3877
	 * The input and output streams must be pollable streams.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_IO_STREAM,
Packit Service ca3877
					 g_param_spec_object ("io-stream",
Packit Service ca3877
							      "I/O Stream",
Packit Service ca3877
							      "Underlying I/O stream",
Packit Service ca3877
							      G_TYPE_IO_STREAM,
Packit Service ca3877
							      G_PARAM_READWRITE |
Packit Service ca3877
							      G_PARAM_CONSTRUCT_ONLY |
Packit Service ca3877
							      G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:connection-type:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The type of connection (client/server).
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_CONNECTION_TYPE,
Packit Service ca3877
					 g_param_spec_enum ("connection-type",
Packit Service ca3877
							    "Connection type",
Packit Service ca3877
							    "Connection type (client/server)",
Packit Service ca3877
							    SOUP_TYPE_WEBSOCKET_CONNECTION_TYPE,
Packit Service ca3877
							    SOUP_WEBSOCKET_CONNECTION_UNKNOWN,
Packit Service ca3877
							    G_PARAM_READWRITE |
Packit Service ca3877
							    G_PARAM_CONSTRUCT_ONLY |
Packit Service ca3877
							    G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:uri:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The URI of the WebSocket.
Packit Service ca3877
	 *
Packit Service ca3877
	 * For servers this represents the address of the WebSocket,
Packit Service ca3877
	 * and for clients it is the address connected to.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_URI,
Packit Service ca3877
					 g_param_spec_boxed ("uri",
Packit Service ca3877
							     "URI",
Packit Service ca3877
							     "The WebSocket URI",
Packit Service ca3877
							     SOUP_TYPE_URI,
Packit Service ca3877
							     G_PARAM_READWRITE |
Packit Service ca3877
							     G_PARAM_CONSTRUCT_ONLY |
Packit Service ca3877
							     G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:origin:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The client's Origin.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_ORIGIN,
Packit Service ca3877
					 g_param_spec_string ("origin",
Packit Service ca3877
							      "Origin",
Packit Service ca3877
							      "The WebSocket origin",
Packit Service ca3877
							      NULL,
Packit Service ca3877
							      G_PARAM_READWRITE |
Packit Service ca3877
							      G_PARAM_CONSTRUCT_ONLY |
Packit Service ca3877
							      G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:protocol:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The chosen protocol, or %NULL if a protocol was not agreed
Packit Service ca3877
	 * upon.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_PROTOCOL,
Packit Service ca3877
					 g_param_spec_string ("protocol",
Packit Service ca3877
							      "Protocol",
Packit Service ca3877
							      "The chosen WebSocket protocol",
Packit Service ca3877
							      NULL,
Packit Service ca3877
							      G_PARAM_READWRITE |
Packit Service ca3877
							      G_PARAM_CONSTRUCT_ONLY |
Packit Service ca3877
							      G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:state:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The current state of the WebSocket.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_STATE,
Packit Service ca3877
					 g_param_spec_enum ("state",
Packit Service ca3877
							    "State",
Packit Service ca3877
							    "State ",
Packit Service ca3877
							    SOUP_TYPE_WEBSOCKET_STATE,
Packit Service ca3877
							    SOUP_WEBSOCKET_STATE_OPEN,
Packit Service ca3877
							    G_PARAM_READABLE |
Packit Service ca3877
							    G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:max-incoming-payload-size:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The maximum payload size for incoming packets the protocol expects
Packit Service ca3877
	 * or 0 to not limit it.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.56
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_MAX_INCOMING_PAYLOAD_SIZE,
Packit Service ca3877
					 g_param_spec_uint64 ("max-incoming-payload-size",
Packit Service ca3877
							      "Max incoming payload size",
Packit Service ca3877
							      "Max incoming payload size ",
Packit Service ca3877
							      0,
Packit Service ca3877
							      G_MAXUINT64,
Packit Service ca3877
							      MAX_INCOMING_PAYLOAD_SIZE_DEFAULT,
Packit Service ca3877
							      G_PARAM_READWRITE |
Packit Service ca3877
							      G_PARAM_CONSTRUCT |
Packit Service ca3877
							      G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection:keepalive-interval:
Packit Service ca3877
	 *
Packit Service ca3877
	 * Interval in seconds on when to send a ping message which will
Packit Service ca3877
	 * serve as a keepalive message. If set to 0 the keepalive message is
Packit Service ca3877
	 * disabled.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.58
Packit Service ca3877
	 */
Packit Service ca3877
	g_object_class_install_property (gobject_class, PROP_KEEPALIVE_INTERVAL,
Packit Service ca3877
					 g_param_spec_uint ("keepalive-interval",
Packit Service ca3877
					                    "Keepalive interval",
Packit Service ca3877
					                    "Keepalive interval",
Packit Service ca3877
					                    0,
Packit Service ca3877
					                    G_MAXUINT,
Packit Service ca3877
					                    0,
Packit Service ca3877
					                    G_PARAM_READWRITE |
Packit Service ca3877
					                    G_PARAM_CONSTRUCT |
Packit Service ca3877
					                    G_PARAM_STATIC_STRINGS));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection::message:
Packit Service ca3877
	 * @self: the WebSocket
Packit Service ca3877
	 * @type: the type of message contents
Packit Service ca3877
	 * @message: the message data
Packit Service ca3877
	 *
Packit Service ca3877
	 * Emitted when we receive a message from the peer.
Packit Service ca3877
	 *
Packit Service ca3877
	 * As a convenience, the @message data will always be
Packit Service ca3877
	 * NUL-terminated, but the NUL byte will not be included in
Packit Service ca3877
	 * the length count.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	signals[MESSAGE] = g_signal_new ("message",
Packit Service ca3877
					 SOUP_TYPE_WEBSOCKET_CONNECTION,
Packit Service ca3877
					 G_SIGNAL_RUN_FIRST,
Packit Service ca3877
					 G_STRUCT_OFFSET (SoupWebsocketConnectionClass, message),
Packit Service ca3877
					 NULL, NULL, g_cclosure_marshal_generic,
Packit Service ca3877
					 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_BYTES);
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection::error:
Packit Service ca3877
	 * @self: the WebSocket
Packit Service ca3877
	 * @error: the error that occured
Packit Service ca3877
	 *
Packit Service ca3877
	 * Emitted when an error occurred on the WebSocket. This may
Packit Service ca3877
	 * be fired multiple times. Fatal errors will be followed by
Packit Service ca3877
	 * the #SoupWebsocketConnection::closed signal being emitted.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	signals[ERROR] = g_signal_new ("error",
Packit Service ca3877
				       SOUP_TYPE_WEBSOCKET_CONNECTION,
Packit Service ca3877
				       G_SIGNAL_RUN_FIRST,
Packit Service ca3877
				       G_STRUCT_OFFSET (SoupWebsocketConnectionClass, error),
Packit Service ca3877
				       NULL, NULL, g_cclosure_marshal_generic,
Packit Service ca3877
				       G_TYPE_NONE, 1, G_TYPE_ERROR);
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection::closing:
Packit Service ca3877
	 * @self: the WebSocket
Packit Service ca3877
	 *
Packit Service ca3877
	 * This signal will be emitted during an orderly close.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	signals[CLOSING] = g_signal_new ("closing",
Packit Service ca3877
					 SOUP_TYPE_WEBSOCKET_CONNECTION,
Packit Service ca3877
					 G_SIGNAL_RUN_LAST,
Packit Service ca3877
					 G_STRUCT_OFFSET (SoupWebsocketConnectionClass, closing),
Packit Service ca3877
					 NULL, NULL, g_cclosure_marshal_generic,
Packit Service ca3877
					 G_TYPE_NONE, 0);
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection::closed:
Packit Service ca3877
	 * @self: the WebSocket
Packit Service ca3877
	 *
Packit Service ca3877
	 * Emitted when the connection has completely closed, either
Packit Service ca3877
	 * due to an orderly close from the peer, one initiated via
Packit Service ca3877
	 * soup_websocket_connection_close() or a fatal error
Packit Service ca3877
	 * condition that caused a close.
Packit Service ca3877
	 *
Packit Service ca3877
	 * This signal will be emitted once.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.50
Packit Service ca3877
	 */
Packit Service ca3877
	signals[CLOSED] = g_signal_new ("closed",
Packit Service ca3877
					SOUP_TYPE_WEBSOCKET_CONNECTION,
Packit Service ca3877
					G_SIGNAL_RUN_FIRST,
Packit Service ca3877
					G_STRUCT_OFFSET (SoupWebsocketConnectionClass, closed),
Packit Service ca3877
					NULL, NULL, g_cclosure_marshal_generic,
Packit Service ca3877
					G_TYPE_NONE, 0);
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupWebsocketConnection::pong:
Packit Service ca3877
	 * @self: the WebSocket
Packit Service ca3877
	 * @message: the application data (if any)
Packit Service ca3877
	 *
Packit Service ca3877
	 * Emitted when we receive a Pong frame (solicited or
Packit Service ca3877
	 * unsolicited) from the peer.
Packit Service ca3877
	 *
Packit Service ca3877
	 * As a convenience, the @message data will always be
Packit Service ca3877
	 * NUL-terminated, but the NUL byte will not be included in
Packit Service ca3877
	 * the length count.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.60
Packit Service ca3877
	 */
Packit Service ca3877
	signals[PONG] = g_signal_new ("pong",
Packit Service ca3877
				      SOUP_TYPE_WEBSOCKET_CONNECTION,
Packit Service ca3877
				      G_SIGNAL_RUN_FIRST,
Packit Service ca3877
				      G_STRUCT_OFFSET (SoupWebsocketConnectionClass, pong),
Packit Service ca3877
				      NULL, NULL, g_cclosure_marshal_generic,
Packit Service ca3877
				      G_TYPE_NONE, 1, G_TYPE_BYTES);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_new:
Packit Service ca3877
 * @stream: a #GIOStream connected to the WebSocket server
Packit Service ca3877
 * @uri: the URI of the connection
Packit Service ca3877
 * @type: the type of connection (client/side)
Packit Service ca3877
 * @origin: (allow-none): the Origin of the client
Packit Service ca3877
 * @protocol: (allow-none): the subprotocol in use
Packit Service ca3877
 *
Packit Service ca3877
 * Creates a #SoupWebsocketConnection on @stream. This should be
Packit Service ca3877
 * called after completing the handshake to begin using the WebSocket
Packit Service ca3877
 * protocol.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: a new #SoupWebsocketConnection
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
SoupWebsocketConnection *
Packit Service ca3877
soup_websocket_connection_new (GIOStream                    *stream,
Packit Service ca3877
			       SoupURI                      *uri,
Packit Service ca3877
			       SoupWebsocketConnectionType   type,
Packit Service ca3877
			       const char                   *origin,
Packit Service ca3877
			       const char                   *protocol)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (G_IS_IO_STREAM (stream), NULL);
Packit Service ca3877
	g_return_val_if_fail (uri != NULL, NULL);
Packit Service ca3877
	g_return_val_if_fail (type != SOUP_WEBSOCKET_CONNECTION_UNKNOWN, NULL);
Packit Service ca3877
Packit Service ca3877
	return g_object_new (SOUP_TYPE_WEBSOCKET_CONNECTION,
Packit Service ca3877
			     "io-stream", stream,
Packit Service ca3877
			     "uri", uri,
Packit Service ca3877
			     "connection-type", type,
Packit Service ca3877
			     "origin", origin,
Packit Service ca3877
			     "protocol", protocol,
Packit Service ca3877
			     NULL);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_io_stream:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the I/O stream the WebSocket is communicating over.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: (transfer none): the WebSocket's I/O stream.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
GIOStream *
Packit Service ca3877
soup_websocket_connection_get_io_stream (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
Packit Service ca3877
Packit Service ca3877
	return self->pv->io_stream;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_connection_type:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the connection type (client/server) of the connection.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: the connection type
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
SoupWebsocketConnectionType
Packit Service ca3877
soup_websocket_connection_get_connection_type (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), SOUP_WEBSOCKET_CONNECTION_UNKNOWN);
Packit Service ca3877
Packit Service ca3877
	return self->pv->connection_type;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_uri:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the URI of the WebSocket.
Packit Service ca3877
 *
Packit Service ca3877
 * For servers this represents the address of the WebSocket, and
Packit Service ca3877
 * for clients it is the address connected to.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: (transfer none): the URI
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
SoupURI *
Packit Service ca3877
soup_websocket_connection_get_uri (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
Packit Service ca3877
Packit Service ca3877
	return self->pv->uri;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_origin:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the origin of the WebSocket.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: (nullable): the origin, or %NULL
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
const char *
Packit Service ca3877
soup_websocket_connection_get_origin (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
Packit Service ca3877
Packit Service ca3877
	return self->pv->origin;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_protocol:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the protocol chosen via negotiation with the peer.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: (nullable): the chosen protocol, or %NULL
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
const char *
Packit Service ca3877
soup_websocket_connection_get_protocol (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
Packit Service ca3877
Packit Service ca3877
	return self->pv->protocol;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_state:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the current state of the WebSocket.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: the state
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
SoupWebsocketState
Packit Service ca3877
soup_websocket_connection_get_state (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
Packit Service ca3877
Packit Service ca3877
	if (self->pv->io_closed)
Packit Service ca3877
		return SOUP_WEBSOCKET_STATE_CLOSED;
Packit Service ca3877
	else if (self->pv->io_closing || self->pv->close_sent)
Packit Service ca3877
		return SOUP_WEBSOCKET_STATE_CLOSING;
Packit Service ca3877
	else
Packit Service ca3877
		return SOUP_WEBSOCKET_STATE_OPEN;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_close_code:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the close code received from the WebSocket peer.
Packit Service ca3877
 *
Packit Service ca3877
 * This only becomes valid once the WebSocket is in the
Packit Service ca3877
 * %SOUP_WEBSOCKET_STATE_CLOSED state. The value will often be in the
Packit Service ca3877
 * #SoupWebsocketCloseCode enumeration, but may also be an application
Packit Service ca3877
 * defined close code.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: the close code or zero.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
gushort
Packit Service ca3877
soup_websocket_connection_get_close_code (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
Packit Service ca3877
Packit Service ca3877
	return self->pv->peer_close_code;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_close_data:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Get the close data received from the WebSocket peer.
Packit Service ca3877
 *
Packit Service ca3877
 * This only becomes valid once the WebSocket is in the
Packit Service ca3877
 * %SOUP_WEBSOCKET_STATE_CLOSED state. The data may be freed once
Packit Service ca3877
 * the main loop is run, so copy it if you need to keep it around.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: the close data or %NULL
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
const char *
Packit Service ca3877
soup_websocket_connection_get_close_data (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), NULL);
Packit Service ca3877
Packit Service ca3877
	return self->pv->peer_close_data;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_send_text:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 * @text: the message contents
Packit Service ca3877
 *
Packit Service e0d935
 * Send a %NULL-terminated text (UTF-8) message to the peer. If you need
Packit Service e0d935
 * to send text messages containing %NULL characters use
Packit Service e0d935
 * soup_websocket_connection_send_message() instead.
Packit Service ca3877
 *
Packit Service ca3877
 * The message is queued to be sent and will be sent when the main loop
Packit Service ca3877
 * is run.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
void
Packit Service ca3877
soup_websocket_connection_send_text (SoupWebsocketConnection *self,
Packit Service ca3877
				     const char *text)
Packit Service ca3877
{
Packit Service ca3877
	gsize length;
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
Packit Service ca3877
	g_return_if_fail (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN);
Packit Service ca3877
	g_return_if_fail (text != NULL);
Packit Service ca3877
Packit Service ca3877
	length = strlen (text);
Packit Service e0d935
        g_return_if_fail (utf8_validate (text, length));
Packit Service ca3877
Packit Service ca3877
	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x01, (const guint8 *) text, length);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_send_binary:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 * @data: (array length=length) (element-type guint8): the message contents
Packit Service ca3877
 * @length: the length of @data
Packit Service ca3877
 *
Packit Service ca3877
 * Send a binary message to the peer.
Packit Service ca3877
 *
Packit Service ca3877
 * The message is queued to be sent and will be sent when the main loop
Packit Service ca3877
 * is run.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
void
Packit Service ca3877
soup_websocket_connection_send_binary (SoupWebsocketConnection *self,
Packit Service ca3877
				       gconstpointer data,
Packit Service ca3877
				       gsize length)
Packit Service ca3877
{
Packit Service ca3877
	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
Packit Service ca3877
	g_return_if_fail (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN);
Packit Service ca3877
	g_return_if_fail (data != NULL);
Packit Service ca3877
Packit Service ca3877
	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x02, data, length);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_close:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 * @code: close code
Packit Service ca3877
 * @data: (allow-none): close data
Packit Service ca3877
 *
Packit Service ca3877
 * Close the connection in an orderly fashion.
Packit Service ca3877
 *
Packit Service ca3877
 * Note that until the #SoupWebsocketConnection::closed signal fires, the connection
Packit Service ca3877
 * is not yet completely closed. The close message is not even sent until the
Packit Service ca3877
 * main loop runs.
Packit Service ca3877
 *
Packit Service ca3877
 * The @code and @data are sent to the peer along with the close request.
Packit Service ca3877
 * Note that the @data must be UTF-8 valid.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.50
Packit Service ca3877
 */
Packit Service ca3877
void
Packit Service ca3877
soup_websocket_connection_close (SoupWebsocketConnection *self,
Packit Service ca3877
				 gushort code,
Packit Service ca3877
				 const char *data)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
Packit Service ca3877
	pv = self->pv;
Packit Service ca3877
	g_return_if_fail (!pv->close_sent);
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (code != SOUP_WEBSOCKET_CLOSE_NO_STATUS &&
Packit Service ca3877
			  code != SOUP_WEBSOCKET_CLOSE_ABNORMAL &&
Packit Service ca3877
			  code != SOUP_WEBSOCKET_CLOSE_TLS_HANDSHAKE);
Packit Service ca3877
	if (pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER)
Packit Service ca3877
		g_return_if_fail (code != SOUP_WEBSOCKET_CLOSE_NO_EXTENSION);
Packit Service ca3877
	else
Packit Service ca3877
		g_return_if_fail (code != SOUP_WEBSOCKET_CLOSE_SERVER_ERROR);
Packit Service ca3877
Packit Service ca3877
	close_connection (self, code, data);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_max_incoming_payload_size:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Gets the maximum payload size allowed for incoming packets.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: the maximum payload size.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.56
Packit Service ca3877
 */
Packit Service ca3877
guint64
Packit Service ca3877
soup_websocket_connection_get_max_incoming_payload_size (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), MAX_INCOMING_PAYLOAD_SIZE_DEFAULT);
Packit Service ca3877
	pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	return pv->max_incoming_payload_size;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_set_max_incoming_payload_size:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 * @max_incoming_payload_size: the maximum payload size
Packit Service ca3877
 *
Packit Service ca3877
 * Sets the maximum payload size allowed for incoming packets. It
Packit Service ca3877
 * does not limit the outgoing packet size.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.56
Packit Service ca3877
 */
Packit Service ca3877
void
Packit Service ca3877
soup_websocket_connection_set_max_incoming_payload_size (SoupWebsocketConnection *self,
Packit Service ca3877
                                                         guint64                  max_incoming_payload_size)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
Packit Service ca3877
	pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->max_incoming_payload_size != max_incoming_payload_size) {
Packit Service ca3877
		pv->max_incoming_payload_size = max_incoming_payload_size;
Packit Service ca3877
		g_object_notify (G_OBJECT (self), "max-incoming-payload-size");
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_get_keepalive_interval:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 *
Packit Service ca3877
 * Gets the keepalive interval in seconds or 0 if disabled.
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: the keepalive interval.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.58
Packit Service ca3877
 */
Packit Service ca3877
guint
Packit Service ca3877
soup_websocket_connection_get_keepalive_interval (SoupWebsocketConnection *self)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self), 0);
Packit Service ca3877
	pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	return pv->keepalive_interval;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
on_queue_ping (gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
Packit Service ca3877
	static const char ping_payload[] = "libsoup";
Packit Service ca3877
Packit Service ca3877
	g_debug ("sending ping message");
Packit Service ca3877
Packit Service ca3877
	send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x09,
Packit Service ca3877
		      (guint8 *) ping_payload, strlen(ping_payload));
Packit Service ca3877
Packit Service ca3877
	return G_SOURCE_CONTINUE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_websocket_connection_set_keepalive_interval:
Packit Service ca3877
 * @self: the WebSocket
Packit Service ca3877
 * @interval: the interval to send a ping message or 0 to disable it
Packit Service ca3877
 *
Packit Service ca3877
 * Sets the interval in seconds on when to send a ping message which will serve
Packit Service ca3877
 * as a keepalive message. If set to 0 the keepalive message is disabled.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.58
Packit Service ca3877
 */
Packit Service ca3877
void
Packit Service ca3877
soup_websocket_connection_set_keepalive_interval (SoupWebsocketConnection *self,
Packit Service ca3877
                                                  guint                    interval)
Packit Service ca3877
{
Packit Service ca3877
	SoupWebsocketConnectionPrivate *pv;
Packit Service ca3877
Packit Service ca3877
	g_return_if_fail (SOUP_IS_WEBSOCKET_CONNECTION (self));
Packit Service ca3877
	pv = self->pv;
Packit Service ca3877
Packit Service ca3877
	if (pv->keepalive_interval != interval) {
Packit Service ca3877
		pv->keepalive_interval = interval;
Packit Service ca3877
		g_object_notify (G_OBJECT (self), "keepalive-interval");
Packit Service ca3877
Packit Service ca3877
		keepalive_stop_timeout (self);
Packit Service ca3877
Packit Service ca3877
		if (interval > 0) {
Packit Service ca3877
			pv->keepalive_timeout = g_timeout_source_new_seconds (interval);
Packit Service ca3877
			g_source_set_callback (pv->keepalive_timeout, on_queue_ping, self, NULL);
Packit Service ca3877
			g_source_attach (pv->keepalive_timeout, pv->main_context);
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
}