source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/wsio.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 35.6 KB
Line 
1 // Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#include <stdlib.h>
5#include <stdio.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include "azure_c_shared_utility/gballoc.h"
9#include "azure_c_shared_utility/wsio.h"
10#include "azure_c_shared_utility/xlogging.h"
11#include "azure_c_shared_utility/singlylinkedlist.h"
12#include "azure_c_shared_utility/optionhandler.h"
13#include "azure_c_shared_utility/xio.h"
14#include "azure_c_shared_utility/shared_util_options.h"
15#include "azure_c_shared_utility/crt_abstractions.h"
16#include "azure_c_shared_utility/uws_client.h"
17#include "azure_c_shared_utility/optimize_size.h"
18
19static const char* WSIO_OPTIONS = "WSIOOptions";
20
21typedef enum IO_STATE_TAG
22{
23 IO_STATE_NOT_OPEN,
24 IO_STATE_OPENING,
25 IO_STATE_OPEN,
26 IO_STATE_CLOSING,
27 IO_STATE_ERROR
28} IO_STATE;
29
30typedef struct PENDING_IO_TAG
31{
32 ON_SEND_COMPLETE on_send_complete;
33 void* callback_context;
34 void* wsio;
35} PENDING_IO;
36
37typedef struct WSIO_INSTANCE_TAG
38{
39 ON_BYTES_RECEIVED on_bytes_received;
40 void* on_bytes_received_context;
41 ON_IO_OPEN_COMPLETE on_io_open_complete;
42 void* on_io_open_complete_context;
43 ON_IO_ERROR on_io_error;
44 void* on_io_error_context;
45 ON_IO_CLOSE_COMPLETE on_io_close_complete;
46 void* on_io_close_complete_context;
47 IO_STATE io_state;
48 SINGLYLINKEDLIST_HANDLE pending_io_list;
49 UWS_CLIENT_HANDLE uws;
50} WSIO_INSTANCE;
51
52static void indicate_error(WSIO_INSTANCE* wsio_instance)
53{
54 wsio_instance->io_state = IO_STATE_ERROR;
55 wsio_instance->on_io_error(wsio_instance->on_io_error_context);
56}
57
58static void indicate_open_complete(WSIO_INSTANCE* ws_io_instance, IO_OPEN_RESULT open_result)
59{
60 ws_io_instance->on_io_open_complete(ws_io_instance->on_io_open_complete_context, open_result);
61}
62
63static void complete_send_item(LIST_ITEM_HANDLE pending_io_list_item, IO_SEND_RESULT io_send_result)
64{
65 PENDING_IO* pending_io = (PENDING_IO*)singlylinkedlist_item_get_value(pending_io_list_item);
66 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)pending_io->wsio;
67
68 /* Codes_SRS_WSIO_01_145: [ Removing it from the list shall be done by calling singlylinkedlist_remove. ]*/
69 if (singlylinkedlist_remove(wsio_instance->pending_io_list, pending_io_list_item) != 0)
70 {
71 LogError("Failed removing pending IO from linked list.");
72 }
73
74 /* Codes_SRS_WSIO_01_105: [ The argument on_send_complete shall be optional, if NULL is passed by the caller then no send complete callback shall be triggered. ]*/
75 if (pending_io->on_send_complete != NULL)
76 {
77 pending_io->on_send_complete(pending_io->callback_context, io_send_result);
78 }
79
80 /* Codes_SRS_WSIO_01_144: [ Also the pending IO data shall be freed. ]*/
81 free(pending_io);
82}
83
84static void on_underlying_ws_send_frame_complete(void* context, WS_SEND_FRAME_RESULT ws_send_frame_result)
85{
86 if (context == NULL)
87 {
88 LogError("NULL context for on_underlying_ws_send_frame_complete");
89 }
90 else
91 {
92 IO_SEND_RESULT io_send_result;
93 LIST_ITEM_HANDLE list_item_handle = (LIST_ITEM_HANDLE)context;
94
95 /* Codes_SRS_WSIO_01_143: [ When on_underlying_ws_send_frame_complete is called after sending a WebSocket frame, the pending IO shall be removed from the list. ]*/
96 switch (ws_send_frame_result)
97 {
98 default:
99 /* Codes_SRS_WSIO_01_148: [ When on_underlying_ws_send_frame_complete is called with any other error code, the callback on_send_complete shall be called with IO_SEND_ERROR. ]*/
100 LogError("Frame send error with result %d", (int)ws_send_frame_result);
101 io_send_result = IO_SEND_ERROR;
102 break;
103
104 case WS_SEND_FRAME_OK:
105 /* Codes_SRS_WSIO_01_146: [ When on_underlying_ws_send_frame_complete is called with WS_SEND_OK, the callback on_send_complete shall be called with IO_SEND_OK. ]*/
106 io_send_result = IO_SEND_OK;
107 break;
108
109 case WS_SEND_FRAME_CANCELLED:
110 /* Codes_SRS_WSIO_01_147: [ When on_underlying_ws_send_frame_complete is called with WS_SEND_CANCELLED, the callback on_send_complete shall be called with IO_SEND_CANCELLED. ]*/
111 io_send_result = IO_SEND_CANCELLED;
112 break;
113 }
114
115 complete_send_item(list_item_handle, io_send_result);
116 }
117}
118
119static void on_underlying_ws_close_complete(void* context)
120{
121 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context;
122 if (wsio_instance == NULL)
123 {
124 /* Codes_SRS_WSIO_01_161: [ If the context passed to on_underlying_ws_close_complete is NULL, on_underlying_ws_close_complete shall do nothing. ]*/
125 LogError("NULL context passed to on_underlying_ws_close_complete");
126 }
127 else
128 {
129 wsio_instance->io_state = IO_STATE_NOT_OPEN;
130
131 /* Codes_SRS_WSIO_01_160: [ If NULL was passed to wsio_close no callback shall be called. ]*/
132 if (wsio_instance->on_io_close_complete != NULL)
133 {
134 /* Codes_SRS_WSIO_01_159: [ When on_underlying_ws_close_complete while the IO is closing (after wsio_close), the close shall be indicated up by calling the on_io_close_complete callback passed to wsio_close. ]*/
135 /* Codes_SRS_WSIO_01_163: [ When on_io_close_complete is called, the context passed to wsio_close shall be passed as argument to on_io_close_complete. ]*/
136 wsio_instance->on_io_close_complete(wsio_instance->on_io_close_complete_context);
137 }
138 }
139}
140
141static int internal_close(WSIO_INSTANCE* wsio_instance, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context)
142{
143 int result;
144
145 /* Codes_SRS_WSIO_01_089: [ wsio_close after a wsio_close shall fail and return a non-zero value. ]*/
146 /* Codes_SRS_WSIO_01_088: [ wsio_close when no open action has been issued shall fail and return a non-zero value. ]*/
147 if (wsio_instance->io_state == IO_STATE_NOT_OPEN)
148 {
149 LogError("wsio_close when not open.");
150 result = MU_FAILURE;
151 }
152 else
153 {
154 if (wsio_instance->io_state == IO_STATE_OPENING)
155 {
156 wsio_instance->io_state = IO_STATE_NOT_OPEN;
157 indicate_open_complete(wsio_instance, IO_OPEN_CANCELLED);
158 result = 0;
159 }
160 else if (wsio_instance->io_state == IO_STATE_CLOSING)
161 {
162 LogError("Already closing");
163 result = MU_FAILURE;
164 }
165 else
166 {
167 LIST_ITEM_HANDLE first_pending_io;
168
169 wsio_instance->io_state = IO_STATE_CLOSING;
170
171 wsio_instance->on_io_close_complete = on_io_close_complete;
172 wsio_instance->on_io_close_complete_context = on_io_close_complete_context;
173
174 /* Codes_SRS_WSIO_01_087: [ wsio_close shall call uws_client_close_async while passing as argument the IO handle created in wsio_create. ]*/
175 if (uws_client_close_async(wsio_instance->uws, on_underlying_ws_close_complete, wsio_instance) != 0)
176 {
177 /* Codes_SRS_WSIO_01_164: [ When uws_client_close_async fails, wsio_close shall call the on_io_close_complete callback and continue. ] */
178 if (wsio_instance->on_io_close_complete != NULL)
179 {
180 wsio_instance->on_io_close_complete(wsio_instance->on_io_close_complete_context);
181 }
182 }
183
184 /* Codes_SRS_WSIO_01_085: [ wsio_close shall close the websockets IO if an open action is either pending or has completed successfully (if the IO is open). ]*/
185 /* Codes_SRS_WSIO_01_091: [ wsio_close shall obtain all the pending IO items by repetitively querying for the head of the pending IO list and freeing that head item. ]*/
186 /* Codes_SRS_WSIO_01_092: [ Obtaining the head of the pending IO list shall be done by calling singlylinkedlist_get_head_item. ]*/
187 while ((first_pending_io = singlylinkedlist_get_head_item(wsio_instance->pending_io_list)) != NULL)
188 {
189 complete_send_item(first_pending_io, IO_SEND_CANCELLED);
190 }
191
192 /* Codes_SRS_WSIO_01_133: [ On success wsio_close shall return 0. ]*/
193 result = 0;
194 wsio_instance->io_state = IO_STATE_NOT_OPEN;
195 }
196 }
197 return result;
198}
199
200CONCRETE_IO_HANDLE wsio_create(void* io_create_parameters)
201{
202 /* Codes_SRS_WSIO_01_066: [ io_create_parameters shall be used as a WSIO_CONFIG* . ]*/
203 WSIO_CONFIG* ws_io_config = (WSIO_CONFIG*)io_create_parameters;
204 WSIO_INSTANCE* result;
205
206 /* Codes_SRS_WSIO_01_065: [ If the argument io_create_parameters is NULL then wsio_create shall return NULL. ]*/
207 if ((ws_io_config == NULL) ||
208 /* Codes_SRS_WSIO_01_067: [ If any of the members hostname, resource_name or protocol is NULL in WSIO_CONFIG then wsio_create shall return NULL. ]*/
209 (ws_io_config->hostname == NULL) ||
210 (ws_io_config->resource_name == NULL) ||
211 (ws_io_config->protocol == NULL))
212 {
213 LogError("NULL io_create_parameters.");
214 result = NULL;
215 }
216 else
217 {
218 /* Codes_SRS_WSIO_01_001: [wsio_create shall create an instance of wsio and return a non-NULL handle to it.] */
219 result = (WSIO_INSTANCE*)calloc(1, sizeof(WSIO_INSTANCE));
220 if (result == NULL)
221 {
222 /* Codes_SRS_WSIO_01_068: [ If allocating memory for the new wsio instance fails then wsio_create shall return NULL. ]*/
223 LogError("Cannot allocate memory for the new WSIO instance.");
224 }
225 else
226 {
227 WS_PROTOCOL protocols;
228
229 protocols.protocol = ws_io_config->protocol;
230
231 result->on_bytes_received = NULL;
232 result->on_bytes_received_context = NULL;
233 result->on_io_open_complete = NULL;
234 result->on_io_open_complete_context = NULL;
235 result->on_io_error = NULL;
236 result->on_io_error_context = NULL;
237 result->on_io_close_complete = NULL;
238 result->on_io_close_complete_context = NULL;
239
240 /* Codes_SRS_WSIO_01_070: [ The underlying uws instance shall be created by calling uws_client_create_with_io. ]*/
241 /* Codes_SRS_WSIO_01_071: [ The arguments for uws_client_create_with_io shall be: ]*/
242 /* Codes_SRS_WSIO_01_185: [ - underlying_io_interface shall be set to the underlying_io_interface field in the io_create_parameters passed to wsio_create. ]*/
243 /* Codes_SRS_WSIO_01_186: [ - underlying_io_parameters shall be set to the underlying_io_parameters field in the io_create_parameters passed to wsio_create. ]*/
244 /* Codes_SRS_WSIO_01_072: [ - hostname set to the hostname field in the io_create_parameters passed to wsio_create. ]*/
245 /* Codes_SRS_WSIO_01_130: [ - port set to the port field in the io_create_parameters passed to wsio_create. ]*/
246 /* Codes_SRS_WSIO_01_128: [ - resource_name set to the resource_name field in the io_create_parameters passed to wsio_create. ]*/
247 /* Codes_SRS_WSIO_01_129: [ - protocols shall be filled with only one structure, that shall have the protocol set to the value of the protocol field in the io_create_parameters passed to wsio_create. ]*/
248 result->uws = uws_client_create_with_io(ws_io_config->underlying_io_interface, ws_io_config->underlying_io_parameters, ws_io_config->hostname, ws_io_config->port, ws_io_config->resource_name, &protocols, 1);
249 if (result->uws == NULL)
250 {
251 /* Codes_SRS_WSIO_01_075: [ If uws_client_create_with_io fails, then wsio_create shall fail and return NULL. ]*/
252 LogError("Cannot create uws instance.");
253 free(result);
254 result = NULL;
255 }
256 else
257 {
258 /* Codes_SRS_WSIO_01_076: [ wsio_create shall create a pending send IO list that is to be used to queue send packets by calling singlylinkedlist_create. ]*/
259 result->pending_io_list = singlylinkedlist_create();
260 if (result->pending_io_list == NULL)
261 {
262 /* Codes_SRS_WSIO_01_077: [ If singlylinkedlist_create fails then wsio_create shall fail and return NULL. ]*/
263 LogError("Cannot create singly linked list.");
264 uws_client_destroy(result->uws);
265 free(result);
266 result = NULL;
267 }
268 else
269 {
270 result->io_state = IO_STATE_NOT_OPEN;
271 }
272 }
273 }
274 }
275
276 return result;
277}
278
279void wsio_destroy(CONCRETE_IO_HANDLE ws_io)
280{
281 /* Codes_SRS_WSIO_01_079: [ If ws_io is NULL, wsio_destroy shall do nothing. ]*/
282 if (ws_io == NULL)
283 {
284 LogError("NULL handle");
285 }
286 else
287 {
288 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io;
289
290 if (wsio_instance->io_state != IO_STATE_NOT_OPEN)
291 {
292 internal_close(wsio_instance, NULL, NULL);
293 }
294
295 /* Codes_SRS_WSIO_01_078: [ wsio_destroy shall free all resources associated with the wsio instance. ]*/
296 /* Codes_SRS_WSIO_01_080: [ wsio_destroy shall destroy the uws instance created in wsio_create by calling uws_client_destroy. ]*/
297 uws_client_destroy(wsio_instance->uws);
298 /* Codes_SRS_WSIO_01_081: [ wsio_destroy shall free the list used to track the pending send IOs by calling singlylinkedlist_destroy. ]*/
299 singlylinkedlist_destroy(wsio_instance->pending_io_list);
300 free(ws_io);
301 }
302}
303
304static void on_underlying_ws_open_complete(void* context, WS_OPEN_RESULT open_result)
305{
306 if (context == NULL)
307 {
308 /* Codes_SRS_WSIO_01_138: [ When on_underlying_ws_open_complete is called with a NULL context, it shall do nothing. ]*/
309 LogError("NULL context in on_underlying_ws_open_complete");
310 }
311 else
312 {
313 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context;
314
315 switch (wsio_instance->io_state)
316 {
317 default:
318 /* Codes_SRS_WSIO_01_142: [ When on_underlying_ws_open_complete is called while in the CLOSING state an error shall be indicated by calling the on_io_error callback passed to wsio_open. ]*/
319 case IO_STATE_CLOSING:
320 /* Codes_SRS_WSIO_01_141: [ When on_underlying_ws_open_complete is called while in the ERROR state it shall indicate an error by calling the on_io_error callback passed to wsio_open. ]*/
321 case IO_STATE_ERROR:
322 /* Codes_SRS_WSIO_01_139: [ When on_underlying_ws_open_complete is called while in OPEN state it shall indicate an error by calling the on_io_error callback passed to wsio_open and switch to the ERROR state. ]*/
323 case IO_STATE_OPEN:
324 /* Codes_SRS_WSIO_01_140: [ When calling on_io_error, the on_io_error_context argument given in wsio_open shall be passed to the callback on_io_error. ]*/
325 indicate_error(wsio_instance);
326 break;
327
328 case IO_STATE_OPENING:
329 wsio_instance->io_state = IO_STATE_OPEN;
330
331 switch (open_result)
332 {
333 default:
334 /* Codes_SRS_WSIO_01_137: [ When on_underlying_ws_open_complete is called with any other error code while the IO is opening, the callback on_io_open_complete shall be called with IO_OPEN_ERROR. ]*/
335 wsio_instance->io_state = IO_STATE_NOT_OPEN;
336 indicate_open_complete(wsio_instance, IO_OPEN_ERROR);
337 break;
338
339 case WS_OPEN_CANCELLED:
340 /* Codes_SRS_WSIO_01_149: [ When on_underlying_ws_open_complete is called with WS_OPEN_CANCELLED while the IO is opening, the callback on_io_open_complete shall be called with IO_OPEN_CANCELLED. ]*/
341 wsio_instance->io_state = IO_STATE_NOT_OPEN;
342 indicate_open_complete(wsio_instance, IO_OPEN_CANCELLED);
343 break;
344
345 case WS_OPEN_OK:
346 /* Codes_SRS_WSIO_01_136: [ When on_underlying_ws_open_complete is called with WS_OPEN_OK while the IO is opening, the callback on_io_open_complete shall be called with IO_OPEN_OK. ]*/
347 wsio_instance->io_state = IO_STATE_OPEN;
348 indicate_open_complete(wsio_instance, IO_OPEN_OK);
349 break;
350 }
351
352 break;
353 }
354 }
355}
356
357static void on_underlying_ws_frame_received(void* context, unsigned char frame_type, const unsigned char* buffer, size_t size)
358{
359 if (context == NULL)
360 {
361 /* Codes_SRS_WSIO_01_150: [ If on_underlying_ws_frame_received is called with NULL context it shall do nothing. ]*/
362 LogError("NULL context for on_underlying_ws_frame_received");
363 }
364 else
365 {
366 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context;
367
368 if (wsio_instance->io_state != IO_STATE_OPEN)
369 {
370 /* Codes_SRS_WSIO_01_126: [ If on_underlying_ws_frame_received is called while the IO is in any state other than OPEN, it shall do nothing. ]*/
371 LogError("on_underlying_ws_frame_received called in a bad state.");
372 }
373 else
374 {
375 if (frame_type != WS_FRAME_TYPE_BINARY)
376 {
377 /* Codes_SRS_WSIO_01_151: [ If the WebSocket frame type is not binary then an error shall be indicated by calling the on_io_error callback passed to wsio_open. ]*/
378 LogError("Invalid non binary WebSocket frame received.");
379 /* Codes_SRS_WSIO_01_152: [ When calling on_io_error, the on_io_error_context argument given in wsio_open shall be passed to the callback on_io_error. ]*/
380 indicate_error(wsio_instance);
381 }
382 else
383 {
384 /* Codes_SRS_WSIO_01_153: [ When on_underlying_ws_frame_received is called with zero size, no bytes shall be indicated up as received. ]*/
385 if (size > 0)
386 {
387 if (buffer == NULL)
388 {
389 /* Codes_SRS_WSIO_01_154: [ When on_underlying_ws_frame_received is called with a positive size and a NULL buffer, an error shall be indicated by calling the on_io_error callback passed to wsio_open. ]*/
390 LogError("NULL buffer received for Websocket frame with positive payload length.");
391 indicate_error(wsio_instance);
392 }
393 else
394 {
395 /* Codes_SRS_WSIO_01_124: [ When on_underlying_ws_frame_received is called the bytes in the frame shall be indicated by calling the on_bytes_received callback passed to wsio_open. ]*/
396 /* Codes_SRS_WSIO_01_125: [ When calling on_bytes_received, the on_bytes_received_context argument given in wsio_open shall be passed to the callback on_bytes_received. ]*/
397 wsio_instance->on_bytes_received(wsio_instance->on_bytes_received_context, buffer, size);
398 }
399 }
400 }
401 }
402 }
403}
404
405static void on_underlying_ws_peer_closed(void* context, uint16_t* close_code, const unsigned char* extra_data, size_t extra_data_length)
406{
407 /* Codes_SRS_WSIO_01_168: [ The close_code, extra_data and extra_data_length arguments shall be ignored. ]*/
408 (void)close_code;
409 (void)extra_data;
410 (void)extra_data_length;
411
412 if (context == NULL)
413 {
414 /* Codes_SRS_WSIO_01_167: [ If on_underlying_ws_peer_closed is called with a NULL context it shall do nothing. ]*/
415 LogError("NULL context for on_underlying_ws_peer_closed");
416 }
417 else
418 {
419 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context;
420
421 switch (wsio_instance->io_state)
422 {
423 default:
424 /* Codes_SRS_WSIO_01_166: [ When on_underlying_ws_peer_closed and the state of the IO is OPEN an error shall be indicated by calling the on_io_error callback passed to wsio_open. ]*/
425 case IO_STATE_OPEN:
426 /* Codes_SRS_WSIO_01_169: [ When on_underlying_ws_peer_closed and the state of the IO is CLOSING an error shall be indicated by calling the on_io_error callback passed to wsio_open. ]*/
427 case IO_STATE_CLOSING:
428 indicate_error(wsio_instance);
429 break;
430
431 case IO_STATE_NOT_OPEN:
432 // Codes_SRS_WSIO_07_001: [When on_underlying_ws_peer_closed and the state of the IO is NOT_OPEN an error will be raised and the io_state will remain as NOT_OPEN]
433 indicate_error(wsio_instance);
434 wsio_instance->io_state = IO_STATE_NOT_OPEN;
435 break;
436
437 case IO_STATE_OPENING:
438 /* Codes_SRS_WSIO_01_170: [ When on_underlying_ws_peer_closed and the state of the IO is OPENING an error shall be indicated by calling the on_io_open_complete callback passed to wsio_open with the error code IO_OPEN_ERROR. ]*/
439 wsio_instance->io_state = IO_STATE_NOT_OPEN;
440 indicate_open_complete(wsio_instance, IO_OPEN_ERROR);
441 break;
442 }
443 }
444}
445
446static void on_underlying_ws_error(void* context, WS_ERROR ws_error)
447{
448 (void)ws_error;
449 /* Don't have much to do with the error here */
450 LogError("on_underlying_ws_error called with error code %d", (int)ws_error);
451
452 if (context == NULL)
453 {
454 /* Codes_SRS_WSIO_01_135: [ When on_underlying_ws_error is called with a NULL context, it shall do nothing. ]*/
455 LogError("NULL context in on_underlying_ws_error");
456 }
457 else
458 {
459 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)context;
460
461 if (wsio_instance->io_state == IO_STATE_OPENING)
462 {
463 /* Codes_SRS_WSIO_01_123: [ When calling on_io_error, the on_io_error_context argument given in wsio_open shall be passed to the callback on_io_error. ]*/
464 wsio_instance->on_io_open_complete(wsio_instance->on_io_open_complete_context, IO_OPEN_ERROR);
465 wsio_instance->io_state = IO_STATE_NOT_OPEN;
466 }
467 else
468 {
469 /* Codes_SRS_WSIO_01_123: [ When calling on_io_error, the on_io_error_context argument given in wsio_open shall be passed to the callback on_io_error. ]*/
470 wsio_instance->on_io_error(wsio_instance->on_io_error_context);
471 }
472 }
473}
474
475int wsio_open(CONCRETE_IO_HANDLE ws_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context)
476{
477 int result = 0;
478
479 if ((ws_io == NULL) ||
480 (on_io_open_complete == NULL) ||
481 (on_bytes_received == NULL) ||
482 (on_io_error == NULL))
483 {
484 /* Codes_SRS_WSIO_01_132: [ If any of the arguments ws_io, on_io_open_complete, on_bytes_received, on_io_error is NULL, wsio_open shall fail and return a non-zero value. ]*/
485 LogError("Bad arguments: ws_io=%p, on_io_open_complete=%p, on_bytes_received=%p, on_io_error=%p",
486 ws_io, on_io_open_complete, on_bytes_received, on_io_error);
487 result = MU_FAILURE;
488 }
489 else
490 {
491 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io;
492
493 if (wsio_instance->io_state != IO_STATE_NOT_OPEN)
494 {
495 /* Codes_SRS_WSIO_01_165: [ wsio_open when CLOSING shall fail and return a non-zero value. ]*/
496 /* Codes_SRS_WSIO_01_131: [ wsio_open when already OPEN or OPENING shall fail and return a non-zero value. ]*/
497 LogError("wsio has already been opened current state: %d", wsio_instance->io_state);
498 result = MU_FAILURE;
499 }
500 else
501 {
502 wsio_instance->on_bytes_received = on_bytes_received;
503 wsio_instance->on_bytes_received_context = on_bytes_received_context;
504 wsio_instance->on_io_open_complete = on_io_open_complete;
505 wsio_instance->on_io_open_complete_context = on_io_open_complete_context;
506 wsio_instance->on_io_error = on_io_error;
507 wsio_instance->on_io_error_context = on_io_error_context;
508
509 wsio_instance->io_state = IO_STATE_OPENING;
510
511 /* Codes_SRS_WSIO_01_082: [ wsio_open shall open the underlying uws instance by calling uws_client_open_async and providing the uws handle created in wsio_create as argument. ] */
512 if (uws_client_open_async(wsio_instance->uws, on_underlying_ws_open_complete, wsio_instance, on_underlying_ws_frame_received, wsio_instance, on_underlying_ws_peer_closed, wsio_instance, on_underlying_ws_error, wsio_instance) != 0)
513 {
514 /* Codes_SRS_WSIO_01_084: [ If opening the underlying uws instance fails then wsio_open shall fail and return a non-zero value. ]*/
515 LogError("Opening the uws instance failed.");
516 wsio_instance->io_state = IO_STATE_NOT_OPEN;
517 result = MU_FAILURE;
518 }
519 else
520 {
521 /* Codes_SRS_WSIO_01_083: [ On success, wsio_open shall return 0. ]*/
522 result = 0;
523 }
524 }
525 }
526
527 return result;
528}
529
530int wsio_close(CONCRETE_IO_HANDLE ws_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context)
531{
532 int result = 0;
533
534 if (ws_io == NULL)
535 {
536 /* Codes_SRS_WSIO_01_086: [ if ws_io is NULL, wsio_close shall return a non-zero value. ]*/
537 LogError("NULL handle");
538 result = MU_FAILURE;
539 }
540 else
541 {
542 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io;
543
544 if (internal_close(wsio_instance, on_io_close_complete, on_io_close_complete_context) != 0)
545 {
546 result = MU_FAILURE;
547 }
548 else
549 {
550 result = 0;
551 }
552 }
553
554 return result;
555}
556
557int wsio_send(CONCRETE_IO_HANDLE ws_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context)
558{
559 int result;
560
561 /* Codes_SRS_WSIO_01_100: [ If any of the arguments ws_io or buffer are NULL, wsio_send shall fail and return a non-zero value. ]*/
562 if ((ws_io == NULL) ||
563 (buffer == NULL) ||
564 /* Codes_SRS_WSIO_01_101: [ If size is zero then wsio_send shall fail and return a non-zero value. ]*/
565 (size == 0))
566 {
567 LogError("Bad arguments: ws_io=%p, buffer=%p, size=%u",
568 ws_io, buffer, (unsigned int)size);
569 result = MU_FAILURE;
570 }
571 else
572 {
573 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io;
574
575 if (wsio_instance->io_state != IO_STATE_OPEN)
576 {
577 /* Codes_SRS_WSIO_01_099: [ If the wsio is not OPEN (open has not been called or is still in progress) then wsio_send shall fail and return a non-zero value. ]*/
578 LogError("Attempting to send when not open");
579 result = MU_FAILURE;
580 }
581 else
582 {
583 LIST_ITEM_HANDLE new_item;
584 PENDING_IO* pending_socket_io = (PENDING_IO*)malloc(sizeof(PENDING_IO));
585 if (pending_socket_io == NULL)
586 {
587 /* Codes_SRS_WSIO_01_134: [ If allocating memory for the pending IO data fails, wsio_send shall fail and return a non-zero value. ]*/
588 result = MU_FAILURE;
589 }
590 else
591 {
592 /* Codes_SRS_WSIO_01_103: [ The entry shall contain the on_send_complete callback and its context. ]*/
593 pending_socket_io->on_send_complete = on_send_complete;
594 pending_socket_io->callback_context = callback_context;
595 pending_socket_io->wsio = wsio_instance;
596
597 /* Codes_SRS_WSIO_01_102: [ An entry shall be queued in the singly linked list by calling singlylinkedlist_add. ]*/
598 if ((new_item = singlylinkedlist_add(wsio_instance->pending_io_list, pending_socket_io)) == NULL)
599 {
600 /* Codes_SRS_WSIO_01_104: [ If singlylinkedlist_add fails, wsio_send shall fail and return a non-zero value. ]*/
601 free(pending_socket_io);
602 result = MU_FAILURE;
603 }
604 else
605 {
606 /* Codes_SRS_WSIO_01_095: [ wsio_send shall call uws_client_send_frame_async, passing the buffer and size arguments as they are: ]*/
607 /* Codes_SRS_WSIO_01_097: [ The is_final argument shall be set to true. ]*/
608 /* Codes_SRS_WSIO_01_096: [ The frame type used shall be WS_FRAME_TYPE_BINARY. ]*/
609 if (uws_client_send_frame_async(wsio_instance->uws, WS_FRAME_TYPE_BINARY, (const unsigned char*)buffer, size, true, on_underlying_ws_send_frame_complete, new_item) != 0)
610 {
611 if (singlylinkedlist_remove(wsio_instance->pending_io_list, new_item) != 0)
612 {
613 LogError("Failed removing pending IO from linked list.");
614 }
615
616 free(pending_socket_io);
617 result = MU_FAILURE;
618 }
619 else
620 {
621 /* Codes_SRS_WSIO_01_098: [ On success, wsio_send shall return 0. ]*/
622 result = 0;
623 }
624 }
625 }
626 }
627 }
628
629 return result;
630}
631
632void wsio_dowork(CONCRETE_IO_HANDLE ws_io)
633{
634 if (ws_io == NULL)
635 {
636 /* Codes_SRS_WSIO_01_107: [ If the ws_io argument is NULL, wsio_dowork shall do nothing. ]*/
637 LogError("NULL handle");
638 }
639 else
640 {
641 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io;
642
643 /* Codes_SRS_WSIO_01_108: [ If the IO is not yet open, wsio_dowork shall do nothing. ]*/
644 if (wsio_instance->io_state != IO_STATE_NOT_OPEN)
645 {
646 /* Codes_SRS_WSIO_01_106: [ wsio_dowork shall call uws_client_dowork with the uws handle created in wsio_create. ]*/
647 uws_client_dowork(wsio_instance->uws);
648 }
649 }
650}
651
652int wsio_setoption(CONCRETE_IO_HANDLE ws_io, const char* optionName, const void* value)
653{
654 int result;
655 if (
656 /* Codes_SRS_WSIO_01_109: [ If any of the arguments ws_io or option_name is NULL wsio_setoption shall return a non-zero value. ]*/
657 (ws_io == NULL) ||
658 (optionName == NULL)
659 )
660 {
661 LogError("Bad parameters: ws_io=%p, optionName=%p",
662 ws_io, optionName);
663 result = MU_FAILURE;
664 }
665 else
666 {
667 WSIO_INSTANCE* wsio_instance = (WSIO_INSTANCE*)ws_io;
668
669 if (strcmp(WSIO_OPTIONS, optionName) == 0)
670 {
671 /* Codes_SRS_WSIO_01_183: [ If the option name is WSIOOptions then wsio_setoption shall call OptionHandler_FeedOptions and pass to it the underlying IO handle and the value argument. ]*/
672 if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, wsio_instance->uws) != OPTIONHANDLER_OK)
673 {
674 /* Codes_SRS_WSIO_01_184: [ If OptionHandler_FeedOptions fails, wsio_setoption shall fail and return a non-zero value. ]*/
675 LogError("unable to OptionHandler_FeedOptions");
676 result = MU_FAILURE;
677 }
678 else
679 {
680 /* Codes_SRS_WSIO_01_158: [ On success, wsio_setoption shall return 0. ]*/
681 result = 0;
682 }
683 }
684 else
685 {
686 /* Codes_SRS_WSIO_01_156: [ Otherwise all options shall be passed as they are to uws by calling uws_client_set_option. ]*/
687 if (uws_client_set_option(wsio_instance->uws, optionName, value) != 0)
688 {
689 /* Codes_SRS_WSIO_01_157: [ If uws_client_set_option fails, wsio_setoption shall fail and return a non-zero value. ]*/
690 LogError("Setting the option %s failed", optionName);
691 result = MU_FAILURE;
692 }
693 else
694 {
695 /* Codes_SRS_WSIO_01_158: [ On success, wsio_setoption shall return 0. ]*/
696 result = 0;
697 }
698 }
699 }
700
701 return result;
702}
703
704static void* wsio_clone_option(const char* name, const void* value)
705{
706 void *result;
707
708 if (
709 (name == NULL) ||
710 (value == NULL)
711 )
712 {
713 /* Codes_SRS_WSIO_01_174: [ If wsio_clone_option is called with NULL name or value it shall return NULL. ]*/
714 LogError("invalid argument detected: const char* name=%p, const void* value=%p", name, value);
715 result = NULL;
716 }
717 else
718 {
719 if (strcmp(name, WSIO_OPTIONS) == 0)
720 {
721 /* Codes_SRS_WSIO_01_171: [** wsio_clone_option called with name being WSIOOptions shall return the same value. ]*/
722 result = (void*)value;
723 }
724 else
725 {
726 /* Codes_SRS_WSIO_01_173: [ wsio_clone_option called with any other option name than WSIOOptions shall return NULL. ]*/
727 LogError("unknown option: %s", name);
728 result = NULL;
729 }
730 }
731
732 return result;
733}
734
735static void wsio_destroy_option(const char* name, const void* value)
736{
737 if (
738 (name == NULL) ||
739 (value == NULL)
740 )
741 {
742 /* Codes_SRS_WSIO_01_177: [ If wsio_destroy_option is called with NULL name or value it shall do nothing. ]*/
743 LogError("Bad arguments: const char* name=%p, const void* value=%p", name, value);
744 }
745 else
746 {
747 if (strcmp(name, WSIO_OPTIONS) == 0)
748 {
749 /* Codes_SRS_WSIO_01_175: [ wsio_destroy_option called with the option name being WSIOOptions shall destroy the value by calling OptionHandler_Destroy. ]*/
750 OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value);
751 }
752 else
753 {
754 /* Codes_SRS_WSIO_01_176: [ If wsio_destroy_option is called with any other name it shall do nothing. ]*/
755 LogError("unknown option: %s", name);
756 }
757 }
758}
759
760OPTIONHANDLER_HANDLE wsio_retrieveoptions(CONCRETE_IO_HANDLE handle)
761{
762 OPTIONHANDLER_HANDLE result;
763 if (handle == NULL)
764 {
765 /* Codes_SRS_WSIO_01_118: [ If parameter handle is NULL then wsio_retrieveoptions shall fail and return NULL. ]*/
766 LogError("parameter handle is NULL");
767 result = NULL;
768 }
769 else
770 {
771 WSIO_INSTANCE* wsio = (WSIO_INSTANCE*)handle;
772
773 /* Codes_SRS_WSIO_01_119: [ wsio_retrieveoptions shall call OptionHandler_Create to produce an OPTIONHANDLER_HANDLE and on success return the new OPTIONHANDLER_HANDLE handle. ]*/
774 result = OptionHandler_Create(wsio_clone_option, wsio_destroy_option, wsio_setoption);
775 if (result == NULL)
776 {
777 /* Codes_SRS_WSIO_01_120: [ If OptionHandler_Create fails then wsio_retrieveoptions shall fail and return NULL. ]*/
778 LogError("OptionHandler_Create failed");
779 }
780 else
781 {
782 /* Codes_SRS_WSIO_01_179: [ When calling uws_client_retrieve_options the uws client handle shall be passed to it. ]*/
783 /* Codes_SRS_WSIO_01_178: [ uws_client_retrieve_options shall add to the option handler one option, whose name shall be uWSCLientOptions and the value shall be queried by calling uws_client_retrieve_options. ]*/
784 OPTIONHANDLER_HANDLE concreteOptions = uws_client_retrieve_options(wsio->uws);
785 if (concreteOptions == NULL)
786 {
787 /* Codes_SRS_WSIO_01_180: [ If uws_client_retrieve_options fails, uws_client_retrieve_options shall fail and return NULL. ]*/
788 LogError("unable to concrete_io_retrieveoptions");
789 OptionHandler_Destroy(result);
790 result = NULL;
791 }
792 else
793 {
794 /* Codes_SRS_WSIO_01_181: [ Adding the option shall be done by calling OptionHandler_AddOption. ]*/
795 if (OptionHandler_AddOption(result, WSIO_OPTIONS, concreteOptions) != OPTIONHANDLER_OK)
796 {
797 /* Codes_SRS_WSIO_01_182: [ If OptionHandler_AddOption fails, uws_client_retrieve_options shall fail and return NULL. ]*/
798 LogError("unable to OptionHandler_AddOption");
799 OptionHandler_Destroy(concreteOptions);
800 OptionHandler_Destroy(result);
801 result = NULL;
802 }
803 }
804 }
805 }
806
807 return result;
808}
809
810static const IO_INTERFACE_DESCRIPTION ws_io_interface_description =
811{
812 wsio_retrieveoptions,
813 wsio_create,
814 wsio_destroy,
815 wsio_open,
816 wsio_close,
817 wsio_send,
818 wsio_dowork,
819 wsio_setoption
820};
821
822const IO_INTERFACE_DESCRIPTION* wsio_get_interface_description(void)
823{
824 return &ws_io_interface_description;
825}
Note: See TracBrowser for help on using the repository browser.