Commit 13cea0fd authored by Carlos Garcia Campos's avatar Carlos Garcia Campos Committed by Carlos Garcia Campos
Browse files

WebSockets: add support for permessage-deflate extension

Add new API to add WebSocket extensions to SoupSession and SoupServer
and include an implementation of permessage-deflate extension (see RFC
7692). In the client side, supported extensions are added to the session
as sub-features of a new session feature, SoupWebsocketExtensionManager.
In the client side, supported extensions are added/removed directly using
the new SoupServer API. All functions to negotiate the handshake
(client_prepare, client_verify, server_check and server_process) have
now a _with_extensions alternative to handle the extensions.
parent 5c452532
Pipeline #100245 passed with stage
in 44 seconds
......@@ -240,8 +240,12 @@ soup_server_add_handler
soup_server_add_early_handler
soup_server_remove_handler
<SUBSECTION>
SOUP_SERVER_ADD_WEBSOCKET_EXTENSION
SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION
SoupServerWebsocketCallback
soup_server_add_websocket_handler
soup_server_add_websocket_extension
soup_server_remove_websocket_extension
<SUBSECTION>
SoupClientContext
soup_client_context_get_local_address
......@@ -1304,19 +1308,25 @@ SOUP_VERSION_PREV_STABLE
<TITLE>WebSockets</TITLE>
<SUBSECTION>
soup_websocket_client_prepare_handshake
soup_websocket_client_prepare_handshake_with_extensions
soup_websocket_client_verify_handshake
soup_websocket_client_verify_handshake_with_extensions
<SUBSECTION>
soup_websocket_server_check_handshake
soup_websocket_server_check_handshake_with_extensions
soup_websocket_server_process_handshake
soup_websocket_server_process_handshake_with_extensions
<SUBSECTION>
SoupWebsocketConnection
SoupWebsocketConnectionType
soup_websocket_connection_new
soup_websocket_connection_new_with_extensions
soup_websocket_connection_get_io_stream
soup_websocket_connection_get_connection_type
soup_websocket_connection_get_uri
soup_websocket_connection_get_origin
soup_websocket_connection_get_protocol
soup_websocket_connection_get_extensions
SoupWebsocketState
soup_websocket_connection_get_state
SoupWebsocketDataType
......@@ -1328,20 +1338,54 @@ soup_websocket_connection_close
soup_websocket_connection_get_close_code
soup_websocket_connection_get_close_data
<SUBSECTION>
SoupWebsocketExtensionManager
<SUBSECTION>
SoupWebsocketExtension
SoupWebsocketExtensionDeflate
soup_websocket_extension_configure
soup_websocket_extension_get_request_params
soup_websocket_extension_get_response_params
soup_websocket_extension_process_outgoing_message
soup_websocket_extension_process_incoming_message
<SUBSECTION>
SoupWebsocketError
SOUP_WEBSOCKET_ERROR
<SUBSECTION Private>
SoupWebsocketConnectionClass
SoupWebsocketConnectionPrivate
SoupWebsocketExtensionManagerClass
SoupWebsocketExtensionClass
SoupWebsocketExtensionDeflateClass
SOUP_IS_WEBSOCKET_CONNECTION
SOUP_IS_WEBSOCKET_CONNECTION_CLASS
SOUP_TYPE_WEBSOCKET_CONNECTION
SOUP_WEBSOCKET_CONNECTION
SOUP_WEBSOCKET_CONNECTION_CLASS
SOUP_WEBSOCKET_CONNECTION_GET_CLASS
SOUP_IS_WEBSOCKET_EXTENSION_MANAGER
SOUP_IS_WEBSOCKET_EXTENSION_MANAGER_CLASS
SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER
SOUP_WEBSOCKET_EXTENSION_MANAGER
SOUP_WEBSOCKET_EXTENSION_MANAGER_CLASS
SOUP_WEBSOCKET_EXTENSION_MANAGER_GET_CLASS
SOUP_IS_WEBSOCKET_EXTENSION
SOUP_IS_WEBSOCKET_EXTENSION_CLASS
SOUP_TYPE_WEBSOCKET_EXTENSION
SOUP_WEBSOCKET_EXTENSION
SOUP_WEBSOCKET_EXTENSION_CLASS
SOUP_WEBSOCKET_EXTENSION_GET_CLASS
SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE
SOUP_IS_WEBSOCKET_EXTENSION_DEFLATE_CLASS
SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE
SOUP_WEBSOCKET_EXTENSION_DEFLATE
SOUP_WEBSOCKET_EXTENSION_DEFLATE_CLASS
SOUP_WEBSOCKET_EXTENSION_DEFLATE_GET_CLASS
soup_websocket_close_code_get_type
soup_websocket_connection_get_type
soup_websocket_connection_type_get_type
soup_websocket_extension_manager_get_type
soup_websocket_extension_get_type
soup_websocket_extension_deflate_get_type
soup_websocket_data_type_get_type
soup_websocket_error_get_quark
soup_websocket_error_get_type
......
......@@ -40,6 +40,7 @@ ignore_headers = [
'soup-cache-client-input-stream.h',
'soup-socket-private.h',
'soup-value-utils.h',
'soup-websocket-extension-manager-private.h',
'soup-xmlrpc-old.h'
]
......
......@@ -75,6 +75,9 @@ soup_sources = [
'soup-version.c',
'soup-websocket.c',
'soup-websocket-connection.c',
'soup-websocket-extension.c',
'soup-websocket-extension-deflate.c',
'soup-websocket-extension-manager.c',
'soup-xmlrpc.c',
'soup-xmlrpc-old.c',
]
......@@ -106,6 +109,7 @@ soup_headers = [
'soup-proxy-resolver-wrapper.h',
'soup-session-private.h',
'soup-socket-private.h',
'soup-websocket-extension-manager-private.h',
]
soup_introspection_headers = [
......@@ -160,6 +164,9 @@ soup_introspection_headers = [
'soup-value-utils.h',
'soup-websocket.h',
'soup-websocket-connection.h',
'soup-websocket-extension.h',
'soup-websocket-extension-deflate.h',
'soup-websocket-extension-manager.h',
'soup-xmlrpc.h',
'soup-xmlrpc-old.h',
]
......@@ -234,6 +241,7 @@ deps = [
libpsl_dep,
brotlidec_dep,
platform_deps,
libz_dep,
]
libsoup = library('soup-@0@'.format(apiversion),
......
......@@ -140,6 +140,8 @@ GInputStream *soup_message_io_get_response_istream (SoupMessage *msg,
gboolean soup_message_disables_feature (SoupMessage *msg,
gpointer feature);
gboolean soup_message_disables_feature_by_type (SoupMessage *msg,
GType feature_type);
GSList *soup_message_get_disabled_features (SoupMessage *msg);
......
......@@ -1860,6 +1860,23 @@ soup_message_disables_feature (SoupMessage *msg, gpointer feature)
return FALSE;
}
gboolean
soup_message_disables_feature_by_type (SoupMessage *msg, GType feature_type)
{
SoupMessagePrivate *priv;
GSList *f;
g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
priv = soup_message_get_instance_private (msg);
for (f = priv->disabled_features; f; f = f->next) {
if (g_type_is_a ((GType)GPOINTER_TO_SIZE (f->data), feature_type))
return TRUE;
}
return FALSE;
}
GSList *
soup_message_get_disabled_features (SoupMessage *msg)
{
......
......@@ -21,6 +21,7 @@
#include "soup-socket-private.h"
#include "soup-websocket.h"
#include "soup-websocket-connection.h"
#include "soup-websocket-extension-deflate.h"
/**
* SECTION:soup-server
......@@ -156,6 +157,7 @@ typedef struct {
char *websocket_origin;
char **websocket_protocols;
GList *websocket_extensions;
SoupServerWebsocketCallback websocket_callback;
GDestroyNotify websocket_destroy;
gpointer websocket_user_data;
......@@ -183,6 +185,8 @@ typedef struct {
SoupAddress *legacy_iface;
int legacy_port;
GPtrArray *websocket_extension_types;
gboolean disposed;
} SoupServerPrivate;
......@@ -204,6 +208,8 @@ enum {
PROP_SERVER_HEADER,
PROP_HTTP_ALIASES,
PROP_HTTPS_ALIASES,
PROP_ADD_WEBSOCKET_EXTENSION,
PROP_REMOVE_WEBSOCKET_EXTENSION,
LAST_PROP
};
......@@ -219,6 +225,7 @@ free_handler (SoupServerHandler *handler)
g_free (handler->path);
g_free (handler->websocket_origin);
g_strfreev (handler->websocket_protocols);
g_list_free_full (handler->websocket_extensions, g_object_unref);
if (handler->early_destroy)
handler->early_destroy (handler->early_user_data);
if (handler->destroy)
......@@ -240,6 +247,11 @@ soup_server_init (SoupServer *server)
priv->http_aliases[1] = NULL;
priv->legacy_port = -1;
priv->websocket_extension_types = g_ptr_array_new_with_free_func ((GDestroyNotify)g_type_class_unref);
/* Use permessage-deflate extension by default */
g_ptr_array_add (priv->websocket_extension_types, g_type_class_ref (SOUP_TYPE_WEBSOCKET_EXTENSION_DEFLATE));
}
static void
......@@ -278,6 +290,8 @@ soup_server_finalize (GObject *object)
g_free (priv->http_aliases);
g_free (priv->https_aliases);
g_ptr_array_free (priv->websocket_extension_types, TRUE);
G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
}
......@@ -466,6 +480,12 @@ soup_server_set_property (GObject *object, guint prop_id,
case PROP_HTTPS_ALIASES:
set_aliases (&priv->https_aliases, g_value_get_boxed (value));
break;
case PROP_ADD_WEBSOCKET_EXTENSION:
soup_server_add_websocket_extension (server, g_value_get_gtype (value));
break;
case PROP_REMOVE_WEBSOCKET_EXTENSION:
soup_server_remove_websocket_extension (server, g_value_get_gtype (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -927,6 +947,51 @@ soup_server_class_init (SoupServerClass *server_class)
"URI schemes that are considered aliases for 'https'",
G_TYPE_STRV,
G_PARAM_READWRITE));
/**
* SoupServer:add-websocket-extension: (skip)
*
* Add support for #SoupWebsocketExtension of the given type.
* (Shortcut for calling soup_server_add_websocket_extension().)
*
* Since: 2.68
**/
/**
* SOUP_SERVER_ADD_WEBSOCKET_EXTENSION: (skip)
*
* Alias for the #SoupServer:add-websocket-extension property, qv.
*
* Since: 2.68
**/
g_object_class_install_property (
object_class, PROP_ADD_WEBSOCKET_EXTENSION,
g_param_spec_gtype (SOUP_SERVER_ADD_WEBSOCKET_EXTENSION,
"Add support for a WebSocket extension",
"Add support for a WebSocket extension of the given type",
SOUP_TYPE_WEBSOCKET_EXTENSION,
G_PARAM_WRITABLE));
/**
* SoupServer:remove-websocket-extension: (skip)
*
* Remove support for #SoupWebsocketExtension of the given type. (Shortcut for
* calling soup_server_remove_websocket_extension().)
*
* Since: 2.68
**/
/**
* SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION: (skip)
*
* Alias for the #SoupServer:remove-websocket-extension property, qv.
*
* Since: 2.68
**/
g_object_class_install_property (
object_class, PROP_REMOVE_WEBSOCKET_EXTENSION,
g_param_spec_gtype (SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION,
"Remove support for a WebSocket extension",
"Remove support for a WebSocket extension of the given type",
SOUP_TYPE_WEBSOCKET_EXTENSION,
G_PARAM_WRITABLE));
}
/**
......@@ -1367,10 +1432,12 @@ complete_websocket_upgrade (SoupMessage *msg, gpointer user_data)
soup_client_context_ref (client);
stream = soup_client_context_steal_connection (client);
conn = soup_websocket_connection_new (stream, uri,
SOUP_WEBSOCKET_CONNECTION_SERVER,
soup_message_headers_get_one (msg->request_headers, "Origin"),
soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"));
conn = soup_websocket_connection_new_with_extensions (stream, uri,
SOUP_WEBSOCKET_CONNECTION_SERVER,
soup_message_headers_get_one (msg->request_headers, "Origin"),
soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"),
handler->websocket_extensions);
handler->websocket_extensions = NULL;
g_object_unref (stream);
soup_client_context_unref (client);
......@@ -1402,9 +1469,14 @@ got_body (SoupMessage *msg, SoupClientContext *client)
return;
if (handler->websocket_callback) {
if (soup_websocket_server_process_handshake (msg,
handler->websocket_origin,
handler->websocket_protocols)) {
SoupServerPrivate *priv;
priv = soup_server_get_instance_private (server);
if (soup_websocket_server_process_handshake_with_extensions (msg,
handler->websocket_origin,
handler->websocket_protocols,
priv->websocket_extension_types,
&handler->websocket_extensions)) {
g_signal_connect (msg, "wrote-informational",
G_CALLBACK (complete_websocket_upgrade),
soup_client_context_ref (client));
......@@ -2696,12 +2768,14 @@ soup_server_add_websocket_handler (SoupServer *server,
g_free (handler->websocket_origin);
if (handler->websocket_protocols)
g_strfreev (handler->websocket_protocols);
g_list_free_full (handler->websocket_extensions, g_object_unref);
handler->websocket_callback = callback;
handler->websocket_destroy = destroy;
handler->websocket_user_data = user_data;
handler->websocket_origin = g_strdup (origin);
handler->websocket_protocols = g_strdupv (protocols);
handler->websocket_extensions = NULL;
}
/**
......@@ -2817,3 +2891,72 @@ soup_server_unpause_message (SoupServer *server,
soup_message_io_unpause (msg);
}
/**
* soup_server_add_websocket_extension:
* @server: a #SoupServer
* @extension_type: a #GType
*
* Add support for a WebSocket extension of the given @extension_type.
* When a WebSocket client requests an extension of @extension_type,
* a new #SoupWebsocketExtension of type @extension_type will be created
* to handle the request.
*
* You can also add support for a WebSocket extension to the server at
* construct time by using the %SOUP_SERVER_ADD_WEBSOCKET_EXTENSION property.
* Note that #SoupWebsocketExtensionDeflate is supported by default, use
* soup_server_remove_websocket_extension() if you want to disable it.
*
* Since: 2.68
*/
void
soup_server_add_websocket_extension (SoupServer *server, GType extension_type)
{
SoupServerPrivate *priv;
g_return_if_fail (SOUP_IS_SERVER (server));
priv = soup_server_get_instance_private (server);
if (!g_type_is_a (extension_type, SOUP_TYPE_WEBSOCKET_EXTENSION)) {
g_warning ("Type '%s' is not a SoupWebsocketExtension", g_type_name (extension_type));
return;
}
g_ptr_array_add (priv->websocket_extension_types, g_type_class_ref (extension_type));
}
/**
* soup_server_remove_websocket_extension:
* @server: a #SoupServer
* @extension_type: a #GType
*
* Removes support for WebSocket extension of type @extension_type (or any subclass of
* @extension_type) from @server. You can also remove extensions enabled by default
* from the server at construct time by using the %SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION
* property.
*
* Since: 2.68
*/
void
soup_server_remove_websocket_extension (SoupServer *server, GType extension_type)
{
SoupServerPrivate *priv;
SoupWebsocketExtensionClass *extension_class;
guint i;
g_return_if_fail (SOUP_IS_SERVER (server));
priv = soup_server_get_instance_private (server);
if (!g_type_is_a (extension_type, SOUP_TYPE_WEBSOCKET_EXTENSION)) {
g_warning ("Type '%s' is not a SoupWebsocketExtension", g_type_name (extension_type));
return;
}
extension_class = g_type_class_peek (extension_type);
for (i = 0; i < priv->websocket_extension_types->len; i++) {
if (priv->websocket_extension_types->pdata[i] == (gpointer)extension_class) {
g_ptr_array_remove_index (priv->websocket_extension_types, i);
break;
}
}
}
......@@ -138,6 +138,9 @@ void soup_server_add_early_handler (SoupServer *server,
gpointer user_data,
GDestroyNotify destroy);
#define SOUP_SERVER_ADD_WEBSOCKET_EXTENSION "add-websocket-extension"
#define SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION "remove-websocket-extension"
typedef void (*SoupServerWebsocketCallback) (SoupServer *server,
SoupWebsocketConnection *connection,
const char *path,
......@@ -151,6 +154,12 @@ void soup_server_add_websocket_handler (SoupServer
SoupServerWebsocketCallback callback,
gpointer user_data,
GDestroyNotify destroy);
SOUP_AVAILABLE_IN_2_68
void soup_server_add_websocket_extension (SoupServer *server,
GType extension_type);
SOUP_AVAILABLE_IN_2_68
void soup_server_remove_websocket_extension (SoupServer *server,
GType extension_type);
SOUP_AVAILABLE_IN_2_4
void soup_server_remove_handler (SoupServer *server,
......
......@@ -24,6 +24,7 @@
#include "soup-socket-private.h"
#include "soup-websocket.h"
#include "soup-websocket-connection.h"
#include "soup-websocket-extension-manager-private.h"
#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
......@@ -4775,6 +4776,19 @@ soup_session_steal_connection (SoupSession *session,
return stream;
}
static GPtrArray *
soup_session_get_supported_websocket_extensions_for_message (SoupSession *session,
SoupMessage *msg)
{
SoupSessionFeature *extension_manager;
extension_manager = soup_session_get_feature_for_message (session, SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER, msg);
if (!extension_manager)
return NULL;
return soup_websocket_extension_manager_get_supported_extensions (SOUP_WEBSOCKET_EXTENSION_MANAGER (extension_manager));
}
static void websocket_connect_async_stop (SoupMessage *msg, gpointer user_data);
static void
......@@ -4799,6 +4813,9 @@ websocket_connect_async_stop (SoupMessage *msg, gpointer user_data)
SoupMessageQueueItem *item = g_task_get_task_data (task);
GIOStream *stream;
SoupWebsocketConnection *client;
SoupSession *session = g_task_get_source_object (task);
GPtrArray *supported_extensions;
GList *accepted_extensions = NULL;
GError *error = NULL;
/* Disconnect websocket_connect_async_stop() handler. */
......@@ -4807,20 +4824,24 @@ websocket_connect_async_stop (SoupMessage *msg, gpointer user_data)
/* Ensure websocket_connect_async_complete is not called either. */
item->callback = NULL;
if (soup_websocket_client_verify_handshake (item->msg, &error)){
supported_extensions = soup_session_get_supported_websocket_extensions_for_message (session, msg);
if (soup_websocket_client_verify_handshake_with_extensions (item->msg, supported_extensions, &accepted_extensions, &error)) {
stream = soup_session_steal_connection (item->session, item->msg);
client = soup_websocket_connection_new (stream,
soup_message_get_uri (item->msg),
SOUP_WEBSOCKET_CONNECTION_CLIENT,
soup_message_headers_get_one (msg->request_headers, "Origin"),
soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"));
client = soup_websocket_connection_new_with_extensions (stream,
soup_message_get_uri (item->msg),
SOUP_WEBSOCKET_CONNECTION_CLIENT,
soup_message_headers_get_one (msg->request_headers, "Origin"),
soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol"),
accepted_extensions);
g_object_unref (stream);
g_task_return_pointer (task, client, g_object_unref);
} else {
soup_message_io_finished (item->msg);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
soup_message_io_finished (item->msg);
g_task_return_error (task, error);
g_object_unref (task);
}
......@@ -4868,12 +4889,14 @@ soup_session_websocket_connect_async (SoupSession *session,
SoupSessionPrivate *priv = soup_session_get_instance_private (session);
SoupMessageQueueItem *item;
GTask *task;
GPtrArray *supported_extensions;
g_return_if_fail (SOUP_IS_SESSION (session));
g_return_if_fail (priv->use_thread_context);
g_return_if_fail (SOUP_IS_MESSAGE (msg));
soup_websocket_client_prepare_handshake (msg, origin, protocols);
supported_extensions = soup_session_get_supported_websocket_extensions_for_message (session, msg);
soup_websocket_client_prepare_handshake_with_extensions (msg, origin, protocols, supported_extensions);
task = g_task_new (session, cancellable, callback, user_data);
item = soup_session_append_queue_item (session, msg, TRUE, FALSE,
......
......@@ -32,7 +32,7 @@ typedef struct _SoupSessionSync SoupSessionSync;
typedef struct _SoupSocket SoupSocket;
typedef struct _SoupURI SoupURI;
typedef struct _SoupWebsocketConnection SoupWebsocketConnection;
typedef struct _SoupWebsocketExtension SoupWebsocketExtension;
/*< private >*/
typedef struct _SoupConnection SoupConnection;
......
......@@ -26,6 +26,7 @@
#include "soup-enum-types.h"
#include "soup-io-stream.h"
#include "soup-uri.h"
#include "soup-websocket-extension.h"
/*
* SECTION:websocketconnection
......@@ -84,6 +85,7 @@ enum {
PROP_STATE,
PROP_MAX_INCOMING_PAYLOAD_SIZE,
PROP_KEEPALIVE_INTERVAL,
PROP_EXTENSIONS
};
enum {
......@@ -145,6 +147,8 @@ struct _SoupWebsocketConnectionPrivate {
GByteArray *message_data;
GSource *keepalive_timeout;
GList *extensions;
};
#define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT 128 * 1024
......@@ -154,6 +158,9 @@ G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, soup_websocket_connection,
static void queue_frame (SoupWebsocketConnection *self, SoupWebsocketQueueFlags flags,
gpointer data, gsize len, gsize amount);
static void emit_error_and_close (SoupWebsocketConnection *self,
GError *error, gboolean prejudice);
static void protocol_error_and_close (SoupWebsocketConnection *self);
/* Code below is based on g_utf8_validate() implementation,
......@@ -427,12 +434,15 @@ send_message (SoupWebsocketConnection *self,
const guint8 *data,
gsize length)
{
gsize buffered_amount = length;
gsize buffered_amount;
GByteArray *bytes;
gsize frame_len;
guint8 *outer;
guint8 *mask = 0;
guint8 *at;
GBytes *filtered_bytes;
GList *l;
GError *error = NULL;
if (!(soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_OPEN)) {
g_debug ("Ignoring message since the connection is closed or is closing");
......@@ -443,6 +453,21 @@ send_message (SoupWebsocketConnection *self,
outer = bytes->data;
outer[0] = 0x80 | opcode;
filtered_bytes = g_bytes_new_static (data, length);
for (l = self->pv->extensions; l != NULL; l = g_list_next (l)) {
SoupWebsocketExtension *extension;
extension = (SoupWebsocketExtension *)l->data;
filtered_bytes = soup_websocket_extension_process_outgoing_message (extension, outer, filtered_bytes, &error);
if (error) {
emit_error_and_close (self, error, FALSE);
return;
}
}
data = g_bytes_get_data (filtered_bytes, &length);
buffered_amount = length;
/* If control message, check payload size */
if (opcode & 0x08) {
if (length > 125) {
......@@ -499,6 +524,7 @@ send_message (SoupWebsocketConnection *self,
frame_len = bytes->len;
queue_frame (self, flags, g_byte_array_free (bytes, FALSE),
frame_len, buffered_amount);
g_bytes_unref (filtered_bytes);
g_debug ("queued %d frame of len %u", (int)opcode, (guint)frame_len);
}
......@@ -771,11 +797,14 @@ process_contents (SoupWebsocketConnection *self,
gboolean control,
gboolean fin,
guint8 opcode,
gconstpointer payload,
gsize payload_len)
GBytes *payload_data)
{