File Coverage

blib/lib/Hypersonic/UA/Stream.pm
Criterion Covered Total %
statement 18 71 25.3
branch n/a
condition 0 2 0.0
subroutine 7 22 31.8
pod 0 16 0.0
total 25 111 22.5


line stmt bran cond sub pod time code
1             package Hypersonic::UA::Stream;
2              
3 1     1   207582 use strict;
  1         2  
  1         29  
4 1     1   4 use warnings;
  1         1  
  1         42  
5 1     1   15 use 5.010;
  1         3  
6              
7             our $VERSION = '0.15';
8              
9             use constant {
10 1         100 STATE_INIT => 0,
11             STATE_HEADERS => 1,
12             STATE_BODY => 2,
13             STATE_FINISHED => 3,
14             STATE_ERROR => 4,
15 1     1   34 };
  1         3  
16              
17             use constant {
18 1         66 SLOT_FD => 0,
19             SLOT_ON_HEADERS => 1,
20             SLOT_ON_DATA => 2,
21             SLOT_ON_COMPLETE => 3,
22             SLOT_ON_ERROR => 4,
23 1     1   4 };
  1         2  
24              
25 1     1   3 use constant MAX_STREAMS => 1024;
  1         1  
  1         1834  
26              
27             sub generate_c_code {
28 0     0 0 0 my ($class, $builder, $opts) = @_;
29              
30 0   0     0 my $max_streams = $opts->{max_streams} // MAX_STREAMS;
31              
32 0         0 $class->gen_stream_registry($builder, $max_streams);
33 0         0 $class->gen_xs_new($builder);
34 0         0 $class->gen_xs_fd($builder);
35 0         0 $class->gen_xs_state($builder);
36 0         0 $class->gen_xs_status($builder);
37 0         0 $class->gen_xs_headers($builder);
38 0         0 $class->gen_xs_is_complete($builder);
39 0         0 $class->gen_xs_is_error($builder);
40 0         0 $class->gen_xs_read_chunk($builder);
41 0         0 $class->gen_xs_abort($builder);
42             }
43              
44             sub get_xs_functions {
45             return {
46 1     1 0 6983 'Hypersonic::UA::Stream::new' => { source => 'xs_uastream_new', is_xs_native => 1 },
47             'Hypersonic::UA::Stream::fd' => { source => 'xs_uastream_fd', is_xs_native => 1 },
48             'Hypersonic::UA::Stream::state' => { source => 'xs_uastream_state', is_xs_native => 1 },
49             'Hypersonic::UA::Stream::status' => { source => 'xs_uastream_status', is_xs_native => 1 },
50             'Hypersonic::UA::Stream::headers' => { source => 'xs_uastream_headers', is_xs_native => 1 },
51             'Hypersonic::UA::Stream::is_complete' => { source => 'xs_uastream_is_complete', is_xs_native => 1 },
52             'Hypersonic::UA::Stream::is_error' => { source => 'xs_uastream_is_error', is_xs_native => 1 },
53             'Hypersonic::UA::Stream::read_chunk' => { source => 'xs_uastream_read_chunk', is_xs_native => 1 },
54             'Hypersonic::UA::Stream::abort' => { source => 'xs_uastream_abort', is_xs_native => 1 },
55             };
56             }
57              
58             sub gen_stream_registry {
59 0     0 0   my ($class, $builder, $max_streams) = @_;
60              
61 0           $builder->line("#define UA_MAX_STREAMS $max_streams")
62             ->line('#define UA_STREAM_STATE_INIT 0')
63             ->line('#define UA_STREAM_STATE_HEADERS 1')
64             ->line('#define UA_STREAM_STATE_BODY 2')
65             ->line('#define UA_STREAM_STATE_FINISHED 3')
66             ->line('#define UA_STREAM_STATE_ERROR 4')
67             ->line('#define UA_STREAM_BUFFER_INITIAL 65536')
68             ->blank;
69              
70 0           $builder->line('typedef struct {')
71             ->line(' int fd;')
72             ->line(' int state;')
73             ->line(' int tls;')
74             ->line(' int status;')
75             ->line(' int http_minor;')
76             ->line(' int64_t content_length;')
77             ->line(' int chunked;')
78             ->line(' int64_t bytes_received;')
79             ->line(' int64_t chunk_remaining;')
80             ->line(' int in_chunk;')
81             ->line(' char* buffer;')
82             ->line(' size_t buffer_len;')
83             ->line(' size_t buffer_cap;')
84             ->line(' HV* headers_hv;')
85             ->line('} UAStreamEntry;')
86             ->blank
87             ->line('static UAStreamEntry ua_stream_registry[UA_MAX_STREAMS];')
88             ->blank;
89              
90 0           $builder->line('static UAStreamEntry* ua_stream_find(int fd) {')
91             ->line(' int i;')
92             ->line(' for (i = 0; i < UA_MAX_STREAMS; i++) {')
93             ->line(' if (ua_stream_registry[i].fd == fd) {')
94             ->line(' return &ua_stream_registry[i];')
95             ->line(' }')
96             ->line(' }')
97             ->line(' return NULL;')
98             ->line('}')
99             ->blank;
100              
101 0           $builder->line('static UAStreamEntry* ua_stream_alloc(int fd) {')
102             ->line(' int i;')
103             ->line(' for (i = 0; i < UA_MAX_STREAMS; i++) {')
104             ->line(' if (ua_stream_registry[i].fd == -1) {')
105             ->line(' UAStreamEntry* s = &ua_stream_registry[i];')
106             ->line(' memset(s, 0, sizeof(UAStreamEntry));')
107             ->line(' s->fd = fd;')
108             ->line(' s->state = UA_STREAM_STATE_INIT;')
109             ->line(' s->content_length = -1;')
110             ->line(' s->buffer = (char*)malloc(UA_STREAM_BUFFER_INITIAL);')
111             ->line(' s->buffer_cap = UA_STREAM_BUFFER_INITIAL;')
112             ->line(' s->buffer_len = 0;')
113             ->line(' return s;')
114             ->line(' }')
115             ->line(' }')
116             ->line(' return NULL;')
117             ->line('}')
118             ->blank;
119              
120 0           $builder->line('static void ua_stream_free(UAStreamEntry* s) {')
121             ->line(' if (s->buffer) free(s->buffer);')
122             ->line(' if (s->headers_hv) SvREFCNT_dec((SV*)s->headers_hv);')
123             ->line(' s->fd = -1;')
124             ->line(' s->buffer = NULL;')
125             ->line(' s->headers_hv = NULL;')
126             ->line('}')
127             ->blank;
128              
129 0           $builder->line('static int ua_stream_buffer_append(UAStreamEntry* s, const char* data, size_t len) {')
130             ->line(' if (s->buffer_len + len > s->buffer_cap) {')
131             ->line(' size_t new_cap = s->buffer_cap * 2;')
132             ->line(' while (new_cap < s->buffer_len + len) new_cap *= 2;')
133             ->line(' char* new_buf = (char*)realloc(s->buffer, new_cap);')
134             ->line(' if (!new_buf) return 0;')
135             ->line(' s->buffer = new_buf;')
136             ->line(' s->buffer_cap = new_cap;')
137             ->line(' }')
138             ->line(' memcpy(s->buffer + s->buffer_len, data, len);')
139             ->line(' s->buffer_len += len;')
140             ->line(' return 1;')
141             ->line('}')
142             ->blank;
143              
144 0           $builder->line('static void ua_stream_parse_headers(UAStreamEntry* s) {')
145             ->line(' char* end = memmem(s->buffer, s->buffer_len, "\\r\\n\\r\\n", 4);')
146             ->line(' if (!end) return;')
147             ->blank
148             ->line(' size_t headers_len = end - s->buffer;')
149             ->blank
150             ->line(' if (s->buffer_len > 12 && memcmp(s->buffer, "HTTP/1.", 7) == 0) {')
151             ->line(' s->http_minor = s->buffer[7] - \'0\';')
152             ->line(' s->status = atoi(s->buffer + 9);')
153             ->line(' }')
154             ->blank
155             ->line(' s->headers_hv = newHV();')
156             ->blank
157             ->line(' char* p = memchr(s->buffer, \'\\n\', headers_len);')
158             ->line(' if (p) p++;')
159             ->blank
160             ->line(' while (p && p < end) {')
161             ->line(' char* line_end = memchr(p, \'\\n\', end - p);')
162             ->line(' if (!line_end) break;')
163             ->blank
164             ->line(' char* colon = memchr(p, \':\', line_end - p);')
165             ->line(' if (colon) {')
166             ->line(' size_t name_len = colon - p;')
167             ->line(' char* val = colon + 1;')
168             ->line(' while (val < line_end && *val == \' \') val++;')
169             ->line(' size_t val_len = line_end - val;')
170             ->line(' if (val_len > 0 && val[val_len-1] == \'\\r\') val_len--;')
171             ->blank
172             ->line(' char name_lower[256];')
173             ->line(' size_t i;')
174             ->line(' for (i = 0; i < name_len && i < 255; i++) {')
175             ->line(' char c = p[i];')
176             ->line(' name_lower[i] = (c >= \'A\' && c <= \'Z\') ? c + 32 : (c == \'-\' ? \'_\' : c);')
177             ->line(' }')
178             ->blank
179             ->line(' hv_store(s->headers_hv, name_lower, name_len, newSVpvn(val, val_len), 0);')
180             ->blank
181             ->line(' if (name_len == 14 && memcmp(name_lower, "content_length", 14) == 0) {')
182             ->line(' s->content_length = atoll(val);')
183             ->line(' }')
184             ->line(' if (name_len == 17 && memcmp(name_lower, "transfer_encoding", 17) == 0) {')
185             ->line(' if (memmem(val, val_len, "chunked", 7)) {')
186             ->line(' s->chunked = 1;')
187             ->line(' }')
188             ->line(' }')
189             ->line(' }')
190             ->line(' p = line_end + 1;')
191             ->line(' }')
192             ->blank
193             ->line(' size_t consumed = (end - s->buffer) + 4;')
194             ->line(' memmove(s->buffer, end + 4, s->buffer_len - consumed);')
195             ->line(' s->buffer_len -= consumed;')
196             ->blank
197             ->line(' s->state = UA_STREAM_STATE_BODY;')
198             ->line('}')
199             ->blank;
200              
201 0           $builder->line('static void ua_stream_process_chunked(UAStreamEntry* s, AV* obj) {')
202             ->line(' while (s->buffer_len > 0) {')
203             ->line(' if (s->in_chunk) {')
204             ->line(' size_t to_read = s->chunk_remaining;')
205             ->line(' if (to_read > s->buffer_len) to_read = s->buffer_len;')
206             ->blank
207             ->line(' if (to_read > 0) {')
208             ->line(' SV** cb_sv = av_fetch(obj, 2, 0);')
209             ->line(' if (cb_sv && SvOK(*cb_sv)) {')
210             ->line(' dSP;')
211             ->line(' ENTER;')
212             ->line(' SAVETMPS;')
213             ->line(' PUSHMARK(SP);')
214             ->line(' XPUSHs(sv_2mortal(newSVpvn(s->buffer, to_read)));')
215             ->line(' PUTBACK;')
216             ->line(' call_sv(*cb_sv, G_DISCARD);')
217             ->line(' FREETMPS;')
218             ->line(' LEAVE;')
219             ->line(' }')
220             ->blank
221             ->line(' memmove(s->buffer, s->buffer + to_read, s->buffer_len - to_read);')
222             ->line(' s->buffer_len -= to_read;')
223             ->line(' s->chunk_remaining -= to_read;')
224             ->line(' }')
225             ->blank
226             ->line(' if (s->chunk_remaining == 0) {')
227             ->line(' if (s->buffer_len >= 2) {')
228             ->line(' memmove(s->buffer, s->buffer + 2, s->buffer_len - 2);')
229             ->line(' s->buffer_len -= 2;')
230             ->line(' s->in_chunk = 0;')
231             ->line(' } else {')
232             ->line(' break;')
233             ->line(' }')
234             ->line(' }')
235             ->line(' } else {')
236             ->line(' char* crlf = memmem(s->buffer, s->buffer_len, "\\r\\n", 2);')
237             ->line(' if (!crlf) break;')
238             ->blank
239             ->line(' int64_t chunk_size = 0;')
240             ->line(' for (char* cp = s->buffer; cp < crlf; cp++) {')
241             ->line(' char c = *cp;')
242             ->line(' if (c >= \'0\' && c <= \'9\') chunk_size = chunk_size * 16 + (c - \'0\');')
243             ->line(' else if (c >= \'a\' && c <= \'f\') chunk_size = chunk_size * 16 + (c - \'a\' + 10);')
244             ->line(' else if (c >= \'A\' && c <= \'F\') chunk_size = chunk_size * 16 + (c - \'A\' + 10);')
245             ->line(' else break;')
246             ->line(' }')
247             ->blank
248             ->line(' size_t consumed = (crlf - s->buffer) + 2;')
249             ->line(' memmove(s->buffer, crlf + 2, s->buffer_len - consumed);')
250             ->line(' s->buffer_len -= consumed;')
251             ->blank
252             ->line(' if (chunk_size == 0) {')
253             ->line(' s->state = UA_STREAM_STATE_FINISHED;')
254             ->line(' SV** cb_sv = av_fetch(obj, 3, 0);')
255             ->line(' if (cb_sv && SvOK(*cb_sv)) {')
256             ->line(' dSP;')
257             ->line(' ENTER;')
258             ->line(' SAVETMPS;')
259             ->line(' PUSHMARK(SP);')
260             ->line(' PUTBACK;')
261             ->line(' call_sv(*cb_sv, G_DISCARD);')
262             ->line(' FREETMPS;')
263             ->line(' LEAVE;')
264             ->line(' }')
265             ->line(' return;')
266             ->line(' }')
267             ->blank
268             ->line(' s->chunk_remaining = chunk_size;')
269             ->line(' s->in_chunk = 1;')
270             ->line(' }')
271             ->line(' }')
272             ->line('}')
273             ->blank;
274              
275 0           $builder->line('static void ua_stream_process_content_length(UAStreamEntry* s, AV* obj) {')
276             ->line(' if (s->buffer_len > 0) {')
277             ->line(' SV** cb_sv = av_fetch(obj, 2, 0);')
278             ->line(' if (cb_sv && SvOK(*cb_sv)) {')
279             ->line(' dSP;')
280             ->line(' ENTER;')
281             ->line(' SAVETMPS;')
282             ->line(' PUSHMARK(SP);')
283             ->line(' XPUSHs(sv_2mortal(newSVpvn(s->buffer, s->buffer_len)));')
284             ->line(' PUTBACK;')
285             ->line(' call_sv(*cb_sv, G_DISCARD);')
286             ->line(' FREETMPS;')
287             ->line(' LEAVE;')
288             ->line(' }')
289             ->line(' s->buffer_len = 0;')
290             ->line(' }')
291             ->blank
292             ->line(' if (s->content_length >= 0 && s->bytes_received >= (size_t)s->content_length) {')
293             ->line(' s->state = UA_STREAM_STATE_FINISHED;')
294             ->line(' SV** cb_sv = av_fetch(obj, 3, 0);')
295             ->line(' if (cb_sv && SvOK(*cb_sv)) {')
296             ->line(' dSP;')
297             ->line(' ENTER;')
298             ->line(' SAVETMPS;')
299             ->line(' PUSHMARK(SP);')
300             ->line(' PUTBACK;')
301             ->line(' call_sv(*cb_sv, G_DISCARD);')
302             ->line(' FREETMPS;')
303             ->line(' LEAVE;')
304             ->line(' }')
305             ->line(' }')
306             ->line('}')
307             ->blank;
308              
309 0           $builder->line('static void ua_stream_registry_init(void) {')
310             ->line(' int i;')
311             ->line(' for (i = 0; i < UA_MAX_STREAMS; i++) {')
312             ->line(' ua_stream_registry[i].fd = -1;')
313             ->line(' }')
314             ->line('}')
315             ->blank;
316             }
317              
318             sub gen_xs_new {
319 0     0 0   my ($class, $builder) = @_;
320              
321 0           $builder->comment('Create new stream')
322             ->xs_function('xs_uastream_new')
323             ->xs_preamble
324             ->line('int fd;')
325             ->line('int tls;')
326             ->line('UAStreamEntry* s;')
327             ->line('AV* obj;')
328             ->line('SV* rv;')
329             ->blank
330             ->line('if (items < 2) croak("Usage: Hypersonic::UA::Stream->new(fd, [tls])");')
331             ->line('fd = (int)SvIV(ST(1));')
332             ->line('tls = (items > 2) ? (int)SvIV(ST(2)) : 0;')
333             ->blank
334             ->line('s = ua_stream_alloc(fd);')
335             ->line('if (!s) croak("Stream registry full");')
336             ->blank
337             ->line('s->tls = tls;')
338             ->blank
339             ->line('obj = newAV();')
340             ->line('av_extend(obj, 4);')
341             ->line('av_store(obj, 0, newSViv(fd));')
342             ->line('av_store(obj, 1, &PL_sv_undef);')
343             ->line('av_store(obj, 2, &PL_sv_undef);')
344             ->line('av_store(obj, 3, &PL_sv_undef);')
345             ->line('av_store(obj, 4, &PL_sv_undef);')
346             ->blank
347             ->line('rv = newRV_noinc((SV*)obj);')
348             ->line('sv_bless(rv, gv_stashpv("Hypersonic::UA::Stream", GV_ADD));')
349             ->line('ST(0) = sv_2mortal(rv);')
350             ->xs_return('1')
351             ->xs_end
352             ->blank;
353             }
354              
355             sub gen_xs_fd {
356 0     0 0   my ($class, $builder) = @_;
357              
358 0           $builder->comment('Get stream fd')
359             ->xs_function('xs_uastream_fd')
360             ->xs_preamble
361             ->line('AV* obj;')
362             ->line('SV** fd_sv;')
363             ->blank
364             ->line('if (items != 1) croak("Usage: $stream->fd()");')
365             ->line('obj = (AV*)SvRV(ST(0));')
366             ->line('fd_sv = av_fetch(obj, 0, 0);')
367             ->line('ST(0) = fd_sv ? *fd_sv : &PL_sv_undef;')
368             ->xs_return('1')
369             ->xs_end
370             ->blank;
371             }
372              
373             sub gen_xs_state {
374 0     0 0   my ($class, $builder) = @_;
375              
376 0           $builder->comment('Get stream state')
377             ->xs_function('xs_uastream_state')
378             ->xs_preamble
379             ->line('AV* obj;')
380             ->line('SV** fd_sv;')
381             ->line('int fd;')
382             ->line('UAStreamEntry* s;')
383             ->blank
384             ->line('if (items != 1) croak("Usage: $stream->state()");')
385             ->line('obj = (AV*)SvRV(ST(0));')
386             ->line('fd_sv = av_fetch(obj, 0, 0);')
387             ->line('fd = (int)SvIV(*fd_sv);')
388             ->blank
389             ->line('s = ua_stream_find(fd);')
390             ->line('ST(0) = s ? sv_2mortal(newSViv(s->state)) : &PL_sv_undef;')
391             ->xs_return('1')
392             ->xs_end
393             ->blank;
394             }
395              
396             sub gen_xs_status {
397 0     0 0   my ($class, $builder) = @_;
398              
399 0           $builder->comment('Get HTTP status')
400             ->xs_function('xs_uastream_status')
401             ->xs_preamble
402             ->line('if (items != 1) croak("Usage: $stream->status()");')
403             ->line('AV* obj = (AV*)SvRV(ST(0));')
404             ->line('SV** fd_sv = av_fetch(obj, 0, 0);')
405             ->line('int fd = (int)SvIV(*fd_sv);')
406             ->blank
407             ->line('UAStreamEntry* s = ua_stream_find(fd);')
408             ->line('ST(0) = s ? sv_2mortal(newSViv(s->status)) : &PL_sv_undef;')
409             ->xs_return('1')
410             ->xs_end
411             ->blank;
412             }
413              
414             sub gen_xs_headers {
415 0     0 0   my ($class, $builder) = @_;
416              
417 0           $builder->comment('Get parsed headers')
418             ->xs_function('xs_uastream_headers')
419             ->xs_preamble
420             ->line('if (items != 1) croak("Usage: $stream->headers()");')
421             ->line('AV* obj = (AV*)SvRV(ST(0));')
422             ->line('SV** fd_sv = av_fetch(obj, 0, 0);')
423             ->line('int fd = (int)SvIV(*fd_sv);')
424             ->blank
425             ->line('UAStreamEntry* s = ua_stream_find(fd);')
426             ->if('s && s->headers_hv')
427             ->line('ST(0) = sv_2mortal(newRV_inc((SV*)s->headers_hv));')
428             ->else
429             ->line('ST(0) = &PL_sv_undef;')
430             ->endif
431             ->xs_return('1')
432             ->xs_end
433             ->blank;
434             }
435              
436             sub gen_xs_is_complete {
437 0     0 0   my ($class, $builder) = @_;
438              
439 0           $builder->comment('Check if stream finished')
440             ->xs_function('xs_uastream_is_complete')
441             ->xs_preamble
442             ->line('if (items != 1) croak("Usage: $stream->is_complete()");')
443             ->line('AV* obj = (AV*)SvRV(ST(0));')
444             ->line('SV** fd_sv = av_fetch(obj, 0, 0);')
445             ->line('int fd = (int)SvIV(*fd_sv);')
446             ->blank
447             ->line('UAStreamEntry* s = ua_stream_find(fd);')
448             ->line('ST(0) = (s && s->state == UA_STREAM_STATE_FINISHED) ? &PL_sv_yes : &PL_sv_no;')
449             ->xs_return('1')
450             ->xs_end
451             ->blank;
452             }
453              
454             sub gen_xs_is_error {
455 0     0 0   my ($class, $builder) = @_;
456              
457 0           $builder->comment('Check if stream errored')
458             ->xs_function('xs_uastream_is_error')
459             ->xs_preamble
460             ->line('if (items != 1) croak("Usage: $stream->is_error()");')
461             ->line('AV* obj = (AV*)SvRV(ST(0));')
462             ->line('SV** fd_sv = av_fetch(obj, 0, 0);')
463             ->line('int fd = (int)SvIV(*fd_sv);')
464             ->blank
465             ->line('UAStreamEntry* s = ua_stream_find(fd);')
466             ->line('ST(0) = (s && s->state == UA_STREAM_STATE_ERROR) ? &PL_sv_yes : &PL_sv_no;')
467             ->xs_return('1')
468             ->xs_end
469             ->blank;
470             }
471              
472             sub gen_xs_read_chunk {
473 0     0 0   my ($class, $builder) = @_;
474              
475 0           $builder->comment('Read and process data chunk')
476             ->xs_function('xs_uastream_read_chunk')
477             ->xs_preamble
478             ->line('if (items != 1) croak("Usage: $stream->read_chunk()");')
479             ->line('AV* obj = (AV*)SvRV(ST(0));')
480             ->line('SV** fd_sv = av_fetch(obj, 0, 0);')
481             ->line('int fd = (int)SvIV(*fd_sv);')
482             ->blank
483             ->line('UAStreamEntry* s = ua_stream_find(fd);')
484             ->if('!s || s->state >= UA_STREAM_STATE_FINISHED')
485             ->line('ST(0) = &PL_sv_undef;')
486             ->line('XSRETURN(1);')
487             ->endif
488             ->blank
489             ->line('static char recv_buf[65536];')
490             ->line('ssize_t n = recv(fd, recv_buf, sizeof(recv_buf), MSG_DONTWAIT);')
491             ->blank
492             ->if('n < 0')
493             ->if('errno == EAGAIN || errno == EWOULDBLOCK')
494             ->line('ST(0) = sv_2mortal(newSVpvn("", 0));')
495             ->else
496             ->line('s->state = UA_STREAM_STATE_ERROR;')
497             ->line('ST(0) = &PL_sv_undef;')
498             ->endif
499             ->line('XSRETURN(1);')
500             ->endif
501             ->blank
502             ->if('n == 0')
503             ->if('s->state == UA_STREAM_STATE_BODY && s->content_length < 0 && !s->chunked')
504             ->line('s->state = UA_STREAM_STATE_FINISHED;')
505             ->elsif('s->state < UA_STREAM_STATE_FINISHED')
506             ->line('s->state = UA_STREAM_STATE_ERROR;')
507             ->endif
508             ->line('ST(0) = &PL_sv_undef;')
509             ->line('XSRETURN(1);')
510             ->endif
511             ->blank
512             ->line('ua_stream_buffer_append(s, recv_buf, n);')
513             ->line('s->bytes_received += n;')
514             ->blank
515             ->if('s->state <= UA_STREAM_STATE_HEADERS')
516             ->line('ua_stream_parse_headers(s);')
517             ->endif
518             ->blank
519             ->if('s->state == UA_STREAM_STATE_BODY')
520             ->if('s->chunked')
521             ->line('ua_stream_process_chunked(s, obj);')
522             ->else
523             ->line('ua_stream_process_content_length(s, obj);')
524             ->endif
525             ->endif
526             ->blank
527             ->line('ST(0) = sv_2mortal(newSViv(n));')
528             ->xs_return('1')
529             ->xs_end
530             ->blank;
531             }
532              
533             sub gen_xs_abort {
534 0     0 0   my ($class, $builder) = @_;
535              
536 0           $builder->comment('Abort stream')
537             ->xs_function('xs_uastream_abort')
538             ->xs_preamble
539             ->line('if (items < 1) croak("Usage: $stream->abort([reason])");')
540             ->line('AV* obj = (AV*)SvRV(ST(0));')
541             ->line('SV** fd_sv = av_fetch(obj, 0, 0);')
542             ->line('int fd = (int)SvIV(*fd_sv);')
543             ->blank
544             ->line('UAStreamEntry* s = ua_stream_find(fd);')
545             ->if('s')
546             ->line('s->state = UA_STREAM_STATE_ERROR;')
547             ->line('close(fd);')
548             ->blank
549             ->line('SV** cb_sv = av_fetch(obj, 4, 0);')
550             ->if('cb_sv && SvOK(*cb_sv)')
551             ->line('const char* reason = (items > 1 && SvOK(ST(1))) ? SvPV_nolen(ST(1)) : "Aborted";')
552             ->line('dSP;')
553             ->line('ENTER;')
554             ->line('SAVETMPS;')
555             ->line('PUSHMARK(SP);')
556             ->line('XPUSHs(sv_2mortal(newSVpv(reason, 0)));')
557             ->line('PUTBACK;')
558             ->line('call_sv(*cb_sv, G_DISCARD);')
559             ->line('FREETMPS;')
560             ->line('LEAVE;')
561             ->endif
562             ->blank
563             ->line('ua_stream_free(s);')
564             ->endif
565             ->blank
566             ->line('ST(0) = &PL_sv_yes;')
567             ->xs_return('1')
568             ->xs_end
569             ->blank;
570             }
571              
572             sub on_headers {
573 0     0 0   my ($self, $cb) = @_;
574 0           $self->[SLOT_ON_HEADERS] = $cb;
575 0           return $self;
576             }
577              
578             sub on_data {
579 0     0 0   my ($self, $cb) = @_;
580 0           $self->[SLOT_ON_DATA] = $cb;
581 0           return $self;
582             }
583              
584             sub on_complete {
585 0     0 0   my ($self, $cb) = @_;
586 0           $self->[SLOT_ON_COMPLETE] = $cb;
587 0           return $self;
588             }
589              
590             sub on_error {
591 0     0 0   my ($self, $cb) = @_;
592 0           $self->[SLOT_ON_ERROR] = $cb;
593 0           return $self;
594             }
595              
596             1;