source: azure_iot_hub/trunk/azure_iohub/c-utility/src/wsio.c@ 388

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

Azure IoT Hub Device C SDK を使ったサンプルの追加

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