File Coverage

feersum_conn.xs
Criterion Covered Total %
statement 245 314 78.0
branch 199 320 62.1
condition n/a
subroutine n/a
pod n/a
total 444 634 70.0


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