File Coverage

lib/Hypersonic/WebSocket.pm
Criterion Covered Total %
statement 106 108 98.1
branch n/a
condition 2 4 50.0
subroutine 37 39 94.8
pod 0 30 0.0
total 145 181 80.1


line stmt bran cond sub pod time code
1             package Hypersonic::WebSocket;
2 2     2   269099 use strict;
  2         4  
  2         62  
3 2     2   7 use warnings;
  2         1  
  2         81  
4 2     2   25 use 5.010;
  2         8  
5              
6             # Hypersonic::WebSocket - High-level WebSocket connection API
7             #
8             # Provides an event-driven interface for WebSocket connections:
9             # $ws->on(open => sub { ... });
10             # $ws->on(message => sub { my ($data) = @_; ... });
11             # $ws->on(close => sub { my ($code, $reason) = @_; ... });
12             # $ws->send($data);
13             # $ws->close();
14              
15             our $VERSION = '0.15';
16              
17 2     2   19 use Scalar::Util ();
  2         1  
  2         40  
18 2     2   542 use Hypersonic::Protocol::WebSocket;
  2         5  
  2         128  
19 2     2   1244 use Hypersonic::Protocol::WebSocket::Frame;
  2         4  
  2         90  
20              
21             # Connection states
22             use constant {
23 2         145 STATE_CONNECTING => 0,
24             STATE_OPEN => 1,
25             STATE_CLOSING => 2,
26             STATE_CLOSED => 3,
27 2     2   16 };
  2         3  
28 2     2   7 use constant MAX_WEBSOCKETS => 65536;
  2         3  
  2         8927  
