1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /*
3  * soup-websocket-extension.c
4  *
5  * Copyright (C) 2019 Igalia S.L.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include "soup-websocket-extension.h"
28 
29 /**
30  * SECTION:soup-websocket-extension
31  * @short_description: a WebSocket extension
32  * @see_also: #SoupSession, #SoupWebsocketExtensionManager
33  *
34  * SoupWebsocketExtension is the base class for WebSocket extension objects.
35  *
36  */
37 
38 /**
39  * SoupWebsocketExtension:
40  *
41  * Class for impelementing websocket extensions.
42  */
43 
44 /**
45  * SoupWebsocketExtensionClass:
46  * @name: the name of the extension
47  * @parent_class: the parent class
48  * @configure: called to configure the extension with the given parameters
49  * @get_request_params: called by the client to build the request header.
50  *    It should include the parameters string starting with ';'
51  * @get_response_params: called by the server to build the response header.
52  *    It should include the parameters string starting with ';'
53  * @process_outgoing_message: called to process the payload data of a message
54  *    before it's sent. Reserved bits of the header should be changed.
55  * @process_incoming_message: called to process the payload data of a message
56  *    after it's received. Reserved bits of the header should be cleared.
57  *
58  * The class structure for the SoupWebsocketExtension.
59  *
60  */
61 
G_DEFINE_ABSTRACT_TYPE(SoupWebsocketExtension,soup_websocket_extension,G_TYPE_OBJECT)62 G_DEFINE_ABSTRACT_TYPE (SoupWebsocketExtension, soup_websocket_extension, G_TYPE_OBJECT)
63 
64 static void
65 soup_websocket_extension_init (SoupWebsocketExtension *extension)
66 {
67 }
68 
69 static void
soup_websocket_extension_class_init(SoupWebsocketExtensionClass * auth_class)70 soup_websocket_extension_class_init (SoupWebsocketExtensionClass *auth_class)
71 {
72 }
73 
74 /**
75  * soup_websocket_extension_configure:
76  * @extension: a #SoupWebsocketExtension
77  * @connection_type: either %SOUP_WEBSOCKET_CONNECTION_CLIENT or %SOUP_WEBSOCKET_CONNECTION_SERVER
78  * @params: (nullable): the parameters, or %NULL
79  * @error: return location for a #GError
80  *
81  * Configures @extension with the given @params
82  *
83  * Returns: %TRUE if extension could be configured with the given parameters, or %FALSE otherwise
84  */
85 gboolean
soup_websocket_extension_configure(SoupWebsocketExtension * extension,SoupWebsocketConnectionType connection_type,GHashTable * params,GError ** error)86 soup_websocket_extension_configure (SoupWebsocketExtension     *extension,
87 				    SoupWebsocketConnectionType connection_type,
88 				    GHashTable                 *params,
89 				    GError                    **error)
90 {
91 	SoupWebsocketExtensionClass *klass;
92 
93 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_EXTENSION (extension), FALSE);
94 	g_return_val_if_fail (connection_type != SOUP_WEBSOCKET_CONNECTION_UNKNOWN, FALSE);
95 	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
96 
97 	klass = SOUP_WEBSOCKET_EXTENSION_GET_CLASS (extension);
98 	if (!klass->configure)
99 		return TRUE;
100 
101 	return klass->configure (extension, connection_type, params, error);
102 }
103 
104 /**
105  * soup_websocket_extension_get_request_params:
106  * @extension: a #SoupWebsocketExtension
107  *
108  * Get the parameters strings to be included in the request header. If the extension
109  * doesn't include any parameter in the request, this function returns %NULL.
110  *
111  * Returns: (nullable) (transfer full): a new allocated string with the parameters
112  *
113  */
114 char *
soup_websocket_extension_get_request_params(SoupWebsocketExtension * extension)115 soup_websocket_extension_get_request_params (SoupWebsocketExtension *extension)
116 {
117 	SoupWebsocketExtensionClass *klass;
118 
119         g_return_val_if_fail (SOUP_IS_WEBSOCKET_EXTENSION (extension), NULL);
120 
121 	klass = SOUP_WEBSOCKET_EXTENSION_GET_CLASS (extension);
122         if (!klass->get_request_params)
123                 return NULL;
124 
125         return klass->get_request_params (extension);
126 }
127 
128 /**
129  * soup_websocket_extension_get_response_params:
130  * @extension: a #SoupWebsocketExtension
131  *
132  * Get the parameters strings to be included in the response header. If the extension
133  * doesn't include any parameter in the response, this function returns %NULL.
134  *
135  * Returns: (nullable) (transfer full): a new allocated string with the parameters
136  *
137  */
138 char *
soup_websocket_extension_get_response_params(SoupWebsocketExtension * extension)139 soup_websocket_extension_get_response_params (SoupWebsocketExtension *extension)
140 {
141 	SoupWebsocketExtensionClass *klass;
142 
143 	g_return_val_if_fail (SOUP_IS_WEBSOCKET_EXTENSION (extension), NULL);
144 
145 	klass = SOUP_WEBSOCKET_EXTENSION_GET_CLASS (extension);
146 	if (!klass->get_response_params)
147 		return NULL;
148 
149 	return klass->get_response_params (extension);
150 }
151 
152 /**
153  * soup_websocket_extension_process_outgoing_message:
154  * @extension: a #SoupWebsocketExtension
155  * @header: (inout): the message header
156  * @payload: (transfer full): the payload data
157  * @error: return location for a #GError
158  *
159  * Process a message before it's sent. If the payload isn't changed the given
160  * @payload is just returned, otherwise g_bytes_unref() is called on the given
161  * @payload and a new #GBytes is returned with the new data.
162  *
163  * Extensions using reserved bits of the header will change them in @header.
164  *
165  * Returns: (transfer full): the message payload data, or %NULL in case of error
166  *
167  */
168 GBytes *
soup_websocket_extension_process_outgoing_message(SoupWebsocketExtension * extension,guint8 * header,GBytes * payload,GError ** error)169 soup_websocket_extension_process_outgoing_message (SoupWebsocketExtension *extension,
170 						   guint8                 *header,
171 						   GBytes                 *payload,
172 						   GError                **error)
173 {
174 	SoupWebsocketExtensionClass *klass;
175 
176         g_return_val_if_fail (SOUP_IS_WEBSOCKET_EXTENSION (extension), NULL);
177 	g_return_val_if_fail (header != NULL, NULL);
178 	g_return_val_if_fail (payload != NULL, NULL);
179 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
180 
181         klass = SOUP_WEBSOCKET_EXTENSION_GET_CLASS (extension);
182 	if (!klass->process_outgoing_message)
183 		return payload;
184 
185 	return klass->process_outgoing_message (extension, header, payload, error);
186 }
187 
188 /**
189  * soup_websocket_extension_process_incoming_message:
190  * @extension: a #SoupWebsocketExtension
191  * @header: (inout): the message header
192  * @payload: (transfer full): the payload data
193  * @error: return location for a #GError
194  *
195  * Process a message after it's received. If the payload isn't changed the given
196  * @payload is just returned, otherwise g_bytes_unref() is called on the given
197  * @payload and a new #GBytes is returned with the new data.
198  *
199  * Extensions using reserved bits of the header will reset them in @header.
200  *
201  * Returns: (transfer full): the message payload data, or %NULL in case of error
202  *
203  */
204 GBytes *
soup_websocket_extension_process_incoming_message(SoupWebsocketExtension * extension,guint8 * header,GBytes * payload,GError ** error)205 soup_websocket_extension_process_incoming_message (SoupWebsocketExtension *extension,
206 						   guint8                 *header,
207 						   GBytes                 *payload,
208 						   GError                **error)
209 {
210 	SoupWebsocketExtensionClass *klass;
211 
212         g_return_val_if_fail (SOUP_IS_WEBSOCKET_EXTENSION (extension), NULL);
213 	g_return_val_if_fail (header != NULL, NULL);
214 	g_return_val_if_fail (payload != NULL, NULL);
215 	g_return_val_if_fail (error == NULL || *error == NULL, NULL);
216 
217         klass = SOUP_WEBSOCKET_EXTENSION_GET_CLASS (extension);
218 	if (!klass->process_incoming_message)
219 		return payload;
220 
221 	return klass->process_incoming_message (extension, header, payload, error);
222 }
223