File Coverage

feersum_conn.xs
Criterion Covered Total %
statement 247 318 77.6
branch 201 328 61.2
condition n/a
subroutine n/a
pod n/a
total 448 646 69.3


line stmt bran cond sub pod time code
1             MODULE = Feersum PACKAGE = Feersum::Connection::Handle
2              
3             PROTOTYPES: ENABLE
4              
5             int
6             fileno (feer_conn_handle *hdl)
7             CODE:
8 0 0         RETVAL = c->fd;
9             OUTPUT:
10             RETVAL
11              
12             void
13             DESTROY (SV *self)
14             ALIAS:
15             Feersum::Connection::Reader::DESTROY = 1
16             Feersum::Connection::Writer::DESTROY = 2
17             PPCODE:
18             {
19 383           feer_conn_handle *hdl = sv_2feer_conn_handle(self, 0);
20              
21 383 100         if (hdl == NULL) {
22             trace3("DESTROY handle (closed) class=%s\n",
23             HvNAME(SvSTASH(SvRV(self))));
24             }
25             else {
26 311           struct feer_conn *c = (struct feer_conn *)hdl;
27             trace3("DESTROY handle fd=%d, class=%s\n", c->fd,
28             HvNAME(SvSTASH(SvRV(self))));
29 311 100         if (ix == 2)
30 15           feersum_close_handle(aTHX_ c, 1);
31             else
32 296           SvREFCNT_dec(c->self); // reader: balance new_feer_conn_handle
33             }
34             }
35              
36             SV*
37             read (feer_conn_handle *hdl, SV *buf, size_t len, ...)
38             PROTOTYPE: $$$;$
39             PPCODE:
40             {
41 99           STRLEN buf_len = 0, src_len = 0;
42             ssize_t offset;
43 99           char *buf_ptr, *src_ptr = NULL;
44              
45             // optimizes for the "read everything" case.
46              
47 99 100         if (unlikely(items == 4) && SvOK(ST(3)) && SvIOK(ST(3)))
    50          
    50          
48 2           offset = SvIV(ST(3));
49             else
50 97           offset = 0;
51              
52             trace("read fd=%d : request len=%"Sz_uf" off=%"Ssz_df"\n",
53             c->fd, (Sz)len, (Ssz)offset);
54              
55 99 50         if (unlikely(c->receiving <= RECEIVE_HEADERS))
56 0           croak("can't call read() until the body begins to arrive");
57              
58 99 100         if (!SvPOK(buf)) {
59             // force to a PV and ensure buffer space
60 9           sv_setpvn(buf,"",0);
61 9 50         SvGROW(buf, len+1);
    100          
62             }
63              
64 99 50         if (unlikely(SvREADONLY(buf)))
65 0           croak("buffer must not be read-only");
66              
67 99 50         if (unlikely(len == 0))
68 0           XSRETURN_IV(0); // assumes undef buffer got allocated to empty-string
69              
70 99           buf_ptr = SvPV(buf, buf_len);
71 99 100         if (likely(c->rbuf))
72 96           src_ptr = SvPV(c->rbuf, src_len);
73              
74 99 100         if (unlikely(offset < 0))
75 1 50         offset = (-offset >= c->received_cl) ? 0 : c->received_cl + offset;
76              
77             // Defensive: ensure offset doesn't exceed buffer (shouldn't happen in normal operation)
78 99 50         if (unlikely(offset > (ssize_t)src_len))
79 0           offset = src_len;
80              
81 99 100         if (unlikely(len + offset > src_len))
82 9           len = src_len - offset;
83              
84             // Don't read past Content-Length boundary into pipelined request data
85 99 50         if (c->expected_cl > 0) {
86 99           ssize_t consumed = c->received_cl - (ssize_t)src_len;
87 99           ssize_t remaining_body = c->expected_cl - consumed - offset;
88 99 100         if (remaining_body <= 0)
89 3           XSRETURN_IV(0);
90 96 100         if ((ssize_t)len > remaining_body)
91 1           len = (size_t)remaining_body;
92             }
93              
94             trace("read fd=%d : normalized len=%"Sz_uf" off=%"Ssz_df" src_len=%"Sz_uf"\n",
95             c->fd, (Sz)len, (Ssz)offset, (Sz)src_len);
96              
97 96 50         if (unlikely(!c->rbuf || src_len == 0 || offset >= c->received_cl)) {
    50          
    50          
    50          
98             trace2("rbuf empty during read %d\n", c->fd);
99 0 0         if (c->receiving == RECEIVE_SHUTDOWN) {
100 0           XSRETURN_IV(0);
101             }
102             else {
103 0           errno = EAGAIN;
104 0           XSRETURN_UNDEF;
105             }
106             }
107              
108 96 100         if (likely(len == src_len && offset == 0)) {
    50          
109             trace2("appending entire rbuf fd=%d\n", c->fd);
110 80           sv_2mortal(c->rbuf); // allow pv to be stolen
111 80 100         if (likely(buf_len == 0)) {
112 78           sv_setsv(buf, c->rbuf);
113             }
114             else {
115 2           sv_catsv(buf, c->rbuf);
116             }
117 80           c->rbuf = NULL;
118             }
119             else {
120 16           src_ptr += offset;
121             trace2("appending partial rbuf fd=%d len=%"Sz_uf" off=%"Ssz_df" ptr=%p\n",
122             c->fd, len, offset, src_ptr);
123 16 100         SvGROW(buf, SvCUR(buf) + len);
    100          
124 16           sv_catpvn(buf, src_ptr, len);
125 16 100         if (likely(items == 3)) {
126             // there wasn't an offset param, throw away beginning
127             // Ensure we own the buffer before modifying with sv_chop
128 14 50         if (unlikely(SvREFCNT(c->rbuf) > 1 || SvREADONLY(c->rbuf))) {
    50          
129 0           SV *copy = newSVsv(c->rbuf);
130 0           SvREFCNT_dec(c->rbuf);
131 0           c->rbuf = copy;
132             }
133             // Safety: ensure len doesn't exceed current buffer length
134 14           STRLEN cur_len = SvCUR(c->rbuf);
135 14 50         if (unlikely(len > cur_len)) len = cur_len;
136 14           sv_chop(c->rbuf, SvPVX(c->rbuf) + len);
137             }
138             }
139              
140 96           XSRETURN_IV(len);
141             }
142              
143             STRLEN
144             write (feer_conn_handle *hdl, ...)
145             PROTOTYPE: $;$
146             CODE:
147             {
148 74 50         if (unlikely(c->responding != RESPOND_STREAMING))
149 0           croak("can only call write in streaming mode");
150              
151             // RFC 7230 §3.3: 1xx/204/205/304 MUST NOT have a body — discard writes
152             // (auto_cl is only set for H1; H2 handles this in its own DATA provider)
153 74 50         if (unlikely(!c->auto_cl && !h2_is_stream(c)))
    0          
154 0           XSRETURN_IV(0);
155              
156 74 50         SV *body = (items == 2) ? ST(1) : &PL_sv_undef;
157 74 50         if (unlikely(!body || !SvOK(body)))
    50          
158 0           XSRETURN_IV(0);
159              
160             trace("write fd=%d c=%p, body=%p\n", c->fd, c, body);
161 74 100         if (SvROK(body)) {
162 1           SV *refd = SvRV(body);
163 1 50         if (SvOK(refd) && SvPOK(refd)) {
    0          
164 0           body = refd;
165             }
166             else {
167 1           croak("body must be a scalar, scalar ref or undef");
168             }
169             }
170 73           (void)SvPV(body, RETVAL);
171              
172 73 50         if (!h2_try_write_chunk(aTHX_ c, body)) {
173 73 100         if (c->use_chunked)
174 45           add_chunk_sv_to_wbuf(c, body);
175             else
176 28           add_sv_to_wbuf(c, body);
177              
178 73           conn_write_ready(c);
179             }
180             }
181             OUTPUT:
182             RETVAL
183              
184             void
185             write_array (feer_conn_handle *hdl, AV *abody)
186             PROTOTYPE: $$
187             PPCODE:
188             {
189 2 50         if (unlikely(c->responding != RESPOND_STREAMING))
190 0           croak("can only call write in streaming mode");
191              
192 2 50         if (unlikely(!c->auto_cl && !h2_is_stream(c)))
    0          
193 0           XSRETURN_EMPTY;
194              
195             trace("write_array fd=%d c=%p, abody=%p\n", c->fd, c, abody);
196              
197 2           I32 amax = av_len(abody);
198             I32 i;
199              
200 2 50         if (h2_is_stream(c)) {
201             /* H2 stream: feed each element through the H2 data provider */
202 0 0         for (i=0; i<=amax; i++) {
203 0           SV *sv = fetch_av_normal(aTHX_ abody, i);
204 0 0         if (likely(sv)) h2_try_write_chunk(aTHX_ c, sv);
205             }
206 0           XSRETURN_EMPTY;
207             }
208              
209 2 50         if (c->use_chunked) {
210 12 100         for (i=0; i<=amax; i++) {
211 10           SV *sv = fetch_av_normal(aTHX_ abody, i);
212 10 100         if (likely(sv)) add_chunk_sv_to_wbuf(c, sv);
213             }
214             }
215             else {
216 0 0         for (i=0; i<=amax; i++) {
217 0           SV *sv = fetch_av_normal(aTHX_ abody, i);
218 0 0         if (likely(sv)) add_sv_to_wbuf(c, sv);
219             }
220             }
221              
222 2           conn_write_ready(c);
223             }
224              
225             void
226             sendfile (feer_conn_handle *hdl, SV *fh, ...)
227             PROTOTYPE: $$;$$
228             PPCODE:
229             {
230             #ifdef __linux__
231 12 50         if (h2_is_stream(c))
232 0           croak("sendfile not supported for HTTP/2 streams (use write instead)");
233 12 50         if (unlikely(c->responding != RESPOND_STREAMING))
234 0           croak("sendfile: can only call after start_streaming()");
235              
236             // Get file descriptor from filehandle
237 12           int file_fd = -1;
238 12           off_t offset = 0;
239 12           size_t length = 0;
240              
241 12 50         if (SvIOK(fh)) {
242             // Bare file descriptor
243 0           file_fd = SvIV(fh);
244             }
245 24 50         else if (SvROK(fh) && SvTYPE(SvRV(fh)) == SVt_PVGV) {
    50          
246             // Glob reference (filehandle)
247 12           IO *io = GvIOp(SvRV(fh));
248 12 50         if (io && IoIFP(io)) {
    100          
249 11           file_fd = PerlIO_fileno(IoIFP(io));
250             }
251             }
252 0 0         else if (SvTYPE(fh) == SVt_PVGV) {
253             // Bare glob
254 0           IO *io = GvIOp(fh);
255 0 0         if (io && IoIFP(io)) {
    0          
256 0           file_fd = PerlIO_fileno(IoIFP(io));
257             }
258             }
259              
260 12 100         if (file_fd < 0)
261 1           croak("sendfile: invalid file handle");
262              
263             // Get file size for length if not specified
264             struct stat st;
265 11 50         if (fstat(file_fd, &st) < 0)
266 0           croak("sendfile: fstat failed: %s", strerror(errno));
267              
268 11 100         if (!S_ISREG(st.st_mode))
269 1           croak("sendfile: not a regular file");
270              
271             // Parse optional offset and validate before using
272 10 100         if (items >= 3 && SvOK(ST(2))) {
    50          
273 7           IV offset_iv = SvIV(ST(2));
274 7 100         if (offset_iv < 0)
275 1           croak("sendfile: offset must be non-negative");
276 6           offset = (off_t)offset_iv;
277             }
278              
279 9 100         if (st.st_size == 0) {
280 1           XSRETURN_EMPTY;
281             }
282 8 100         if (offset >= st.st_size)
283 1           croak("sendfile: offset out of range");
284              
285 7 100         if (items >= 4 && SvOK(ST(3))) {
    50          
286 2           UV length_uv = SvUV(ST(3));
287             // Check that length fits in ssize_t (signed) before casting
288             // This prevents bypass via values >= 2^63 becoming negative
289             // Use (UV)((~(size_t)0) >> 1) as portable SSIZE_MAX
290 2 50         if (length_uv > (UV)((~(size_t)0) >> 1))
291 0           croak("sendfile: length too large");
292 2           length = (size_t)length_uv;
293             // Validate length doesn't exceed file size - offset
294 2 50         if (length > (size_t)(st.st_size - offset))
295 2           croak("sendfile: offset + length exceeds file size");
296             } else {
297             // Default: send from offset to end of file
298 5           length = st.st_size - offset;
299             }
300              
301 5 50         if (length == 0) {
302             // Nothing to send, just return
303 0           XSRETURN_EMPTY;
304             }
305              
306             trace("sendfile setup: fd=%d file_fd=%d off=%ld len=%zu\n",
307             c->fd, file_fd, (long)offset, length);
308              
309             // Close any in-progress sendfile before starting a new one
310 5 50         CLOSE_SENDFILE_FD(c);
    0          
311             // Dup the fd so we own it (caller can close their handle)
312 5           c->sendfile_fd = dup(file_fd);
313 5 50         if (c->sendfile_fd < 0)
314 0           croak("sendfile: dup failed: %s", strerror(errno));
315              
316 5           c->sendfile_off = offset;
317 5           c->sendfile_remain = length;
318              
319 5           conn_write_ready(c);
320 5           XSRETURN_EMPTY;
321             #else
322             PERL_UNUSED_VAR(fh);
323             croak("sendfile: only supported on Linux");
324             #endif
325             }
326              
327             int
328             seek (feer_conn_handle *hdl, ssize_t offset, ...)
329             PROTOTYPE: $$;$
330             CODE:
331             {
332 7           int whence = SEEK_CUR;
333 7 50         if (items == 3 && SvOK(ST(2)) && SvIOK(ST(2)))
    50          
    50          
334 7           whence = SvIV(ST(2));
335              
336             trace("seek fd=%d offset=%"Ssz_df" whence=%d\n", c->fd, offset, whence);
337              
338 7 50         if (unlikely(!c->rbuf)) {
339             // handle is effectively "closed"
340 0           RETVAL = 0;
341             }
342 7 100         else if (offset == 0) {
343 1           RETVAL = 1; // stay put for any whence
344             }
345 6 100         else if (offset > 0 && (whence == SEEK_CUR || whence == SEEK_SET)) {
    100          
    50          
346             STRLEN len;
347 2           const char *str = SvPV_const(c->rbuf, len);
348 2 50         if (offset > len)
349 0           offset = len;
350             // Ensure we own the buffer before modifying with sv_chop
351             // (sv_chop modifies the SV in-place, unsafe if shared)
352 2 50         if (SvREFCNT(c->rbuf) > 1 || SvREADONLY(c->rbuf)) {
    50          
353 0           SV *copy = newSVsv(c->rbuf);
354 0           SvREFCNT_dec(c->rbuf);
355 0           c->rbuf = copy;
356 0           str = SvPV_const(c->rbuf, len);
357             }
358 2           sv_chop(c->rbuf, str + offset);
359 2           RETVAL = 1;
360             }
361 6 50         else if (offset < 0 && whence == SEEK_END) {
    100          
362             STRLEN len;
363 2           const char *str = SvPV_const(c->rbuf, len);
364 2           offset += len; // can't be > len since block is offset<0
365 2 50         if (offset == 0) {
366 0           RETVAL = 1; // no-op, but OK
367             }
368 2 100         else if (offset > 0) {
369             // Ensure we own the buffer before modifying
370 1 50         if (SvREFCNT(c->rbuf) > 1 || SvREADONLY(c->rbuf)) {
    50          
371 0           SV *copy = newSVsv(c->rbuf);
372 0           SvREFCNT_dec(c->rbuf);
373 0           c->rbuf = copy;
374 0           str = SvPV_const(c->rbuf, len);
375             }
376 1           sv_chop(c->rbuf, str + offset);
377 1           RETVAL = 1;
378             }
379             else {
380             // past beginning of string
381 1           RETVAL = 0;
382             }
383             }
384             else {
385             // invalid seek
386 2           RETVAL = 0;
387             }
388             }
389             OUTPUT:
390             RETVAL
391              
392             int
393             close (feer_conn_handle *hdl)
394             PROTOTYPE: $
395             ALIAS:
396             Feersum::Connection::Reader::close = 1
397             Feersum::Connection::Writer::close = 2
398             CODE:
399             {
400             assert(ix && "close() must be called via Reader::close or Writer::close");
401 46           RETVAL = feersum_close_handle(aTHX_ c, (ix == 2));
402 46 100         SvUVX(hdl_sv) = 0;
403             }
404             OUTPUT:
405             RETVAL
406              
407             void
408             _poll_cb (feer_conn_handle *hdl, SV *cb)
409             PROTOTYPE: $$
410             ALIAS:
411             Feersum::Connection::Reader::poll_cb = 1
412             Feersum::Connection::Writer::poll_cb = 2
413             PPCODE:
414             {
415 23 50         if (unlikely(ix < 1 || ix > 2))
    50          
416 0           croak("can't call _poll_cb directly");
417              
418 23           bool is_read = (ix == 1);
419 23 100         SV **cb_slot = is_read ? &c->poll_read_cb : &c->poll_write_cb;
420              
421 23 100         if (*cb_slot != NULL) {
422 11           SvREFCNT_dec(*cb_slot);
423 11           *cb_slot = NULL;
424             }
425              
426 23 100         if (!SvOK(cb)) {
427             trace("unset poll_cb ix=%d\n", ix);
428 11 100         if (is_read) {
429             // Stop streaming mode if callback is unset
430 2 50         if (c->receiving == RECEIVE_STREAMING) {
431 2           change_receiving_state(c, RECEIVE_BODY);
432             }
433             }
434 11           return;
435             }
436 12 50         else if (unlikely(!IsCodeRef(cb)))
    50          
437 0           croak("must supply a code reference to poll_cb");
438              
439 12           *cb_slot = newSVsv(cb);
440              
441 12 100         if (is_read) {
442             // Switch to streaming receive mode
443             // Allow from RECEIVE_BODY (normal body) or RECEIVE_SHUTDOWN
444             // (post-upgrade, e.g. WebSocket 101 where body reading was stopped)
445 2 50         if (c->receiving == RECEIVE_BODY || c->receiving == RECEIVE_SHUTDOWN) {
    50          
446 2           change_receiving_state(c, RECEIVE_STREAMING);
447             }
448             // If there's already body data in rbuf, call the callback immediately
449 2 50         if (c->rbuf && SvCUR(c->rbuf) > 0) {
    50          
450 2           call_poll_callback(c, 0); // 0 = read callback
451             }
452             else {
453 0           start_read_watcher(c);
454             }
455             }
456             else {
457 10           conn_write_ready(c);
458             }
459             }
460              
461             SV*
462             response_guard (feer_conn_handle *hdl, ...)
463             PROTOTYPE: $;$
464             CODE:
465 3 100         RETVAL = feersum_conn_guard(aTHX_ c, (items==2) ? ST(1) : NULL);
466             OUTPUT:
467             RETVAL
468              
469             void
470             return_from_psgix_io (feer_conn_handle *hdl, SV *io_sv)
471             PROTOTYPE: $$
472             PPCODE:
473             {
474 1           SSize_t cnt = feersum_return_from_io(aTHX_ c, io_sv, "return_from_psgix_io");
475 1 50         mXPUSHi(cnt);
476             }
477              
478             MODULE = Feersum PACKAGE = Feersum::Connection
479              
480             PROTOTYPES: ENABLE
481              
482             SV *
483             start_streaming (struct feer_conn *c, SV *message, AV *headers)
484             PROTOTYPE: $$\@
485             CODE:
486 51           feersum_start_response(aTHX_ c, message, headers, 1);
487 50           RETVAL = new_feer_conn_handle(aTHX_ c, 1); // RETVAL gets mortalized
488             OUTPUT:
489             RETVAL
490              
491             int
492             is_http11 (struct feer_conn *c)
493             CODE:
494 6 50         RETVAL = c->is_http11;
495             OUTPUT:
496             RETVAL
497              
498             size_t
499             send_response (struct feer_conn *c, SV* message, AV *headers, SV *body)
500             PROTOTYPE: $$\@$
501             CODE:
502 383 50         if (unlikely(!SvOK(body)))
503 0           croak("can't send_response with an undef body");
504 383           feersum_start_response(aTHX_ c, message, headers, 0);
505 380           RETVAL = feersum_write_whole_body(aTHX_ c, body);
506             OUTPUT:
507             RETVAL
508              
509             SV*
510             _continue_streaming_psgi (struct feer_conn *c, SV *psgi_response)
511             PROTOTYPE: $\@
512             CODE:
513             {
514             AV *av;
515 7           int len = 0;
516              
517 7 50         if (IsArrayRef(psgi_response)) {
    50          
518 7           av = (AV*)SvRV(psgi_response);
519 7           len = av_len(av) + 1;
520             }
521              
522 7 100         if (len == 3) {
523             // 0 is "don't recurse" (i.e. don't allow another code-ref)
524 3           feersum_handle_psgi_response(aTHX_ c, psgi_response, 0);
525 3           RETVAL = &PL_sv_undef;
526             }
527 4 50         else if (len == 2) {
528 4           SV *message = *(av_fetch(av,0,0));
529 4           SV *headers = *(av_fetch(av,1,0));
530 4 50         if (unlikely(!IsArrayRef(headers)))
    50          
531 0           croak("PSGI headers must be an array ref");
532 4           feersum_start_response(aTHX_ c, message, (AV*)SvRV(headers), 1);
533 4           RETVAL = new_feer_conn_handle(aTHX_ c, 1); // RETVAL gets mortalized
534             }
535             else {
536 0           croak("PSGI response starter expects a 2 or 3 element array-ref");
537             }
538             }
539             OUTPUT:
540             RETVAL
541              
542             void
543             force_http10 (struct feer_conn *c)
544             PROTOTYPE: $
545             ALIAS:
546             force_http11 = 1
547             PPCODE:
548 11           c->is_http11 = ix;
549              
550             SV *
551             env (struct feer_conn *c)
552             PROTOTYPE: $
553             CODE:
554 204           RETVAL = newRV_noinc((SV*)feersum_env(aTHX_ c));
555             OUTPUT:
556             RETVAL
557              
558             SV *
559             method (struct feer_conn *c)
560             PROTOTYPE: $
561             CODE:
562 28           struct feer_req *r = c->req;
563 28 50         if (unlikely(!r))
564 0           croak("Cannot access request method: no active request");
565             #ifdef FEERSUM_HAS_H2
566             RETVAL = feersum_env_method_h2(aTHX_ c, r);
567             #else
568 28           RETVAL = feersum_env_method(aTHX_ r);
569             #endif
570             OUTPUT:
571             RETVAL
572              
573             SV *
574             uri (struct feer_conn *c)
575             PROTOTYPE: $
576             CODE:
577 2           struct feer_req *r = c->req;
578 2 50         if (unlikely(!r))
579 0           croak("Cannot access request URI: no active request");
580 2           RETVAL = feersum_env_uri(aTHX_ r);
581             OUTPUT:
582             RETVAL
583              
584             SV *
585             protocol (struct feer_conn *c)
586             PROTOTYPE: $
587             CODE:
588 2           struct feer_req *r = c->req;
589 2 50         if (unlikely(!r))
590 0           croak("Cannot access request protocol: no active request");
591 2           RETVAL = SvREFCNT_inc_simple_NN(feersum_env_protocol(aTHX_ r));
592             OUTPUT:
593             RETVAL
594              
595             SV *
596             path (struct feer_conn *c)
597             PROTOTYPE: $
598             CODE:
599 27           struct feer_req *r = c->req;
600 27 50         if (unlikely(!r))
601 0           croak("Cannot access request path: no active request");
602 27           RETVAL = SvREFCNT_inc_simple_NN(feersum_env_path(aTHX_ r));
603             OUTPUT:
604             RETVAL
605              
606             SV *
607             query (struct feer_conn *c)
608             PROTOTYPE: $
609             CODE:
610 14           struct feer_req *r = c->req;
611 14 50         if (unlikely(!r))
612 0           croak("Cannot access request query: no active request");
613 14           RETVAL = SvREFCNT_inc_simple_NN(feersum_env_query(aTHX_ r));
614             OUTPUT:
615             RETVAL
616              
617             SV *
618             remote_address (struct feer_conn *c)
619             PROTOTYPE: $
620             CODE:
621 14           RETVAL = SvREFCNT_inc_simple_NN(feersum_env_addr(aTHX_ c));
622             OUTPUT:
623             RETVAL
624              
625             SV *
626             remote_port (struct feer_conn *c)
627             PROTOTYPE: $
628             CODE:
629 3           RETVAL = SvREFCNT_inc_simple_NN(feersum_env_port(aTHX_ c));
630             OUTPUT:
631             RETVAL
632              
633             SV *
634             proxy_tlvs (struct feer_conn *c)
635             PROTOTYPE: $
636             CODE:
637             // Returns PROXY protocol v2 TLVs hashref (native interface only)
638             // Keys are TLV type numbers as strings, values are raw TLV data
639 3 100         RETVAL = c->proxy_tlvs ? SvREFCNT_inc(c->proxy_tlvs) : &PL_sv_undef;
640             OUTPUT:
641             RETVAL
642              
643             SV *
644             trailers (struct feer_conn *c)
645             PROTOTYPE: $
646             CODE:
647 0 0         RETVAL = c->trailers ? newRV_inc((SV*)c->trailers) : &PL_sv_undef;
648             OUTPUT:
649             RETVAL
650              
651             SV *
652             client_address (struct feer_conn *c)
653             PROTOTYPE: $
654             CODE:
655             {
656 8           SV *fwd = NULL;
657 8 100         if (c->cached_use_reverse_proxy && c->req)
    50          
658 5           fwd = extract_forwarded_addr(aTHX_ c->req);
659 8 100         RETVAL = fwd ? fwd : SvREFCNT_inc_simple_NN(feersum_env_addr(aTHX_ c));
660             }
661             OUTPUT:
662             RETVAL
663              
664             SV *
665             url_scheme (struct feer_conn *c)
666             PROTOTYPE: $
667             CODE:
668             {
669 11           RETVAL = feer_determine_url_scheme(aTHX_ c);
670 11 100         if (!RETVAL) RETVAL = newSVpvs("http");
671             }
672             OUTPUT:
673             RETVAL
674              
675             ssize_t
676             content_length (struct feer_conn *c)
677             PROTOTYPE: $
678             CODE:
679 18           RETVAL = feersum_env_content_length(aTHX_ c);
680             OUTPUT:
681             RETVAL
682              
683             SV *
684             input (struct feer_conn *c)
685             PROTOTYPE: $
686             CODE:
687 11 50         if (likely(c->expected_cl > 0)) {
688 11           RETVAL = new_feer_conn_handle(aTHX_ c, 0);
689             } else {
690 0           RETVAL = &PL_sv_undef;
691             }
692             OUTPUT:
693             RETVAL
694              
695             SV *
696             headers (struct feer_conn *c, int norm = 0)
697             PROTOTYPE: $;$
698             CODE:
699 7           struct feer_req *r = c->req;
700 7 50         if (unlikely(!r))
701 0           croak("Cannot access request headers: no active request");
702 7           RETVAL = newRV_noinc((SV*)feersum_env_headers(aTHX_ r, norm));
703             OUTPUT:
704             RETVAL
705              
706             SV *
707             header (struct feer_conn *c, SV *name)
708             PROTOTYPE: $$
709             CODE:
710 10           struct feer_req *r = c->req;
711 10 50         if (unlikely(!r))
712 0           croak("Cannot access request header: no active request");
713 10           RETVAL = feersum_env_header(aTHX_ r, name);
714             OUTPUT:
715             RETVAL
716              
717             int
718             fileno (struct feer_conn *c)
719             CODE:
720 10 100         RETVAL = c->fd;
721             OUTPUT:
722             RETVAL
723              
724             SV *
725             io (struct feer_conn *c)
726             CODE:
727 13           RETVAL = feersum_env_io(aTHX_ c);
728             OUTPUT:
729             RETVAL
730              
731             void
732             return_from_io (struct feer_conn *c, SV *io_sv)
733             PROTOTYPE: $$
734             PPCODE:
735             {
736 3           SSize_t cnt = feersum_return_from_io(aTHX_ c, io_sv, "return_from_io");
737 2 50         mXPUSHi(cnt);
738             }
739              
740             bool
741             is_keepalive (struct feer_conn *c)
742             CODE:
743 4 100         RETVAL = c->is_keepalive;
744             OUTPUT:
745             RETVAL
746              
747             SV*
748             response_guard (struct feer_conn *c, ...)
749             PROTOTYPE: $;$
750             CODE:
751 3 100         RETVAL = feersum_conn_guard(aTHX_ c, (items == 2) ? ST(1) : NULL);
752             OUTPUT:
753             RETVAL
754              
755             void
756             DESTROY (struct feer_conn *c)
757             PPCODE:
758             {
759             unsigned i;
760 636           int fd = c->fd;
761             trace("DESTROY connection fd=%d c=%p\n", fd, c);
762              
763 636           feer_conn_set_busy(c);
764              
765             if (FEERSUM_CONN_FREE_ENABLED()) {
766             FEERSUM_CONN_FREE(fd);
767             }
768              
769             // During global destruction, SV arena is being torn down and refcounts
770             // are unreliable. Only close the fd; all memory is reclaimed at exit.
771 636 100         if (unlikely(PL_phase == PERL_PHASE_DESTRUCT)) {
772 11           safe_close_conn(c, "close at destruction");
773 11           return;
774             }
775              
776             // Stop any active watchers/timers to prevent them from firing on a freed object.
777             // We don't decrement refcount here because DESTROY is already cleaning up.
778 625 50         if (ev_is_active(&c->read_ev_io)) {
779 0           ev_io_stop(feersum_ev_loop, &c->read_ev_io);
780             }
781 625 50         if (ev_is_active(&c->write_ev_io)) {
782 0           ev_io_stop(feersum_ev_loop, &c->write_ev_io);
783             }
784 625 50         if (ev_is_active(&c->read_ev_timer)) {
785 0           ev_timer_stop(feersum_ev_loop, &c->read_ev_timer);
786             }
787 625 50         if (ev_is_active(&c->header_ev_timer)) {
788 0           ev_timer_stop(feersum_ev_loop, &c->header_ev_timer);
789             }
790 625 50         if (ev_is_active(&c->write_ev_timer)) {
791 0           ev_timer_stop(feersum_ev_loop, &c->write_ev_timer);
792             }
793              
794 625 100         if (likely(c->rbuf)) SvREFCNT_dec(c->rbuf);
795 625 50         if (c->trailers) SvREFCNT_dec((SV*)c->trailers);
796 625 100         if (c->proxy_tlvs) SvREFCNT_dec(c->proxy_tlvs);
797              
798 625 50         if (c->wbuf_rinq) {
799             struct iomatrix *m;
800 0 0         while ((m = (struct iomatrix *)rinq_shift(&c->wbuf_rinq)) != NULL) {
801 0 0         for (i=0; i < m->count; i++) {
802 0 0         if (m->sv[i]) SvREFCNT_dec(m->sv[i]);
803             }
804 0 0         IOMATRIX_FREE(m);
805             }
806             }
807              
808 625           free_request(c);
809             #ifdef FEERSUM_HAS_H2
810             if (c->h2_session)
811             feer_h2_free_session(c);
812             #endif
813             #ifdef FEERSUM_HAS_TLS
814             #ifdef FEERSUM_HAS_H2
815             if (!c->is_h2_stream)
816             #endif
817 625           feer_tls_free_conn(c);
818             #endif
819 625 100         if (c->remote_addr) SvREFCNT_dec(c->remote_addr);
820 625 100         if (c->remote_port) SvREFCNT_dec(c->remote_port);
821              
822 625           safe_close_conn(c, "close at destruction");
823              
824 625 50         if (c->poll_write_cb) SvREFCNT_dec(c->poll_write_cb);
825 625 50         if (c->poll_read_cb) SvREFCNT_dec(c->poll_read_cb);
826              
827 625 100         if (c->ext_guard) SvREFCNT_dec(c->ext_guard);
828              
829             {
830 625           struct feer_server *server = c->server;
831 625           server->active_conns--;
832 625           SvREFCNT_dec(server->self); // release server ref held since new_feer_conn
833              
834 625 100         if (unlikely(server->shutting_down && server->active_conns <= 0)) {
    100          
835 1           ev_idle_stop(feersum_ev_loop, &server->ei);
836 1           ev_prepare_stop(feersum_ev_loop, &server->ep);
837 1           ev_check_stop(feersum_ev_loop, &server->ec);
838              
839             trace3("... was last conn, going to try shutdown\n");
840 1 50         if (server->shutdown_cb_cv)
841 1           invoke_shutdown_cb(aTHX_ server);
842             }
843             }
844             }
845