File Coverage

EVx.xs
Criterion Covered Total %
statement 256 330 77.5
branch 115 234 49.1
condition n/a
subroutine n/a
pod n/a
total 371 564 65.7


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #include "wslay/wslay.h"
5             #include "EVAPI.h"
6              
7             //windows
8             #ifdef WIN32
9             #ifndef EWOULDBLOCK
10             #define EWOULDBLOCK WSAEWOULDBLOCK
11             #endif
12             #else
13             #ifndef EWOULDBLOCK
14             #define EWOULDBLOCK EAGAIN
15             #endif
16             #endif
17              
18             #define FRAGMENTED_EOF 0
19             #define FRAGMENTED_ERROR -1
20             #define FRAGMENTED_DATA 1
21              
22             #define REQUIRE_CTX(ws) if (!(ws)->ctx) { croak("WebSocket connection already closed"); }
23              
24             typedef struct {
25             wslay_event_context_ptr ctx;
26             HV* perl_callbacks;
27             ev_io io;
28             SV* queue_wait_cb;
29             struct wslay_event_callbacks callbacks;
30             char read_stopped;
31             char write_stopped;
32             } websocket_object;
33              
34             static void wait_io_event(websocket_object* websock_object);
35              
36 15           static ssize_t recv_callback(wslay_event_context_ptr ctx, uint8_t* buf, size_t len, int flags, void* data) {
37 15           websocket_object* websock_object = (websocket_object*) data;
38             ssize_t r;
39 15 100         while ((r = recv(websock_object->io.fd, buf, len, 0)) == -1 && errno == EINTR);
    50          
40 15 100         if (r == -1) {
41 7 50         if (errno == EAGAIN || errno == EWOULDBLOCK) {
    0          
42 7           wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
43             } else {
44 0           wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
45             }
46 8 50         } else if (r == 0) { /* Unexpected EOF is also treated as an error */
47 0           wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
48 0           r = -1;
49             }
50 15           return r;
51             }
52              
53 18           static ssize_t send_callback(wslay_event_context_ptr ctx, const uint8_t* buf, size_t len, int flags, void* data) {
54 18           websocket_object* websock_object = (websocket_object*) data;
55             ssize_t r;
56 18           int sflags = 0;
57             #ifdef MSG_MORE
58 18 100         if(flags & WSLAY_MSG_MORE) { sflags |= MSG_MORE; }
59             #endif // MSG_MORE
60 18 50         while ((r = send(websock_object->io.fd, buf, len, sflags)) == -1 && errno == EINTR);
    0          
61 18 50         if (r == -1) {
62 0 0         if(errno == EAGAIN || errno == EWOULDBLOCK) {
    0          
63 0           wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
64             } else {
65 0           wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
66             }
67             }
68 18           return r;
69             }
70              
71 35           static websocket_object* get_wslay_context (HV* hv) {
72             MAGIC* mg;
73 35 50         for (mg = SvMAGIC((SV*) hv); mg; mg = mg->mg_moremagic) {
74 35 50         if (mg->mg_type == PERL_MAGIC_ext && mg->mg_virtual == NULL) {
    50          
75 35           return (websocket_object*) mg->mg_ptr;
76             }
77             }
78 0           croak("Can't get ptr from object hash!\n");
79             }
80              
81 5           static int genmask_callback(wslay_event_context_ptr ctx, uint8_t* buf, size_t len, void* data) {
82 5           websocket_object* websock_object = (websocket_object*) data;
83             SV** cb;
84 5 50         if ((cb = hv_fetch(websock_object->perl_callbacks , "genmask", 7, 0))) {
85             int count;
86             SV* sv_data;
87             STRLEN source_len;
88             char *source_buf;
89 0           dSP;
90 0           ENTER;
91 0           SAVETMPS;
92 0 0         PUSHMARK(SP);
93 0 0         XPUSHs(sv_2mortal(newSViv(len)));
94 0           PUTBACK;
95 0           count = call_sv(*cb, G_SCALAR);
96 0           SPAGAIN;
97 0 0         if (count != 1) { croak("Wslay - genmask callback returned bad value!\n"); }
98 0           sv_data = POPs;
99 0           source_buf = SvPV(sv_data, source_len);
100 0 0         if (source_len) { memcpy(buf, source_buf, (source_len < len ? source_len : len)); }
101 0           PUTBACK;
102 0 0         FREETMPS;
103 0           LEAVE;
104 0           return 0;
105             };
106             {
107             size_t i;
108 25 100         for(i = 0; i < len; i++){ buf[i] = (char) rand(); }
109             }
110 5           return 0;
111             }
112              
113 8           static void on_frame_recv_start_callback (wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_start_arg* frame, void* data) {
114             SV** cb;
115 8 100         if (!(cb = hv_fetch(((websocket_object*) data)->perl_callbacks, "on_frame_recv_start", 19, 0)) ) {
116 4           return;
117             }
118 4           dSP;
119 4           ENTER;
120 4           SAVETMPS;
121 4 50         PUSHMARK(SP);
122 4 50         EXTEND(SP, 4);
123 4           PUSHs(sv_2mortal(newSViv(frame->fin)));
124 4           PUSHs(sv_2mortal(newSViv(frame->rsv)));
125 4           PUSHs(sv_2mortal(newSViv(frame->opcode)));
126 4           PUSHs(sv_2mortal(newSVuv(frame->payload_length)));
127 4           PUTBACK;
128 4           call_sv(*cb, G_VOID);
129 4 50         FREETMPS;
130 4           LEAVE;
131             }
132              
133 8           static void on_frame_recv_chunk_callback (wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_chunk_arg* chunk, void* data) {
134             SV** cb;
135 8 100         if (!(cb = hv_fetch(((websocket_object*) data)->perl_callbacks, "on_frame_recv_chunk", 19, 0))) {
136 4           return;
137             }
138 4           dSP;
139 4           ENTER;
140 4           SAVETMPS;
141 4 50         PUSHMARK(SP);
142 4 50         EXTEND(SP, 1);
143 4           PUSHs(sv_2mortal(newSVpvn(chunk->data, chunk->data_length)));
144 4           PUTBACK;
145 4           call_sv(*cb, G_VOID);
146 4 50         FREETMPS;
147 4           LEAVE;
148             }
149              
150 8           static void on_frame_recv_end_callback(wslay_event_context_ptr ctx, void* data) {
151             SV** cb;
152 8 100         if (!(cb = hv_fetch(((websocket_object*) data)->perl_callbacks, "on_frame_recv_end", 17, 0))) {
153 4           return;
154             }
155 4           dSP;
156 4           ENTER;
157 4           SAVETMPS;
158 4 50         PUSHMARK(SP);
159 4           call_sv(*cb, G_DISCARD|G_NOARGS);
160 4 50         FREETMPS;
161 4           LEAVE;
162             }
163              
164 7           static void on_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg* msg, void* data) {
165             SV** cb;
166             SV* msg_data;
167 7 100         if (msg->opcode == 0x08) { return; }
168 6 50         if (!(cb = hv_fetch(((websocket_object*) data)->perl_callbacks, "on_msg_recv", 11, 0))) {
169 0           return;
170             }
171 6           msg_data = newSVpvn(msg->msg, msg->msg_length);
172 6 100         if (!(msg->rsv & WSLAY_RSV1_BIT) && msg->opcode == 1) { SvUTF8_on(msg_data); }
    100          
173 6           dSP;
174 6           ENTER;
175 6           SAVETMPS;
176 6 50         PUSHMARK(SP);
177 6 50         EXTEND(SP, 4);
178 6           PUSHs(sv_2mortal(newSViv(msg->rsv)));
179 6           PUSHs(sv_2mortal(newSViv(msg->opcode)));
180 6           PUSHs(sv_2mortal(msg_data));
181 6           PUSHs(sv_2mortal(newSViv(msg->status_code)));
182 6           PUTBACK;
183 6           call_sv(*cb, G_VOID);
184 6 50         FREETMPS;
185 6           LEAVE;
186             }
187              
188 3           static ssize_t fragmented_msg_callback(wslay_event_context_ptr ctx, uint8_t* buf, size_t len, const union wslay_event_msg_source* source, int* eof, void* userdata) {
189 3           websocket_object* websock_object = (websocket_object*) userdata;
190 3           ssize_t bytes_written = 0;
191             int count;
192             SV* data;
193             int status;
194             STRLEN source_len;
195             char* source_buf;
196 3           dSP;
197 3           ENTER;
198 3           SAVETMPS;
199 3 50         PUSHMARK(SP);
200 3 50         XPUSHs(sv_2mortal(newSViv(len)));
201 3           PUTBACK;
202 3           count = call_sv((SV*) source->data, G_ARRAY);
203 3           SPAGAIN;
204 3 100         if (count == 1) {
205 2           status = FRAGMENTED_DATA;
206 2           data = POPs;
207 1 50         } else if (count == 2) {
208 1           status = POPi;
209 1           data = POPs;
210             } else {
211 0           croak("Wslay - fragmented msg cb MUST return one or two elements! \n");
212             }
213 3           source_buf = SvPV(data, source_len);
214 3 100         if (source_len) {
215 2           bytes_written = (source_len < len ? source_len : len );
216 2           memcpy(buf, source_buf, bytes_written);
217             }
218 3           PUTBACK;
219 3 50         FREETMPS;
220 3           LEAVE;
221 3 100         if (status == FRAGMENTED_EOF) {
222 1           *eof = 1;
223 1           SvREFCNT_dec((SV*) source->data);
224 2 50         } else if (status == FRAGMENTED_ERROR) {
225 0           bytes_written = -1;
226 0           wslay_event_set_error(websock_object->ctx, WSLAY_ERR_CALLBACK_FAILURE);
227 0           SvREFCNT_dec((SV*) source->data);
228             }
229             // else - FRAGMENTED_DATA
230 3           return bytes_written;
231             }
232              
233             //////////////////////
234 12           static void close_connection(websocket_object* websock_object) {
235             int status;
236             SV** cb;
237 12 50         if (!websock_object->ctx) { return; }
238 12           status = wslay_event_get_status_code_received(websock_object->ctx);
239 12           wslay_event_context_free(websock_object->ctx);
240 12           websock_object->ctx = NULL;
241 12           ev_io_stop(EV_DEFAULT, &(websock_object->io));
242 12 50         if (websock_object->io.fd >= 0) {
243 12           close(websock_object->io.fd);
244 12           websock_object->io.fd = -1;
245             }
246 12 50         if ((cb = hv_fetch(websock_object->perl_callbacks, "on_close", 8, 0))) {
247 12           dSP;
248 12           ENTER;
249 12           SAVETMPS;
250 12 50         PUSHMARK(SP);
251 12 50         EXTEND(SP, 1);
252 12           PUSHs(sv_2mortal(newSViv(status)));
253 12           PUTBACK;
254 12           call_sv(*cb, G_VOID);
255 12 50         FREETMPS;
256 12           LEAVE;
257             };
258             }
259              
260 17           static void wslay_io_event (struct ev_loop* loop, struct ev_io* w, int revents) {
261 17           websocket_object* websock_object = (websocket_object*) w->data;
262 17 100         if (revents & EV_READ) {
263 8 50         if (wslay_event_recv(websock_object->ctx)) {
264 0           close_connection(websock_object);
265 0           return;
266             }
267             }
268 17 50         if (!websock_object->ctx) { return; }
269 17 100         if (revents & EV_WRITE) {
270 9 50         if (wslay_event_send(websock_object->ctx)) {
271 0           close_connection(websock_object);
272 0           return;
273             }
274             }
275 17           wait_io_event(websock_object);
276             };
277              
278 39           static void wait_io_event(websocket_object* websock_object) {
279 39           int events = 0;
280 39           char wanted_io = 0;
281 39           ev_io_stop(EV_DEFAULT, &(websock_object->io));
282 39 50         if (websock_object->read_stopped && websock_object->write_stopped) { return; }
    0          
283 39 100         if (wslay_event_want_read(websock_object->ctx)) {
284 35 50         if (!websock_object->read_stopped) { events |= EV_READ; }
285 35           wanted_io = 1;
286             }
287 39 100         if (wslay_event_want_write(websock_object->ctx)) {
288 12 100         if (!websock_object->write_stopped) { events |= EV_WRITE; }
289 12           wanted_io = 1;
290 27           } else if (
291 28           websock_object->queue_wait_cb &&
292 1           !wslay_event_get_queued_msg_count(websock_object->ctx)
293             ) {
294 1           SV* wait_cb = websock_object->queue_wait_cb;
295 1           websock_object->queue_wait_cb = NULL;
296 1           SvREFCNT_inc((SV*)websock_object->perl_callbacks);
297             {
298 1           dSP;
299 1           ENTER;
300 1           SAVETMPS;
301 1 50         PUSHMARK(SP);
302 1           call_sv(wait_cb, G_DISCARD|G_NOARGS);
303 1 50         FREETMPS;
304 1           LEAVE;
305             }
306 1           SvREFCNT_dec(wait_cb);
307             /* recheck want write - safe because HV refcount prevents DESTROY */
308 1 50         if (websock_object->ctx && wslay_event_want_write(websock_object->ctx)) {
    50          
309 0 0         if (!websock_object->write_stopped) { events |= EV_WRITE; }
310 0           wanted_io = 1;
311             }
312             {
313 1           int ctx_alive = (websock_object->ctx != NULL);
314 1           SvREFCNT_dec((SV*)websock_object->perl_callbacks);
315 1 50         if (!ctx_alive) { return; }
316             }
317             }
318              
319 39 100         if (events) {
320 37           ev_io_set(&(websock_object->io), websock_object->io.fd, events);
321 37           ev_io_start(EV_DEFAULT, &(websock_object->io));
322 2 50         } else if (!wanted_io && websock_object->ctx) {
    50          
323 2           close_connection(websock_object);
324             }
325              
326             };
327              
328              
329             MODULE = Net::WebSocket::EVx PACKAGE = Net::WebSocket::EVx
330              
331              
332             BOOT:
333             {
334 1 50         I_EV_API("Net::WebSocket::EVx");
    50          
    50          
335             #ifdef WIN32
336             _setmaxstdio(2048);
337             #endif
338             }
339              
340             PROTOTYPES: DISABLE
341              
342             void _wslay_event_context_init(object, sock, is_server)
343             HV* object
344             int sock
345             int is_server
346             CODE:
347 12           websocket_object* websock_object = calloc(1, sizeof(websocket_object));
348 12           ev_io_init(&(websock_object->io), wslay_io_event, sock, EV_READ);
349 12           websock_object->io.data = (SV*) websock_object;
350 12           websock_object->perl_callbacks = object;
351 12           websock_object->callbacks.recv_callback = recv_callback;
352 12           websock_object->callbacks.send_callback = send_callback;
353 12           websock_object->callbacks.genmask_callback = genmask_callback;
354 12           websock_object->callbacks.on_frame_recv_start_callback = on_frame_recv_start_callback;
355 12           websock_object->callbacks.on_frame_recv_chunk_callback = on_frame_recv_chunk_callback;
356 12           websock_object->callbacks.on_frame_recv_end_callback = on_frame_recv_end_callback;
357 12           websock_object->callbacks.on_msg_recv_callback = on_msg_recv_callback;
358 24 100         if (is_server
    50          
359 6           ? wslay_event_context_server_init(&(websock_object->ctx), &(websock_object->callbacks), websock_object)
360 6           : wslay_event_context_client_init(&(websock_object->ctx), &(websock_object->callbacks), websock_object)
361             ) {
362 0           free(websock_object);
363 0           croak("Can't initialize! WSLAY_ERR_NOMEM \n");
364             }
365 12           sv_magicext((SV*) object, 0, PERL_MAGIC_ext, NULL, (const char *) websock_object, 0);
366 12           wslay_event_config_set_allowed_rsv_bits(websock_object->ctx, WSLAY_RSV1_BIT);
367 12           wait_io_event(websock_object);
368              
369             void _wslay_event_config_set_no_buffering (object, buffering)
370             HV* object
371             int buffering
372             CODE:
373 12           websocket_object* websock_object = get_wslay_context(object);
374 12 50         REQUIRE_CTX(websock_object);
375 12           wslay_event_config_set_no_buffering(websock_object->ctx, buffering);
376              
377             void _wslay_event_config_set_max_recv_msg_length(object, len)
378             HV* object
379             UV len
380             CODE:
381 0           websocket_object* websock_object = get_wslay_context(object);
382 0 0         REQUIRE_CTX(websock_object);
383 0           wslay_event_config_set_max_recv_msg_length(websock_object->ctx, len);
384              
385             void shutdown_read(object)
386             HV* object
387             CODE:
388 0           websocket_object* websock_object = get_wslay_context(object);
389 0 0         REQUIRE_CTX(websock_object);
390 0           wslay_event_shutdown_read(websock_object->ctx);
391              
392             void shutdown_write(object)
393             HV* object
394             CODE:
395 0           websocket_object* websock_object = get_wslay_context(object);
396 0 0         REQUIRE_CTX(websock_object);
397 0           wslay_event_shutdown_write(websock_object->ctx);
398              
399             void stop(object)
400             HV* object
401             CODE:
402 0           websocket_object* websock_object = get_wslay_context(object);
403 0 0         REQUIRE_CTX(websock_object);
404 0           websock_object->read_stopped = 1;
405 0           websock_object->write_stopped = 1;
406 0           wait_io_event(websock_object);
407              
408             void stop_read(object)
409             HV* object
410             CODE:
411 0           websocket_object* websock_object = get_wslay_context(object);
412 0 0         REQUIRE_CTX(websock_object);
413 0           websock_object->read_stopped = 1;
414 0           wait_io_event(websock_object);
415              
416             void stop_write(object)
417             HV* object
418             CODE:
419 1           websocket_object* websock_object = get_wslay_context(object);
420 1 50         REQUIRE_CTX(websock_object);
421 1           websock_object->write_stopped = 1;
422 1           wait_io_event(websock_object);
423              
424             void start(object)
425             HV* object
426             CODE:
427 0           websocket_object* websock_object = get_wslay_context(object);
428 0 0         REQUIRE_CTX(websock_object);
429 0           websock_object->read_stopped = 0;
430 0           websock_object->write_stopped = 0;
431 0           wait_io_event(websock_object);
432              
433             void start_read(object)
434             HV* object
435             CODE:
436 0           websocket_object* websock_object = get_wslay_context(object);
437 0 0         REQUIRE_CTX(websock_object);
438 0           websock_object->read_stopped = 0;
439 0           wait_io_event(websock_object);
440              
441             void start_write(object)
442             HV* object
443             CODE:
444 1           websocket_object* websock_object = get_wslay_context(object);
445 1 50         REQUIRE_CTX(websock_object);
446 1           websock_object->write_stopped = 0;
447 1           wait_io_event(websock_object);
448              
449             void _set_waiter(object, waiter)
450             HV* object
451             SV* waiter
452             CODE:
453 1           websocket_object* websock_object = get_wslay_context(object);
454 1 50         REQUIRE_CTX(websock_object);
455 1 50         if (websock_object->queue_wait_cb) { SvREFCNT_dec(websock_object->queue_wait_cb); }
456 1           websock_object->queue_wait_cb = waiter;
457 1           SvREFCNT_inc(waiter);
458 1           wait_io_event(websock_object);
459              
460             int queue_msg (object, data, opcode=1)
461             HV* object
462             SV* data
463             int opcode
464             CODE:
465 4           websocket_object* websock_object = get_wslay_context(object);
466 4 50         REQUIRE_CTX(websock_object);
467             STRLEN len;
468             struct wslay_event_msg msg;
469 4           msg.msg = SvPV(data, len);
470 4           msg.msg_length = len;
471 4           msg.opcode = opcode;
472 4           int result = wslay_event_queue_msg(websock_object->ctx, &msg);
473 4 50         if (result == WSLAY_ERR_INVALID_ARGUMENT) { croak("Wslay queue_msg - WSLAY_ERR_INVALID_ARGUMENT"); }
474 4 50         if (result == WSLAY_ERR_NOMEM) { croak("Wslay queue_msg - WSLAY_ERR_NOMEM"); }
475 4           wait_io_event(websock_object);
476 4 50         RETVAL = result;
477             OUTPUT:
478             RETVAL
479              
480             int queue_msg_ex (object, data, opcode=1, rsv=WSLAY_RSV1_BIT)
481             HV* object
482             SV* data
483             int opcode
484             int rsv
485             CODE:
486 1           websocket_object* websock_object = get_wslay_context(object);
487 1 50         REQUIRE_CTX(websock_object);
488             STRLEN len;
489             struct wslay_event_msg msg;
490 1           msg.msg = SvPV(data, len);
491 1           msg.msg_length = len;
492 1           msg.opcode = opcode;
493 1           int result = wslay_event_queue_msg_ex(websock_object->ctx, &msg, rsv);
494 1 50         if (result == WSLAY_ERR_INVALID_ARGUMENT) { croak("Wslay queue_msg_ex - WSLAY_ERR_INVALID_ARGUMENT"); }
495 1 50         if (result == WSLAY_ERR_NOMEM) { croak("Wslay queue_msg_ex - WSLAY_ERR_NOMEM"); }
496 1           wait_io_event(websock_object);
497 1 50         RETVAL = result;
498             OUTPUT:
499             RETVAL
500              
501             int queue_fragmented (object, cb, opcode=2)
502             HV* object
503             SV* cb
504             int opcode
505             CODE:
506 1           websocket_object* websock_object = get_wslay_context(object);
507 1 50         REQUIRE_CTX(websock_object);
508             struct wslay_event_fragmented_msg msg;
509 1           msg.opcode = opcode;
510 1           msg.source.data = SvREFCNT_inc(cb);
511 1           msg.read_callback = fragmented_msg_callback;
512 1           int result = wslay_event_queue_fragmented_msg(websock_object->ctx, &msg);
513 1 50         if (result == WSLAY_ERR_INVALID_ARGUMENT) { SvREFCNT_dec(cb); croak("Wslay queue_fragmented - WSLAY_ERR_INVALID_ARGUMENT"); }
514 1 50         if (result == WSLAY_ERR_NOMEM) { SvREFCNT_dec(cb); croak("Wslay queue_fragmented - WSLAY_ERR_NOMEM"); }
515 1 50         if (result) { SvREFCNT_dec(cb); }
516 1           wait_io_event(websock_object);
517 1 50         RETVAL = result;
518             OUTPUT:
519             RETVAL
520              
521             int queue_fragmented_ex (object, cb, opcode=2, rsv=WSLAY_RSV1_BIT)
522             HV* object
523             SV* cb
524             int opcode
525             int rsv
526             CODE:
527 0           websocket_object* websock_object = get_wslay_context(object);
528 0 0         REQUIRE_CTX(websock_object);
529             struct wslay_event_fragmented_msg msg;
530 0           msg.opcode = opcode;
531 0           msg.source.data = SvREFCNT_inc(cb);
532 0           msg.read_callback = fragmented_msg_callback;
533 0           int result = wslay_event_queue_fragmented_msg_ex(websock_object->ctx, &msg, rsv);
534 0 0         if (result == WSLAY_ERR_INVALID_ARGUMENT) { SvREFCNT_dec(cb); croak("Wslay queue_fragmented_ex - WSLAY_ERR_INVALID_ARGUMENT"); }
535 0 0         if (result == WSLAY_ERR_NOMEM) { SvREFCNT_dec(cb); croak("Wslay queue_fragmented_ex - WSLAY_ERR_NOMEM"); }
536 0 0         if (result) { SvREFCNT_dec(cb); }
537 0           wait_io_event(websock_object);
538 0 0         RETVAL = result;
539             OUTPUT:
540             RETVAL
541              
542             int close (object, status_code = 0, data = NULL)
543             HV* object
544             int status_code
545             SV* data
546             CODE:
547 1           websocket_object* websock_object = get_wslay_context(object);
548 1 50         REQUIRE_CTX(websock_object);
549 1           STRLEN reason_length = 0;
550 1           char *reason = NULL;
551 1 50         if (data) { reason = SvPV(data, reason_length); }
552 1           int result = wslay_event_queue_close(websock_object->ctx, status_code, reason, reason_length);
553 1 50         if (result == WSLAY_ERR_INVALID_ARGUMENT) {croak("Wslay close - WSLAY_ERR_INVALID_ARGUMENT"); }
554 1 50         if (result == WSLAY_ERR_NOMEM) { croak("Wslay close - WSLAY_ERR_NOMEM"); }
555 1           wslay_event_shutdown_read(websock_object->ctx);
556 1           wait_io_event(websock_object);
557 1 50         RETVAL = result;
558             OUTPUT:
559             RETVAL
560              
561             UV queued_count (object)
562             HV* object
563             CODE:
564 1           websocket_object* websock_object = get_wslay_context(object);
565 1 50         REQUIRE_CTX(websock_object);
566 1           RETVAL = wslay_event_get_queued_msg_count(websock_object->ctx);
567             OUTPUT:
568             RETVAL
569              
570             void DESTROY (object)
571             HV* object
572             CODE:
573 12           websocket_object* websock_object = get_wslay_context(object);
574 12 50         if (websock_object->queue_wait_cb) { SvREFCNT_dec(websock_object->queue_wait_cb); }
575 12 100         if (websock_object->ctx) { close_connection(websock_object); }
576 12           free(websock_object);