29              
30             # State constants for external use
31 1     1 0 5920 sub CONNECTING { STATE_CONNECTING }
32 0     0   0 sub OPEN { STATE_OPEN }
33 2     2 0 879 sub CLOSING { STATE_CLOSING }
34 0     0 0 0 sub CLOSED { STATE_CLOSED }
35              
36             # ============================================================
37             # XS Code Generation - ALL instance methods generated in C
38             # ============================================================
39              
40             sub generate_c_code {
41 2     2 0 35 my ($class, $builder, $opts) = @_;
42 2   50     16 $opts //= {};
43 2   50     15 my $max = $opts->{max_websockets} // MAX_WEBSOCKETS;
44              
45 2         11 $class->gen_websocket_registry($builder, $max);
46 2         7 $class->gen_ws_helpers($builder);
47 2         9 $class->gen_xs_new($builder);
48 2         7 $class->gen_xs_fd($builder);
49 2         5 $class->gen_xs_state($builder);
50 2         9 $class->gen_xs_protocol($builder);
51 2         9 $class->gen_xs_stream($builder);
52 2         8 $class->gen_xs_request($builder);
53 2         7 $class->gen_xs_is_open($builder);
54 2         6 $class->gen_xs_is_closing($builder);
55 2         5 $class->gen_xs_is_closed($builder);
56 2         10 $class->gen_xs_on($builder);
57 2         11 $class->gen_xs_emit($builder);
58 2         10 $class->gen_xs_accept($builder);
59 2         13 $class->gen_xs_send($builder);
60 2         12 $class->gen_xs_send_binary($builder);
61 2         10 $class->gen_xs_ping($builder);
62 2         12 $class->gen_xs_pong($builder);
63 2         32 $class->gen_xs_close($builder);
64 2         12 $class->gen_xs_handle_close($builder);
65 2         10 $class->gen_xs_handle_message($builder);
66 2         9 $class->gen_xs_process_data($builder);
67 2         11 $class->gen_xs_flush_send_buffer($builder);
68 2         14 $class->gen_xs_param($builder);
69 2         22 $class->gen_xs_header($builder);
70              
71 2         9 return $builder;
72             }
73              
74             sub gen_websocket_registry {
75 2     2 0 4 my ($class, $builder, $max) = @_;
76              
77 2         40 $builder->comment('WebSocket connection registry - O(1) lookup by fd')
78             ->line('#define WS_MAX ' . $max)
79             ->line('#define WS_STATE_CONNECTING 0')
80             ->line('#define WS_STATE_OPEN 1')
81             ->line('#define WS_STATE_CLOSING 2')
82             ->line('#define WS_STATE_CLOSED 3')
83             ->blank
84             ->line('typedef struct {')
85             ->line(' int state;')
86             ->line(' int close_code;')
87             ->line(' char protocol[128];')
88             ->line(' char close_reason[128];')
89             ->line(' SV* ws_object;') # Store the WebSocket Perl object
90             ->line('} WSConnectionState;')
91             ->blank
92             ->line('static WSConnectionState ws_registry[WS_MAX];')
93             ->blank;
94             }
95              
96             sub gen_ws_helpers {
97 2     2 0 4 my ($class, $builder) = @_;
98              
99 2         20 $builder->comment('Reset WebSocket connection state')
100             ->line('static void ws_reset(int fd) {')
101             ->line(' if (fd < 0 || fd >= WS_MAX) return;')
102             ->line(' if (ws_registry[fd].ws_object) {')
103             ->line(' SvREFCNT_dec(ws_registry[fd].ws_object);')
104             ->line(' }')
105             ->line(' memset(&ws_registry[fd], 0, sizeof(WSConnectionState));')
106             ->line(' ws_registry[fd].state = WS_STATE_CONNECTING;')
107             ->line(' ws_registry[fd].close_code = 0;')
108             ->line(' ws_registry[fd].ws_object = NULL;')
109             ->line('}')
110             ->blank;
111              
112 2         23 $builder->comment('Set WebSocket state')
113             ->line('static void ws_set_state(int fd, int state) {')
114             ->line(' if (fd >= 0 && fd < WS_MAX) {')
115             ->line(' ws_registry[fd].state = state;')
116             ->line(' }')
117             ->line('}')
118             ->blank;
119             }
120              
121             sub gen_xs_new {
122 2     2 0 3 my ($class, $builder) = @_;
123              
124 2         240 $builder->xs_function('xs_websocket_new')
125             ->xs_preamble
126             ->line('HV* self_hv = newHV();')
127             ->line('SV* self_rv = newRV_noinc((SV*)self_hv);')
128             ->line('sv_bless(self_rv, gv_stashpv("Hypersonic::WebSocket", GV_ADD));')
129             ->blank
130             ->line('int fd = -1;')
131             ->line('SV* stream_sv = NULL;')
132             ->line('SV* request_sv = NULL;')
133             ->line('SV* protocol_sv = NULL;')
134             ->line('IV max_message_size = 16 * 1024 * 1024;')
135             ->blank
136             ->comment('First arg is stream object')
137             ->if('items >= 2')
138             ->line('stream_sv = ST(1);')
139             ->endif
140             ->blank
141             ->comment('Parse hash args: new($stream, fd => N, protocol => P, ...)')
142             ->for('int i = 2', 'i < items', 'i += 2')
143             ->if('i + 1 < items')
144             ->line('STRLEN klen;')
145             ->line('const char* key = SvPV(ST(i), klen);')
146             ->if('klen == 2 && strncmp(key, "fd", 2) == 0')
147             ->line('fd = SvIV(ST(i + 1));')
148             ->endif
149             ->if('klen == 7 && strncmp(key, "request", 7) == 0')
150             ->line('request_sv = ST(i + 1);')
151             ->endif
152             ->if('klen == 8 && strncmp(key, "protocol", 8) == 0')
153             ->line('protocol_sv = ST(i + 1);')
154             ->endif
155             ->if('klen == 16 && strncmp(key, "max_message_size", 16) == 0')
156             ->line('max_message_size = SvIV(ST(i + 1));')
157             ->endif
158             ->endif
159             ->endfor
160             ->blank
161             ->comment('Store stream reference')
162             ->if('stream_sv')
163             ->line('hv_stores(self_hv, "stream", newSVsv(stream_sv));')
164             ->endif
165             ->blank
166             ->comment('Store fd and initialize registry')
167             ->line('hv_stores(self_hv, "fd", newSViv(fd));')
168             ->if('fd >= 0 && fd < WS_MAX')
169             ->line('ws_reset(fd);')
170             ->if('protocol_sv && SvOK(protocol_sv)')
171             ->line('STRLEN plen;')
172             ->line('const char* proto = SvPV(protocol_sv, plen);')
173             ->if('plen < sizeof(ws_registry[fd].protocol)')
174             ->line('memcpy(ws_registry[fd].protocol, proto, plen);')
175             ->line('ws_registry[fd].protocol[plen] = \'\\0\';')
176             ->endif
177             ->endif
178             ->endif
179             ->blank
180             ->comment('Store request reference')
181             ->if('request_sv')
182             ->line('hv_stores(self_hv, "request", newSVsv(request_sv));')
183             ->endif
184             ->blank
185             ->comment('Store protocol as Perl scalar')
186             ->if('protocol_sv && SvOK(protocol_sv)')
187             ->line('hv_stores(self_hv, "protocol", newSVsv(protocol_sv));')
188             ->endif
189             ->blank
190             ->comment('Initialize Perl-side fields for event handling')
191             ->line('hv_stores(self_hv, "handlers", newRV_noinc((SV*)newHV()));')
192             ->line('hv_stores(self_hv, "buffer", newSVpvn("", 0));')
193             ->line('hv_stores(self_hv, "fragments", newRV_noinc((SV*)newAV()));')
194             ->line('hv_stores(self_hv, "send_buffer", newRV_noinc((SV*)newAV()));')
195             ->line('hv_stores(self_hv, "max_message_size", newSViv(max_message_size));')
196             ->blank
197             ->line('ST(0) = sv_2mortal(self_rv);')
198             ->line('XSRETURN(1);')
199             ->xs_end
200             ->blank;
201             }
202              
203             sub gen_xs_fd {
204 2     2 0 17 my ($class, $builder) = @_;
205              
206 2         32 $builder->xs_function('xs_websocket_fd')
207             ->xs_preamble
208             ->check_items(1, 1, '$ws->fd')
209             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
210             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
211             ->if('fd_sv && *fd_sv')
212             ->line('XSRETURN_IV(SvIV(*fd_sv));')
213             ->endif
214             ->line('XSRETURN_IV(-1);')
215             ->xs_end
216             ->blank;
217             }
218              
219             sub gen_xs_state {
220 2     2 0 4 my ($class, $builder) = @_;
221              
222 2         35 $builder->xs_function('xs_websocket_state')
223             ->xs_preamble
224             ->check_items(1, 1, '$ws->state')
225             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
226             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
227             ->if('fd_sv && *fd_sv')
228             ->line('int fd = SvIV(*fd_sv);')
229             ->if('fd >= 0 && fd < WS_MAX')
230             ->line('XSRETURN_IV(ws_registry[fd].state);')
231             ->endif
232             ->endif
233             ->line('XSRETURN_IV(WS_STATE_CONNECTING);')
234             ->xs_end
235             ->blank;
236             }
237              
238             sub gen_xs_protocol {
239 2     2 0 5 my ($class, $builder) = @_;
240              
241 2         56 $builder->xs_function('xs_websocket_protocol')
242             ->xs_preamble
243             ->check_items(1, 1, '$ws->protocol')
244             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
245             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
246             ->if('fd_sv && *fd_sv')
247             ->line('int fd = SvIV(*fd_sv);')
248             ->if('fd >= 0 && fd < WS_MAX && ws_registry[fd].protocol[0]')
249             ->line('XSRETURN_PV(ws_registry[fd].protocol);')
250             ->endif
251             ->endif
252             ->line('SV** proto_sv = hv_fetchs(self_hv, "protocol", 0);')
253             ->if('proto_sv && *proto_sv && SvOK(*proto_sv)')
254             ->line('ST(0) = *proto_sv;')
255             ->line('XSRETURN(1);')
256             ->endif
257             ->line('XSRETURN_UNDEF;')
258             ->xs_end
259             ->blank;
260             }
261              
262             sub gen_xs_stream {
263 2     2 0 5 my ($class, $builder) = @_;
264              
265 2         41 $builder->xs_function('xs_websocket_stream')
266             ->xs_preamble
267             ->check_items(1, 1, '$ws->stream')
268             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
269             ->line('SV** stream_sv = hv_fetchs(self_hv, "stream", 0);')
270             ->if('stream_sv && *stream_sv && SvOK(*stream_sv)')
271             ->line('ST(0) = *stream_sv;')
272             ->line('XSRETURN(1);')
273             ->endif
274             ->line('XSRETURN_UNDEF;')
275             ->xs_end
276             ->blank;
277             }
278              
279             sub gen_xs_request {
280 2     2 0 3 my ($class, $builder) = @_;
281              
282 2         70 $builder->xs_function('xs_websocket_request')
283             ->xs_preamble
284             ->check_items(1, 1, '$ws->request')
285             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
286             ->line('SV** req_sv = hv_fetchs(self_hv, "request", 0);')
287             ->if('req_sv && *req_sv && SvOK(*req_sv)')
288             ->line('ST(0) = *req_sv;')
289             ->line('XSRETURN(1);')
290             ->endif
291             ->line('XSRETURN_UNDEF;')
292             ->xs_end
293             ->blank;
294             }
295              
296             sub gen_xs_is_open {
297 2     2 0 4 my ($class, $builder) = @_;
298              
299 2         75 $builder->xs_function('xs_websocket_is_open')
300             ->xs_preamble
301             ->check_items(1, 1, '$ws->is_open')
302             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
303             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
304             ->if('fd_sv && *fd_sv')
305             ->line('int fd = SvIV(*fd_sv);')
306             ->if('fd >= 0 && fd < WS_MAX && ws_registry[fd].state == WS_STATE_OPEN')
307             ->line('XSRETURN_YES;')
308             ->endif
309             ->endif
310             ->line('XSRETURN_NO;')
311             ->xs_end
312             ->blank;
313             }
314              
315             sub gen_xs_is_closing {
316 2     2 0 4 my ($class, $builder) = @_;
317              
318 2         32 $builder->xs_function('xs_websocket_is_closing')
319             ->xs_preamble
320             ->check_items(1, 1, '$ws->is_closing')
321             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
322             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
323             ->if('fd_sv && *fd_sv')
324             ->line('int fd = SvIV(*fd_sv);')
325             ->if('fd >= 0 && fd < WS_MAX && ws_registry[fd].state == WS_STATE_CLOSING')
326             ->line('XSRETURN_YES;')
327             ->endif
328             ->endif
329             ->line('XSRETURN_NO;')
330             ->xs_end
331             ->blank;
332             }
333              
334             sub gen_xs_is_closed {
335 2     2 0 4 my ($class, $builder) = @_;
336              
337 2         79 $builder->xs_function('xs_websocket_is_closed')
338             ->xs_preamble
339             ->check_items(1, 1, '$ws->is_closed')
340             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
341             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
342             ->if('fd_sv && *fd_sv')
343             ->line('int fd = SvIV(*fd_sv);')
344             ->if('fd >= 0 && fd < WS_MAX && ws_registry[fd].state == WS_STATE_CLOSED')
345             ->line('XSRETURN_YES;')
346             ->endif
347             ->endif
348             ->line('XSRETURN_NO;')
349             ->xs_end
350             ->blank;
351             }
352              
353             sub gen_xs_on {
354 2     2 0 4 my ($class, $builder) = @_;
355              
356 2         94 $builder->xs_function('xs_websocket_on')
357             ->xs_preamble
358             ->if('items != 3')
359             ->line('croak("Usage: $ws->on(event, handler)");')
360             ->endif
361             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
362             ->line('STRLEN elen;')
363             ->line('const char* event = SvPV(ST(1), elen);')
364             ->line('SV* handler = ST(2);')
365             ->blank
366             ->comment('Validate event name')
367             ->line('int valid = 0;')
368             ->if('elen == 4 && strncmp(event, "open", 4) == 0') ->line('valid = 1;') ->endif
369             ->if('elen == 7 && strncmp(event, "message", 7) == 0') ->line('valid = 1;') ->endif
370             ->if('elen == 6 && strncmp(event, "binary", 6) == 0') ->line('valid = 1;') ->endif
371             ->if('elen == 4 && strncmp(event, "ping", 4) == 0') ->line('valid = 1;') ->endif
372             ->if('elen == 4 && strncmp(event, "pong", 4) == 0') ->line('valid = 1;') ->endif
373             ->if('elen == 5 && strncmp(event, "close", 5) == 0') ->line('valid = 1;') ->endif
374             ->if('elen == 5 && strncmp(event, "error", 5) == 0') ->line('valid = 1;') ->endif
375             ->blank
376             ->if('!valid')
377             ->line('warn("Unknown WebSocket event: %s", event);')
378             ->line('ST(0) = ST(0);')
379             ->line('XSRETURN(1);')
380             ->endif
381             ->blank
382             ->comment('Store handler in handlers hash')
383             ->line('SV** handlers_rv = hv_fetchs(self_hv, "handlers", 0);')
384             ->if('handlers_rv && *handlers_rv && SvROK(*handlers_rv)')
385             ->line('HV* handlers_hv = (HV*)SvRV(*handlers_rv);')
386             ->line('hv_store(handlers_hv, event, elen, newSVsv(handler), 0);')
387             ->endif
388             ->blank
389             ->line('ST(0) = ST(0);')
390             ->line('XSRETURN(1);')
391             ->xs_end
392             ->blank;
393             }
394              
395             sub gen_xs_emit {
396 2     2 0 4 my ($class, $builder) = @_;
397              
398 2         98 $builder->xs_function('xs_websocket_emit')
399             ->xs_preamble
400             ->if('items < 2')
401             ->line('croak("Usage: $ws->emit(event, ...)");')
402             ->endif
403             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
404             ->line('STRLEN elen;')
405             ->line('const char* event = SvPV(ST(1), elen);')
406             ->blank
407             ->comment('Get handler from handlers hash')
408             ->line('SV** handlers_rv = hv_fetchs(self_hv, "handlers", 0);')
409             ->if('!handlers_rv || !*handlers_rv || !SvROK(*handlers_rv)')
410             ->line('ST(0) = ST(0);')
411             ->line('XSRETURN(1);')
412             ->endif
413             ->blank
414             ->line('HV* handlers_hv = (HV*)SvRV(*handlers_rv);')
415             ->line('SV** handler_sv = hv_fetch(handlers_hv, event, elen, 0);')
416             ->if('!handler_sv || !*handler_sv || !SvOK(*handler_sv)')
417             ->line('ST(0) = ST(0);')
418             ->line('XSRETURN(1);')
419             ->endif
420             ->blank
421             ->comment('Call the handler with remaining args')
422             ->line('ENTER;')
423             ->line('SAVETMPS;')
424             ->line('PUSHMARK(SP);')
425             ->for('int i = 2', 'i < items', 'i++')
426             ->line('XPUSHs(ST(i));')
427             ->endfor
428             ->line('PUTBACK;')
429             ->blank
430             ->line('int count = call_sv(*handler_sv, G_EVAL | G_DISCARD);')
431             ->blank
432             ->if('SvTRUE(ERRSV)')
433             ->line('warn("WebSocket %s handler error: %s", event, SvPV_nolen(ERRSV));')
434             ->comment('Emit error event if not already handling error')
435             ->if('!(elen == 5 && strncmp(event, "error", 5) == 0)')
436             ->line('SV** err_handler = hv_fetchs(handlers_hv, "error", 0);')
437             ->if('err_handler && *err_handler && SvOK(*err_handler)')
438             ->line('PUSHMARK(SP);')
439             ->line('XPUSHs(ERRSV);')
440             ->line('PUTBACK;')
441             ->line('call_sv(*err_handler, G_EVAL | G_DISCARD);')
442             ->endif
443             ->endif
444             ->endif
445             ->blank
446             ->line('FREETMPS;')
447             ->line('LEAVE;')
448             ->blank
449             ->line('ST(0) = ST(0);')
450             ->line('XSRETURN(1);')
451             ->xs_end
452             ->blank;
453             }
454              
455             sub gen_xs_accept {
456 2     2 0 4 my ($class, $builder) = @_;
457              
458 2         314 $builder->xs_function('xs_websocket_accept')
459             ->xs_preamble
460             ->if('items != 2')
461             ->line('croak("Usage: $ws->accept(handshake)");')
462             ->endif
463             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
464             ->line('SV* handshake_sv = ST(1);')
465             ->blank
466             ->comment('Get fd and check state')
467             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
468             ->line('int fd = (fd_sv && *fd_sv) ? SvIV(*fd_sv) : -1;')
469             ->blank
470             ->if('fd >= 0 && fd < WS_MAX && ws_registry[fd].state != WS_STATE_CONNECTING')
471             ->line('XSRETURN_IV(0);')
472             ->endif
473             ->blank
474             ->comment('Check handshake is valid hashref with is_websocket')
475             ->if('!SvROK(handshake_sv) || SvTYPE(SvRV(handshake_sv)) != SVt_PVHV')
476             ->line('XSRETURN_IV(0);')
477             ->endif
478             ->blank
479             ->line('HV* hs_hv = (HV*)SvRV(handshake_sv);')
480             ->line('SV** is_ws = hv_fetchs(hs_hv, "is_websocket", 0);')
481             ->if('!is_ws || !*is_ws || !SvTRUE(*is_ws)')
482             ->line('XSRETURN_IV(0);')
483             ->endif
484             ->blank
485             ->comment('Call Perl to build response (uses Protocol::WebSocket)')
486             ->line('ENTER;')
487             ->line('SAVETMPS;')
488             ->line('PUSHMARK(SP);')
489             ->line('XPUSHs(sv_2mortal(newSVpvs("Hypersonic::Protocol::WebSocket")));')
490             ->blank
491             ->comment('Get ws_key')
492             ->line('SV** ws_key = hv_fetchs(hs_hv, "ws_key", 0);')
493             ->if('ws_key && *ws_key')
494             ->line('XPUSHs(sv_2mortal(newSVpvs("key")));')
495             ->line('XPUSHs(*ws_key);')
496             ->endif
497             ->blank
498             ->comment('Get protocol')
499             ->line('SV** proto_sv = hv_fetchs(self_hv, "protocol", 0);')
500             ->if('!proto_sv || !*proto_sv || !SvOK(*proto_sv)')
501             ->line('proto_sv = hv_fetchs(hs_hv, "ws_protocol", 0);')
502             ->endif
503             ->if('proto_sv && *proto_sv && SvOK(*proto_sv)')
504             ->line('XPUSHs(sv_2mortal(newSVpvs("protocol")));')
505             ->line('XPUSHs(*proto_sv);')
506             ->endif
507             ->blank
508             ->line('PUTBACK;')
509             ->line('int count = call_method("build_response", G_SCALAR);')
510             ->line('SPAGAIN;')
511             ->blank
512             ->if('count != 1')
513             ->line('FREETMPS;')
514             ->line('LEAVE;')
515             ->line('XSRETURN_IV(0);')
516             ->endif
517             ->blank
518             ->line('SV* response = POPs;')
519             ->blank
520             ->comment('Write response via stream')
521             ->line('SV** stream_sv = hv_fetchs(self_hv, "stream", 0);')
522             ->if('stream_sv && *stream_sv && SvROK(*stream_sv)')
523             ->line('PUSHMARK(SP);')
524             ->line('XPUSHs(*stream_sv);')
525             ->line('XPUSHs(response);')
526             ->line('PUTBACK;')
527             ->line('call_method("_raw_write", G_DISCARD | G_EVAL);')
528             ->endif
529             ->blank
530             ->line('FREETMPS;')
531             ->line('LEAVE;')
532             ->blank
533             ->comment('Transition to open state')
534             ->if('fd >= 0 && fd < WS_MAX')
535             ->line('ws_registry[fd].state = WS_STATE_OPEN;')
536             ->endif
537             ->blank
538             ->comment('Emit open event')
539             ->line('SV** handlers_rv = hv_fetchs(self_hv, "handlers", 0);')
540             ->if('handlers_rv && *handlers_rv && SvROK(*handlers_rv)')
541             ->line('HV* handlers_hv = (HV*)SvRV(*handlers_rv);')
542             ->line('SV** open_handler = hv_fetchs(handlers_hv, "open", 0);')
543             ->if('open_handler && *open_handler && SvOK(*open_handler)')
544             ->line('PUSHMARK(SP);')
545             ->line('PUTBACK;')
546             ->line('call_sv(*open_handler, G_DISCARD | G_EVAL);')
547             ->endif
548             ->endif
549             ->blank
550             ->line('XSRETURN_IV(1);')
551             ->xs_end
552             ->blank;
553             }
554              
555             sub gen_xs_send {
556 2     2 0 5 my ($class, $builder) = @_;
557              
558 2         73 $builder->xs_function('xs_websocket_send')
559             ->xs_preamble
560             ->if('items != 2')
561             ->line('croak("Usage: $ws->send(data)");')
562             ->endif
563             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
564             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
565             ->blank
566             ->if('!fd_sv || !*fd_sv')
567             ->line('XSRETURN_IV(0);')
568             ->endif
569             ->blank
570             ->line('int fd = SvIV(*fd_sv);')
571             ->if('fd < 0 || fd >= WS_MAX || ws_registry[fd].state != WS_STATE_OPEN')
572             ->line('XSRETURN_IV(0);')
573             ->endif
574             ->blank
575             ->line('STRLEN len;')
576             ->line('const char* data = SvPV(ST(1), len);')
577             ->if('!SvOK(ST(1))')
578             ->line('XSRETURN_IV(0);')
579             ->endif
580             ->blank
581             ->comment('Encode text frame')
582             ->line('uint8_t frame[65546];')
583             ->line('size_t frame_len = ws_encode_text(frame, sizeof(frame), data, len);')
584             ->if('frame_len == 0')
585             ->line('XSRETURN_IV(0);')
586             ->endif
587             ->blank
588             ->comment('Send directly to fd')
589             ->line('ssize_t sent = send(fd, frame, frame_len, 0);')
590             ->if('sent < 0')
591             ->line('XSRETURN_IV(0);')
592             ->endif
593             ->blank
594             ->line('XSRETURN_IV(1);')
595             ->xs_end
596             ->blank;
597             }
598              
599             sub gen_xs_send_binary {
600 2     2 0 3 my ($class, $builder) = @_;
601              
602 2         88 $builder->xs_function('xs_websocket_send_binary')
603             ->xs_preamble
604             ->if('items != 2')
605             ->line('croak("Usage: $ws->send_binary(data)");')
606             ->endif
607             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
608             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
609             ->blank
610             ->if('!fd_sv || !*fd_sv')
611             ->line('XSRETURN_IV(0);')
612             ->endif
613             ->blank
614             ->line('int fd = SvIV(*fd_sv);')
615             ->if('fd < 0 || fd >= WS_MAX || ws_registry[fd].state != WS_STATE_OPEN')
616             ->line('XSRETURN_IV(0);')
617             ->endif
618             ->blank
619             ->line('STRLEN len;')
620             ->line('const char* data = SvPV(ST(1), len);')
621             ->if('!SvOK(ST(1))')
622             ->line('XSRETURN_IV(0);')
623             ->endif
624             ->blank
625             ->line('uint8_t frame[65546];')
626             ->line('size_t frame_len = ws_encode_binary(frame, sizeof(frame), (const uint8_t*)data, len);')
627             ->if('frame_len == 0')
628             ->line('XSRETURN_IV(0);')
629             ->endif
630             ->blank
631             ->line('ssize_t sent = send(fd, frame, frame_len, 0);')
632             ->if('sent < 0')
633             ->line('XSRETURN_IV(0);')
634             ->endif
635             ->blank
636             ->line('XSRETURN_IV(1);')
637             ->xs_end
638             ->blank;
639             }
640              
641             sub gen_xs_ping {
642 2     2 0 4 my ($class, $builder) = @_;
643              
644 2         94 $builder->xs_function('xs_websocket_ping')
645             ->xs_preamble
646             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
647             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
648             ->blank
649             ->if('!fd_sv || !*fd_sv')
650             ->line('XSRETURN_IV(0);')
651             ->endif
652             ->blank
653             ->line('int fd = SvIV(*fd_sv);')
654             ->if('fd < 0 || fd >= WS_MAX || ws_registry[fd].state != WS_STATE_OPEN')
655             ->line('XSRETURN_IV(0);')
656             ->endif
657             ->blank
658             ->line('const char* data = "";')
659             ->line('STRLEN len = 0;')
660             ->if('items >= 2 && SvOK(ST(1))')
661             ->line('data = SvPV(ST(1), len);')
662             ->endif
663             ->blank
664             ->line('uint8_t frame[256];')
665             ->line('size_t frame_len = ws_encode_ping(frame, sizeof(frame), (const uint8_t*)data, len);')
666             ->if('frame_len == 0')
667             ->line('XSRETURN_IV(0);')
668             ->endif
669             ->blank
670             ->line('ssize_t sent = send(fd, frame, frame_len, 0);')
671             ->if('sent < 0')
672             ->line('XSRETURN_IV(0);')
673             ->endif
674             ->blank
675             ->line('XSRETURN_IV(1);')
676             ->xs_end
677             ->blank;
678             }
679              
680             sub gen_xs_pong {
681 2     2 0 5 my ($class, $builder) = @_;
682              
683 2         68 $builder->xs_function('xs_websocket_pong')
684             ->xs_preamble
685             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
686             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
687             ->blank
688             ->if('!fd_sv || !*fd_sv')
689             ->line('XSRETURN_IV(0);')
690             ->endif
691             ->blank
692             ->line('int fd = SvIV(*fd_sv);')
693             ->if('fd < 0 || fd >= WS_MAX || ws_registry[fd].state != WS_STATE_OPEN')
694             ->line('XSRETURN_IV(0);')
695             ->endif
696             ->blank
697             ->line('const char* data = "";')
698             ->line('STRLEN len = 0;')
699             ->if('items >= 2 && SvOK(ST(1))')
700             ->line('data = SvPV(ST(1), len);')
701             ->endif
702             ->blank
703             ->line('uint8_t frame[256];')
704             ->line('size_t frame_len = ws_encode_pong(frame, sizeof(frame), (const uint8_t*)data, len);')
705             ->if('frame_len == 0')
706             ->line('XSRETURN_IV(0);')
707             ->endif
708             ->blank
709             ->line('ssize_t sent = send(fd, frame, frame_len, 0);')
710             ->if('sent < 0')
711             ->line('XSRETURN_IV(0);')
712             ->endif
713             ->blank
714             ->line('XSRETURN_IV(1);')
715             ->xs_end
716             ->blank;
717             }
718              
719             sub gen_xs_close {
720 2     2 0 7 my ($class, $builder) = @_;
721              
722 2         114 $builder->xs_function('xs_websocket_close')
723             ->xs_preamble
724             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
725             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
726             ->blank
727             ->if('!fd_sv || !*fd_sv')
728             ->line('XSRETURN_IV(0);')
729             ->endif
730             ->blank
731             ->line('int fd = SvIV(*fd_sv);')
732             ->if('fd < 0 || fd >= WS_MAX || ws_registry[fd].state >= WS_STATE_CLOSING')
733             ->line('XSRETURN_IV(0);')
734             ->endif
735             ->blank
736             ->line('uint16_t code = 1000;')
737             ->line('const char* reason = "";')
738             ->line('STRLEN reason_len = 0;')
739             ->blank
740             ->if('items >= 2 && SvOK(ST(1))')
741             ->line('code = (uint16_t)SvIV(ST(1));')
742             ->endif
743             ->if('items >= 3 && SvOK(ST(2))')
744             ->line('reason = SvPV(ST(2), reason_len);')
745             ->endif
746             ->blank
747             ->line('ws_registry[fd].state = WS_STATE_CLOSING;')
748             ->line('ws_registry[fd].close_code = code;')
749             ->if('reason_len > 0 && reason_len < sizeof(ws_registry[fd].close_reason)')
750             ->line('memcpy(ws_registry[fd].close_reason, reason, reason_len);')
751             ->line('ws_registry[fd].close_reason[reason_len] = \'\\0\';')
752             ->endif
753             ->blank
754             ->line('uint8_t frame[256];')
755             ->line('size_t frame_len = ws_encode_close(frame, sizeof(frame), code, reason);')
756             ->if('frame_len == 0')
757             ->line('XSRETURN_IV(0);')
758             ->endif
759             ->blank
760             ->line('ssize_t sent = send(fd, frame, frame_len, 0);')
761             ->if('sent < 0')
762             ->line('XSRETURN_IV(0);')
763             ->endif
764             ->blank
765             ->line('XSRETURN_IV(1);')
766             ->xs_end
767             ->blank;
768             }
769              
770             sub gen_xs_handle_close {
771 2     2 0 2 my ($class, $builder) = @_;
772              
773 2         201 $builder->xs_function('xs_websocket_handle_close')
774             ->xs_preamble
775             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
776             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
777             ->line('int fd = (fd_sv && *fd_sv) ? SvIV(*fd_sv) : -1;')
778             ->blank
779             ->line('uint16_t code = 1000;')
780             ->line('const char* reason = "";')
781             ->line('STRLEN reason_len = 0;')
782             ->if('items >= 2 && SvOK(ST(1))')
783             ->line('code = (uint16_t)SvIV(ST(1));')
784             ->endif
785             ->if('items >= 3 && SvOK(ST(2))')
786             ->line('reason = SvPV(ST(2), reason_len);')
787             ->endif
788             ->blank
789             ->comment('If state is OPEN, echo close back')
790             ->if('fd >= 0 && fd < WS_MAX && ws_registry[fd].state == WS_STATE_OPEN')
791             ->line('ws_registry[fd].state = WS_STATE_CLOSING;')
792             ->line('ws_registry[fd].close_code = code;')
793             ->blank
794             ->line('uint8_t frame[256];')
795             ->line('size_t frame_len = ws_encode_close(frame, sizeof(frame), code, "");')
796             ->if('frame_len > 0')
797             ->line('send(fd, frame, frame_len, 0);')
798             ->endif
799             ->endif
800             ->blank
801             ->comment('Set state to CLOSED')
802             ->if('fd >= 0 && fd < WS_MAX')
803             ->line('ws_registry[fd].state = WS_STATE_CLOSED;')
804             ->endif
805             ->blank
806             ->comment('Emit close event')
807             ->line('SV** handlers_rv = hv_fetchs(self_hv, "handlers", 0);')
808             ->if('handlers_rv && *handlers_rv && SvROK(*handlers_rv)')
809             ->line('HV* handlers_hv = (HV*)SvRV(*handlers_rv);')
810             ->line('SV** close_handler = hv_fetchs(handlers_hv, "close", 0);')
811             ->if('close_handler && *close_handler && SvOK(*close_handler)')
812             ->line('ENTER;')
813             ->line('SAVETMPS;')
814             ->line('PUSHMARK(SP);')
815             ->line('XPUSHs(sv_2mortal(newSViv(code)));')
816             ->if('reason_len > 0')
817             ->line('XPUSHs(sv_2mortal(newSVpvn(reason, reason_len)));')
818             ->else
819             ->line('XPUSHs(&PL_sv_undef);')
820             ->endif
821             ->line('PUTBACK;')
822             ->line('call_sv(*close_handler, G_DISCARD | G_EVAL);')
823             ->line('FREETMPS;')
824             ->line('LEAVE;')
825             ->endif
826             ->endif
827             ->blank
828             ->line('XSRETURN_IV(1);')
829             ->xs_end
830             ->blank;
831             }
832              
833             sub gen_xs_handle_message {
834 2     2 0 4 my ($class, $builder) = @_;
835              
836 2         204 $builder->xs_function('xs_websocket_handle_message')
837             ->xs_preamble
838             ->if('items != 3')
839             ->line('croak("Usage: $ws->handle_message(opcode, data)");')
840             ->endif
841             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
842             ->line('int opcode = SvIV(ST(1));')
843             ->line('SV* data_sv = ST(2);')
844             ->blank
845             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
846             ->line('int fd = (fd_sv && *fd_sv) ? SvIV(*fd_sv) : -1;')
847             ->blank
848             ->if('fd < 0 || fd >= WS_MAX || ws_registry[fd].state != WS_STATE_OPEN')
849             ->line('XSRETURN_UNDEF;')
850             ->endif
851             ->blank
852             ->line('SV** handlers_rv = hv_fetchs(self_hv, "handlers", 0);')
853             ->if('!handlers_rv || !*handlers_rv || !SvROK(*handlers_rv)')
854             ->line('XSRETURN_UNDEF;')
855             ->endif
856             ->line('HV* handlers_hv = (HV*)SvRV(*handlers_rv);')
857             ->blank
858             ->comment('Handle by opcode')
859             ->if('opcode == WS_OP_TEXT')
860             ->comment('Emit message event')
861             ->line('SV** msg_handler = hv_fetchs(handlers_hv, "message", 0);')
862             ->if('msg_handler && *msg_handler && SvOK(*msg_handler)')
863             ->line('ENTER;')
864             ->line('SAVETMPS;')
865             ->line('PUSHMARK(SP);')
866             ->line('XPUSHs(data_sv);')
867             ->line('XPUSHs(sv_2mortal(newSViv(0)));') # is_binary = 0
868             ->line('PUTBACK;')
869             ->line('call_sv(*msg_handler, G_DISCARD | G_EVAL);')
870             ->line('FREETMPS;')
871             ->line('LEAVE;')
872             ->endif
873             ->elsif('opcode == WS_OP_BINARY')
874             ->comment('Emit binary event')
875             ->line('SV** bin_handler = hv_fetchs(handlers_hv, "binary", 0);')
876             ->if('bin_handler && *bin_handler && SvOK(*bin_handler)')
877             ->line('ENTER;')
878             ->line('SAVETMPS;')
879             ->line('PUSHMARK(SP);')
880             ->line('XPUSHs(data_sv);')
881             ->line('PUTBACK;')
882             ->line('call_sv(*bin_handler, G_DISCARD | G_EVAL);')
883             ->line('FREETMPS;')
884             ->line('LEAVE;')
885             ->endif
886             ->comment('Also emit message event')
887             ->line('SV** msg_handler = hv_fetchs(handlers_hv, "message", 0);')
888             ->if('msg_handler && *msg_handler && SvOK(*msg_handler)')
889             ->line('ENTER;')
890             ->line('SAVETMPS;')
891             ->line('PUSHMARK(SP);')
892             ->line('XPUSHs(data_sv);')
893             ->line('XPUSHs(sv_2mortal(newSViv(1)));') # is_binary = 1
894             ->line('PUTBACK;')
895             ->line('call_sv(*msg_handler, G_DISCARD | G_EVAL);')
896             ->line('FREETMPS;')
897             ->line('LEAVE;')
898             ->endif
899             ->elsif('opcode == WS_OP_PING')
900             ->comment('Emit ping event')
901             ->line('SV** ping_handler = hv_fetchs(handlers_hv, "ping", 0);')
902             ->if('ping_handler && *ping_handler && SvOK(*ping_handler)')
903             ->line('ENTER;')
904             ->line('SAVETMPS;')
905             ->line('PUSHMARK(SP);')
906             ->line('XPUSHs(data_sv);')
907             ->line('PUTBACK;')
908             ->line('call_sv(*ping_handler, G_DISCARD | G_EVAL);')
909             ->line('FREETMPS;')
910             ->line('LEAVE;')
911             ->endif
912             ->comment('Auto-pong')
913             ->line('STRLEN pong_len;')
914             ->line('const char* pong_data = SvPV(data_sv, pong_len);')
915             ->line('uint8_t pong_frame[256];')
916             ->line('size_t pong_frame_len = ws_encode_pong(pong_frame, sizeof(pong_frame), (const uint8_t*)pong_data, pong_len);')
917             ->if('pong_frame_len > 0')
918             ->line('send(fd, pong_frame, pong_frame_len, 0);')
919             ->endif
920             ->elsif('opcode == WS_OP_PONG')
921             ->comment('Emit pong event')
922             ->line('SV** pong_handler = hv_fetchs(handlers_hv, "pong", 0);')
923             ->if('pong_handler && *pong_handler && SvOK(*pong_handler)')
924             ->line('ENTER;')
925             ->line('SAVETMPS;')
926             ->line('PUSHMARK(SP);')
927             ->line('XPUSHs(data_sv);')
928             ->line('PUTBACK;')
929             ->line('call_sv(*pong_handler, G_DISCARD | G_EVAL);')
930             ->line('FREETMPS;')
931             ->line('LEAVE;')
932             ->endif
933             ->endif
934             ->blank
935             ->line('XSRETURN_UNDEF;')
936             ->xs_end
937             ->blank;
938             }
939              
940             sub gen_xs_process_data {
941 2     2 0 4 my ($class, $builder) = @_;
942              
943 2         591 $builder->xs_function('xs_websocket_process_data')
944             ->xs_preamble
945             ->if('items != 2')
946             ->line('croak("Usage: $ws->process_data(data)");')
947             ->endif
948             ->line('SV* self_sv = ST(0);')
949             ->line('HV* self_hv = (HV*)SvRV(self_sv);')
950             ->line('STRLEN data_len;')
951             ->line('const char* data = SvPV(ST(1), data_len);')
952             ->blank
953             ->comment('Append to buffer')
954             ->line('SV** buffer_sv = hv_fetchs(self_hv, "buffer", 0);')
955             ->if('!buffer_sv || !*buffer_sv')
956             ->line('XSRETURN_IV(1);')
957             ->endif
958             ->line('sv_catpvn(*buffer_sv, data, data_len);')
959             ->blank
960             ->line('SV** fd_sv = hv_fetchs(self_hv, "fd", 0);')
961             ->line('int fd = (fd_sv && *fd_sv) ? SvIV(*fd_sv) : -1;')
962             ->blank
963             ->comment('Process frames in buffer')
964             ->while('SvCUR(*buffer_sv) >= 2')
965             ->comment('Call Frame->decode_frame via Perl')
966             ->line('ENTER;')
967             ->line('SAVETMPS;')
968             ->line('PUSHMARK(SP);')
969             ->line('XPUSHs(sv_2mortal(newSVpvs("Hypersonic::Protocol::WebSocket::Frame")));')
970             ->line('XPUSHs(*buffer_sv);')
971             ->line('PUTBACK;')
972             ->line('int count = call_method("decode_frame", G_SCALAR);')
973             ->line('SPAGAIN;')
974             ->blank
975             ->if('count != 1')
976             ->line('FREETMPS;')
977             ->line('LEAVE;')
978             ->line('break;')
979             ->endif
980             ->blank
981             ->line('SV* frame_sv = POPs;')
982             ->if('!SvROK(frame_sv) || SvTYPE(SvRV(frame_sv)) != SVt_PVHV')
983             ->line('FREETMPS;')
984             ->line('LEAVE;')
985             ->line('break;')
986             ->endif
987             ->blank
988             ->line('HV* frame_hv = (HV*)SvRV(frame_sv);')
989             ->blank
990             ->comment('Get frame fields')
991             ->line('SV** total_size_sv = hv_fetchs(frame_hv, "total_size", 0);')
992             ->line('SV** opcode_sv = hv_fetchs(frame_hv, "opcode", 0);')
993             ->line('SV** fin_sv = hv_fetchs(frame_hv, "fin", 0);')
994             ->line('SV** payload_sv = hv_fetchs(frame_hv, "payload", 0);')
995             ->blank
996             ->if('!total_size_sv || !opcode_sv')
997             ->line('FREETMPS;')
998             ->line('LEAVE;')
999             ->line('break;')
1000             ->endif
1001             ->blank
1002             ->line('STRLEN total_size = SvIV(*total_size_sv);')
1003             ->line('int opcode = SvIV(*opcode_sv);')
1004             ->line('int fin = fin_sv && *fin_sv ? SvIV(*fin_sv) : 1;')
1005             ->blank
1006             ->comment('Remove consumed bytes from buffer')
1007             ->line('STRLEN buf_len;')
1008             ->line('char* buf = SvPV(*buffer_sv, buf_len);')
1009             ->line('sv_setpvn(*buffer_sv, buf + total_size, buf_len - total_size);')
1010             ->blank
1011             ->comment('Handle CLOSE frame')
1012             ->if('opcode == WS_OP_CLOSE')
1013             ->comment('Parse close code/reason')
1014             ->line('PUSHMARK(SP);')
1015             ->line('XPUSHs(sv_2mortal(newSVpvs("Hypersonic::Protocol::WebSocket::Frame")));')
1016             ->if('payload_sv && *payload_sv')
1017             ->line('XPUSHs(*payload_sv);')
1018             ->else
1019             ->line('XPUSHs(&PL_sv_undef);')
1020             ->endif
1021             ->line('PUTBACK;')
1022             ->line('count = call_method("parse_close", G_ARRAY);')
1023             ->line('SPAGAIN;')
1024             ->blank
1025             ->line('SV* reason_sv = &PL_sv_undef;')
1026             ->line('SV* code_sv = &PL_sv_undef;')
1027             ->if('count >= 2')
1028             ->line('reason_sv = POPs;')
1029             ->line('code_sv = POPs;')
1030             ->elsif('count >= 1')
1031             ->line('code_sv = POPs;')
1032             ->endif
1033             ->blank
1034             ->comment('Call handle_close')
1035             ->line('PUSHMARK(SP);')
1036             ->line('XPUSHs(self_sv);')
1037             ->line('XPUSHs(code_sv);')
1038             ->line('XPUSHs(reason_sv);')
1039             ->line('PUTBACK;')
1040             ->line('call_method("handle_close", G_DISCARD);')
1041             ->blank
1042             ->line('FREETMPS;')
1043             ->line('LEAVE;')
1044             ->line('XSRETURN_IV(0);')
1045             ->endif
1046             ->blank
1047             ->comment('Handle CONTINUATION frame')
1048             ->if('opcode == WS_OP_CONTINUATION')
1049             ->line('SV** fragments_rv = hv_fetchs(self_hv, "fragments", 0);')
1050             ->if('fragments_rv && *fragments_rv && SvROK(*fragments_rv)')
1051             ->line('AV* fragments_av = (AV*)SvRV(*fragments_rv);')
1052             ->if('payload_sv && *payload_sv')
1053             ->line('av_push(fragments_av, newSVsv(*payload_sv));')
1054             ->endif
1055             ->if('fin')
1056             ->comment('Complete fragmented message')
1057             ->line('SV* full_msg = newSVpvn("", 0);')
1058             ->line('I32 frag_len = av_len(fragments_av);')
1059             ->for('I32 i = 0', 'i <= frag_len', 'i++')
1060             ->line('SV** frag = av_fetch(fragments_av, i, 0);')
1061             ->if('frag && *frag')
1062             ->line('sv_catsv(full_msg, *frag);')
1063             ->endif
1064             ->endfor
1065             ->line('av_clear(fragments_av);')
1066             ->blank
1067             ->line('SV** first_opcode_sv = hv_fetchs(self_hv, "fragment_opcode", 0);')
1068             ->line('int first_opcode = (first_opcode_sv && *first_opcode_sv) ? SvIV(*first_opcode_sv) : WS_OP_TEXT;')
1069             ->blank
1070             ->line('PUSHMARK(SP);')
1071             ->line('XPUSHs(self_sv);')
1072             ->line('XPUSHs(sv_2mortal(newSViv(first_opcode)));')
1073             ->line('XPUSHs(sv_2mortal(full_msg));')
1074             ->line('PUTBACK;')
1075             ->line('call_method("handle_message", G_DISCARD);')
1076             ->endif
1077             ->endif
1078             ->elsif('!fin')
1079             ->comment('Start of fragmented message')
1080             ->line('hv_stores(self_hv, "fragment_opcode", newSViv(opcode));')
1081             ->line('SV** fragments_rv = hv_fetchs(self_hv, "fragments", 0);')
1082             ->if('fragments_rv && *fragments_rv && SvROK(*fragments_rv)')
1083             ->line('AV* fragments_av = (AV*)SvRV(*fragments_rv);')
1084             ->line('av_clear(fragments_av);')
1085             ->if('payload_sv && *payload_sv')
1086             ->line('av_push(fragments_av, newSVsv(*payload_sv));')
1087             ->endif
1088             ->endif
1089             ->else
1090             ->comment('Complete message in single frame')
1091             ->line('PUSHMARK(SP);')
1092             ->line('XPUSHs(self_sv);')
1093             ->line('XPUSHs(sv_2mortal(newSViv(opcode)));')
1094             ->if('payload_sv && *payload_sv')
1095             ->line('XPUSHs(*payload_sv);')
1096             ->else
1097             ->line('XPUSHs(sv_2mortal(newSVpvn("", 0)));')
1098             ->endif
1099             ->line('PUTBACK;')
1100             ->line('call_method("handle_message", G_DISCARD);')
1101             ->endif
1102             ->blank
1103             ->line('FREETMPS;')
1104             ->line('LEAVE;')
1105             ->endloop
1106             ->blank
1107             ->line('XSRETURN_IV(1);')
1108             ->xs_end
1109             ->blank;
1110             }
1111              
1112             sub gen_xs_flush_send_buffer {
1113 2     2 0 3 my ($class, $builder) = @_;
1114              
1115 2         129 $builder->xs_function('xs_websocket_flush_send_buffer')
1116             ->xs_preamble
1117             ->check_items(1, 1, '$ws->_flush_send_buffer')
1118             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
1119             ->blank
1120             ->line('SV** send_buffer_rv = hv_fetchs(self_hv, "send_buffer", 0);')
1121             ->if('!send_buffer_rv || !*send_buffer_rv || !SvROK(*send_buffer_rv)')
1122             ->line('XSRETURN_UNDEF;')
1123             ->endif
1124             ->blank
1125             ->line('AV* send_buffer_av = (AV*)SvRV(*send_buffer_rv);')
1126             ->if('av_len(send_buffer_av) < 0')
1127             ->line('XSRETURN_UNDEF;')
1128             ->endif
1129             ->blank
1130             ->line('SV** stream_sv = hv_fetchs(self_hv, "stream", 0);')
1131             ->if('!stream_sv || !*stream_sv || !SvROK(*stream_sv)')
1132             ->line('XSRETURN_UNDEF;')
1133             ->endif
1134             ->blank
1135             ->comment('Concatenate all frames')
1136             ->line('SV* data = newSVpvn("", 0);')
1137             ->line('I32 len = av_len(send_buffer_av);')
1138             ->for('I32 i = 0', 'i <= len', 'i++')
1139             ->line('SV** frame = av_fetch(send_buffer_av, i, 0);')
1140             ->if('frame && *frame')
1141             ->line('sv_catsv(data, *frame);')
1142             ->endif
1143             ->endfor
1144             ->line('av_clear(send_buffer_av);')
1145             ->blank
1146             ->comment('Write via stream')
1147             ->line('ENTER;')
1148             ->line('SAVETMPS;')
1149             ->line('PUSHMARK(SP);')
1150             ->line('XPUSHs(*stream_sv);')
1151             ->line('XPUSHs(sv_2mortal(data));')
1152             ->line('PUTBACK;')
1153             ->line('call_method("_raw_write", G_DISCARD | G_EVAL);')
1154             ->line('FREETMPS;')
1155             ->line('LEAVE;')
1156             ->blank
1157             ->line('XSRETURN_UNDEF;')
1158             ->xs_end
1159             ->blank;
1160             }
1161              
1162             sub gen_xs_param {
1163 2     2 0 4 my ($class, $builder) = @_;
1164              
1165 2         120 $builder->xs_function('xs_websocket_param')
1166             ->xs_preamble
1167             ->if('items != 2')
1168             ->line('croak("Usage: $ws->param(name)");')
1169             ->endif
1170             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
1171             ->line('SV* name_sv = ST(1);')
1172             ->blank
1173             ->line('SV** req_sv = hv_fetchs(self_hv, "request", 0);')
1174             ->if('!req_sv || !*req_sv || !SvOK(*req_sv)')
1175             ->line('XSRETURN_UNDEF;')
1176             ->endif
1177             ->blank
1178             ->comment('If request is a hashref, check params')
1179             ->if('SvROK(*req_sv) && SvTYPE(SvRV(*req_sv)) == SVt_PVHV')
1180             ->line('HV* req_hv = (HV*)SvRV(*req_sv);')
1181             ->line('SV** params_sv = hv_fetchs(req_hv, "params", 0);')
1182             ->if('params_sv && *params_sv && SvROK(*params_sv)')
1183             ->line('HV* params_hv = (HV*)SvRV(*params_sv);')
1184             ->line('STRLEN nlen;')
1185             ->line('const char* name = SvPV(name_sv, nlen);')
1186             ->line('SV** val = hv_fetch(params_hv, name, nlen, 0);')
1187             ->if('val && *val')
1188             ->line('ST(0) = *val;')
1189             ->line('XSRETURN(1);')
1190             ->endif
1191             ->endif
1192             ->line('XSRETURN_UNDEF;')
1193             ->endif
1194             ->blank
1195             ->comment('If request is an object, call param method')
1196             ->line('ENTER;')
1197             ->line('SAVETMPS;')
1198             ->line('PUSHMARK(SP);')
1199             ->line('XPUSHs(*req_sv);')
1200             ->line('XPUSHs(name_sv);')
1201             ->line('PUTBACK;')
1202             ->line('int count = call_method("param", G_SCALAR | G_EVAL);')
1203             ->line('SPAGAIN;')
1204             ->blank
1205             ->if('count >= 1')
1206             ->line('SV* result = POPs;')
1207             ->line('ST(0) = sv_2mortal(newSVsv(result));')
1208             ->line('FREETMPS;')
1209             ->line('LEAVE;')
1210             ->line('XSRETURN(1);')
1211             ->endif
1212             ->blank
1213             ->line('FREETMPS;')
1214             ->line('LEAVE;')
1215             ->line('XSRETURN_UNDEF;')
1216             ->xs_end
1217             ->blank;
1218             }
1219              
1220             sub gen_xs_header {
1221 2     2 0 9 my ($class, $builder) = @_;
1222              
1223 2         150 $builder->xs_function('xs_websocket_header')
1224             ->xs_preamble
1225             ->if('items != 2')
1226             ->line('croak("Usage: $ws->header(name)");')
1227             ->endif
1228             ->line('HV* self_hv = (HV*)SvRV(ST(0));')
1229             ->line('STRLEN nlen;')
1230             ->line('const char* name = SvPV(ST(1), nlen);')
1231             ->blank
1232             ->line('SV** req_sv = hv_fetchs(self_hv, "request", 0);')
1233             ->if('!req_sv || !*req_sv || !SvOK(*req_sv)')
1234             ->line('XSRETURN_UNDEF;')
1235             ->endif
1236             ->blank
1237             ->comment('If request is a hashref, check headers')
1238             ->if('SvROK(*req_sv) && SvTYPE(SvRV(*req_sv)) == SVt_PVHV')
1239             ->line('HV* req_hv = (HV*)SvRV(*req_sv);')
1240             ->line('SV** headers_sv = hv_fetchs(req_hv, "headers", 0);')
1241             ->if('headers_sv && *headers_sv && SvROK(*headers_sv)')
1242             ->line('HV* headers_hv = (HV*)SvRV(*headers_sv);')
1243             ->comment('Lowercase the name for lookup')
1244             ->line('char* lc_name = (char*)alloca(nlen + 1);')
1245             ->for('STRLEN i = 0', 'i < nlen', 'i++')
1246             ->line('lc_name[i] = tolower((unsigned char)name[i]);')
1247             ->endfor
1248             ->line('lc_name[nlen] = \'\\0\';')
1249             ->blank
1250             ->line('SV** val = hv_fetch(headers_hv, lc_name, nlen, 0);')
1251             ->if('val && *val')
1252             ->line('ST(0) = *val;')
1253             ->line('XSRETURN(1);')
1254             ->endif
1255             ->endif
1256             ->line('XSRETURN_UNDEF;')
1257             ->endif
1258             ->blank
1259             ->comment('If request is an object, call header method')
1260             ->line('ENTER;')
1261             ->line('SAVETMPS;')
1262             ->line('PUSHMARK(SP);')
1263             ->line('XPUSHs(*req_sv);')
1264             ->line('XPUSHs(ST(1));')
1265             ->line('PUTBACK;')
1266             ->line('int count = call_method("header", G_SCALAR | G_EVAL);')
1267             ->line('SPAGAIN;')
1268             ->blank
1269             ->if('count >= 1')
1270             ->line('SV* result = POPs;')
1271             ->line('ST(0) = sv_2mortal(newSVsv(result));')
1272             ->line('FREETMPS;')
1273             ->line('LEAVE;')
1274             ->line('XSRETURN(1);')
1275             ->endif
1276             ->blank
1277             ->line('FREETMPS;')
1278             ->line('LEAVE;')
1279             ->line('XSRETURN_UNDEF;')
1280             ->xs_end
1281             ->blank;
1282             }
1283              
1284             # XS function registry for JIT compilation
1285             sub get_xs_functions {
1286             return {
1287 2     2 0 1427772 'Hypersonic::WebSocket::new' => { source => 'xs_websocket_new', is_xs_native => 1 },
1288             'Hypersonic::WebSocket::fd' => { source => 'xs_websocket_fd', is_xs_native => 1 },
1289             'Hypersonic::WebSocket::state' => { source => 'xs_websocket_state', is_xs_native => 1 },
1290             'Hypersonic::WebSocket::protocol' => { source => 'xs_websocket_protocol', is_xs_native => 1 },
1291             'Hypersonic::WebSocket::stream' => { source => 'xs_websocket_stream', is_xs_native => 1 },
1292             'Hypersonic::WebSocket::request' => { source => 'xs_websocket_request', is_xs_native => 1 },
1293             'Hypersonic::WebSocket::is_open' => { source => 'xs_websocket_is_open', is_xs_native => 1 },
1294             'Hypersonic::WebSocket::is_closing' => { source => 'xs_websocket_is_closing', is_xs_native => 1 },
1295             'Hypersonic::WebSocket::is_closed' => { source => 'xs_websocket_is_closed', is_xs_native => 1 },
1296             'Hypersonic::WebSocket::on' => { source => 'xs_websocket_on', is_xs_native => 1 },
1297             'Hypersonic::WebSocket::emit' => { source => 'xs_websocket_emit', is_xs_native => 1 },
1298             'Hypersonic::WebSocket::accept' => { source => 'xs_websocket_accept', is_xs_native => 1 },
1299             'Hypersonic::WebSocket::send' => { source => 'xs_websocket_send', is_xs_native => 1 },
1300             'Hypersonic::WebSocket::send_binary' => { source => 'xs_websocket_send_binary', is_xs_native => 1 },
1301             'Hypersonic::WebSocket::ping' => { source => 'xs_websocket_ping', is_xs_native => 1 },
1302             'Hypersonic::WebSocket::pong' => { source => 'xs_websocket_pong', is_xs_native => 1 },
1303             'Hypersonic::WebSocket::close' => { source => 'xs_websocket_close', is_xs_native => 1 },
1304             'Hypersonic::WebSocket::handle_close' => { source => 'xs_websocket_handle_close', is_xs_native => 1 },
1305             'Hypersonic::WebSocket::handle_message' => { source => 'xs_websocket_handle_message', is_xs_native => 1 },
1306             'Hypersonic::WebSocket::process_data' => { source => 'xs_websocket_process_data', is_xs_native => 1 },
1307             'Hypersonic::WebSocket::_flush_send_buffer' => { source => 'xs_websocket_flush_send_buffer', is_xs_native => 1 },
1308             'Hypersonic::WebSocket::param' => { source => 'xs_websocket_param', is_xs_native => 1 },
1309             'Hypersonic::WebSocket::header' => { source => 'xs_websocket_header', is_xs_native => 1 },
1310             };
1311             }
1312              
1313             1;
1314              
1315             __END__