File Coverage

Feersum.xs
Criterion Covered Total %
statement 317 359 88.3
branch 179 248 72.1
condition n/a
subroutine n/a
pod n/a
total 496 607 81.7


line stmt bran cond sub pod time code
1             #include "EVAPI.h"
2              
3             #include "feersum_core.h"
4             #include "picohttpparser-git/picohttpparser.c"
5              
6             #include "rinq.c"
7              
8             #include "feersum_core.c.inc"
9             #include "feersum_utils.c.inc"
10             #include "feersum_h1.c.inc"
11             #include "feersum_psgi.c.inc"
12              
13             #ifdef FEERSUM_HAS_TLS
14             #include "feersum_tls.c.inc"
15             #endif
16             #ifdef FEERSUM_HAS_H2
17             #include "feersum_h2.c.inc"
18             #endif
19              
20             MODULE = Feersum PACKAGE = Feersum
21              
22             PROTOTYPES: ENABLE
23              
24             SV *
25             _xs_new_server(SV *classname)
26             CODE:
27             {
28             PERL_UNUSED_VAR(classname);
29 72           struct feer_server *s = new_feer_server(aTHX);
30 72           RETVAL = feer_server_2sv(s);
31             }
32             OUTPUT:
33             RETVAL
34              
35             SV *
36             _xs_default_server(SV *classname)
37             CODE:
38             {
39             PERL_UNUSED_VAR(classname);
40 123           RETVAL = feer_server_2sv(default_server);
41             }
42             OUTPUT:
43             RETVAL
44              
45             void
46             set_server_name_and_port(struct feer_server *server, SV *name, SV *port)
47             PPCODE:
48             {
49 216           struct feer_listen *lsnr = &server->listeners[server->n_listeners > 0 ? server->n_listeners - 1 : 0];
50 216 100         if (lsnr->server_name)
51 2           SvREFCNT_dec(lsnr->server_name);
52 216           lsnr->server_name = newSVsv(name);
53 216           SvREADONLY_on(lsnr->server_name);
54              
55 216 100         if (lsnr->server_port)
56 2           SvREFCNT_dec(lsnr->server_port);
57 216           lsnr->server_port = newSVsv(port);
58 216           SvREADONLY_on(lsnr->server_port);
59             }
60              
61             void
62             accept_on_fd(struct feer_server *server, int fd)
63             PPCODE:
64             {
65             struct sockaddr_storage addr;
66 215           socklen_t addr_len = sizeof(addr);
67             struct feer_listen *lsnr;
68              
69             // Determine which listener slot to use
70 215 100         if (server->n_listeners == 0) {
71 163           lsnr = &server->listeners[0];
72 163           server->n_listeners = 1;
73             } else {
74             // Look for an unused slot (fd == -1) to reuse
75             int j;
76 52           lsnr = NULL;
77 194 100         for (j = 0; j < server->n_listeners; j++) {
78 142 50         if (server->listeners[j].fd == -1) {
79 0           lsnr = &server->listeners[j];
80             #ifdef FEERSUM_HAS_TLS
81 0 0         if (lsnr->tls_ctx_ref) {
82 0           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
83 0           lsnr->tls_ctx_ref = NULL;
84             }
85             #endif
86 0           break;
87             }
88             }
89 52 50         if (!lsnr) {
90 52 50         if (server->n_listeners < FEER_MAX_LISTENERS) {
91 52           lsnr = &server->listeners[server->n_listeners];
92             // Initialize new listener slot
93 52           Zero(lsnr, 1, struct feer_listen);
94 52           lsnr->server = server;
95 52           lsnr->fd = -1;
96 52           lsnr->is_tcp = 1;
97             #ifdef __linux__
98 52           lsnr->epoll_fd = -1;
99             #endif
100 52           server->n_listeners++;
101             } else {
102 0           croak("Too many listeners (max %d)", FEER_MAX_LISTENERS);
103             }
104             }
105             }
106              
107             // Zero addr to ensure safe defaults if getsockname fails
108 215           Zero(&addr, 1, struct sockaddr_storage);
109 215 50         if (getsockname(fd, (struct sockaddr*)&addr, &addr_len) == -1) {
110             // Log error but continue with safe default (AF_INET assumed)
111             // This allows the server to function even if getsockname fails
112 0           warn("getsockname failed: %s (assuming TCP socket)", strerror(errno));
113 0           addr.ss_family = AF_INET;
114             }
115 215           switch (addr.ss_family) {
116 215           case AF_INET:
117             case AF_INET6:
118 215           lsnr->is_tcp = 1;
119             #ifdef TCP_DEFER_ACCEPT
120             trace("going to defer accept on %d\n",fd);
121 215 50         if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &(int){1}, sizeof(int)) < 0)
122 0           trouble("setsockopt TCP_DEFER_ACCEPT fd=%d: %s\n", fd, strerror(errno));
123             #endif
124 215           break;
125             #ifdef AF_UNIX
126 0           case AF_UNIX:
127 0           lsnr->is_tcp = 0;
128 0           break;
129             #endif
130             }
131              
132             trace("going to accept on %d\n",fd);
133 215           feersum_ev_loop = EV_DEFAULT;
134 215           lsnr->fd = fd;
135              
136 215           signal(SIGPIPE, SIG_IGN);
137              
138             // Only init per-server watchers once (on first listener)
139 215 100         if (!server->watchers_initialized) {
140 163           server->watchers_initialized = true;
141              
142 163           ev_prepare_init(&server->ep, prepare_cb);
143 163           server->ep.data = (void *)server;
144 163           ev_prepare_start(feersum_ev_loop, &server->ep);
145              
146 163           ev_check_init(&server->ec, check_cb);
147 163           server->ec.data = (void *)server;
148 163           ev_check_start(feersum_ev_loop, &server->ec);
149              
150 163           ev_idle_init(&server->ei, idle_cb);
151 163           server->ei.data = (void *)server;
152              
153 163           date_timer_refs++;
154 52 100         } else if (!ev_is_active(&server->ep)) {
155             // Re-arm prepare watcher for runtime listener addition
156 30           ev_prepare_start(feersum_ev_loop, &server->ep);
157             }
158              
159             // Initialize date header and start periodic timer (1 second interval)
160             // Shared across all servers - only start once
161 215 100         if (!ev_is_active(&date_timer)) {
162 124           date_timer_cb(feersum_ev_loop, &date_timer, 0); // initial update
163 124           ev_timer_init(&date_timer, date_timer_cb, 1.0, 1.0);
164 124           ev_timer_start(feersum_ev_loop, &date_timer);
165             }
166              
167 215           setup_accept_watcher(lsnr, fd);
168             }
169              
170             void
171             unlisten (struct feer_server *server)
172             PPCODE:
173             {
174             int i;
175             trace("stopping accept\n");
176 16           ev_prepare_stop(feersum_ev_loop, &server->ep);
177 16           ev_check_stop(feersum_ev_loop, &server->ec);
178 16           ev_idle_stop(feersum_ev_loop, &server->ei);
179 32 100         for (i = 0; i < server->n_listeners; i++) {
180 16           struct feer_listen *lsnr = &server->listeners[i];
181 16           ev_io_stop(feersum_ev_loop, &lsnr->accept_w);
182 16           ev_timer_stop(feersum_ev_loop, &lsnr->emfile_w);
183             #ifdef __linux__
184 16 100         if (lsnr->epoll_fd >= 0) {
185 2 50         if (unlikely(close(lsnr->epoll_fd) < 0))
186 0           trouble("close(epoll_fd) fd=%d: %s\n", lsnr->epoll_fd, strerror(errno));
187 2           lsnr->epoll_fd = -1;
188             }
189             #endif
190 16           lsnr->fd = -1;
191 16           lsnr->paused = 0;
192             #ifdef FEERSUM_HAS_TLS
193 16 100         if (lsnr->tls_ctx_ref) {
194 2           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
195 2           lsnr->tls_ctx_ref = NULL;
196             }
197             #endif
198 16 100         if (lsnr->server_name) {
199 15           SvREFCNT_dec(lsnr->server_name);
200 15           lsnr->server_name = NULL;
201             }
202 16 100         if (lsnr->server_port) {
203 15           SvREFCNT_dec(lsnr->server_port);
204 15           lsnr->server_port = NULL;
205             }
206             }
207 16           server->n_listeners = 0;
208 16 100         if (server->watchers_initialized) {
209 12           server->watchers_initialized = false;
210 12 50         if (--date_timer_refs <= 0) {
211 12           ev_timer_stop(feersum_ev_loop, &date_timer);
212 12           date_timer_refs = 0;
213             }
214             }
215             }
216              
217             void
218             pause_accept (struct feer_server *server)
219             PPCODE:
220             {
221             int i;
222 2 50         if (server->shutting_down) {
223             trace("cannot pause during shutdown\n");
224 0           XSRETURN_NO;
225             }
226 2           int paused_any = 0;
227 4 100         for (i = 0; i < server->n_listeners; i++) {
228 2           struct feer_listen *lsnr = &server->listeners[i];
229 2 100         if (!lsnr->paused && ev_is_active(&lsnr->accept_w)) {
    50          
230             trace("pausing accept on listener %d\n", i);
231 1           ev_io_stop(feersum_ev_loop, &lsnr->accept_w);
232 1           lsnr->paused = 1;
233 1           paused_any = 1;
234             }
235             }
236 2 100         if (paused_any)
237 1           XSRETURN_YES;
238             else
239 1           XSRETURN_NO;
240             }
241              
242             void
243             resume_accept (struct feer_server *server)
244             PPCODE:
245             {
246             int i;
247 2 50         if (server->shutting_down) {
248             trace("cannot resume during shutdown\n");
249 0           XSRETURN_NO;
250             }
251 2           int resumed_any = 0;
252 4 100         for (i = 0; i < server->n_listeners; i++) {
253 2           struct feer_listen *lsnr = &server->listeners[i];
254 2 100         if (lsnr->paused) {
255             trace("resuming accept on listener %d\n", i);
256 1           ev_io_start(feersum_ev_loop, &lsnr->accept_w);
257 1           lsnr->paused = 0;
258 1           resumed_any = 1;
259             }
260             }
261 2 100         if (resumed_any)
262 1           XSRETURN_YES;
263             else
264 1           XSRETURN_NO;
265             }
266              
267             bool
268             accept_is_paused (struct feer_server *server)
269             CODE:
270             {
271             int i;
272 3           RETVAL = (server->n_listeners > 0);
273 4 100         for (i = 0; i < server->n_listeners; i++) {
274 3 100         if (!server->listeners[i].paused) { RETVAL = 0; break; }
275             }
276             }
277             OUTPUT:
278             RETVAL
279              
280             void
281             request_handler(struct feer_server *server, SV *cb)
282             PROTOTYPE: $&
283             ALIAS:
284             psgi_request_handler = 1
285             PPCODE:
286             {
287 238 50         if (unlikely(!SvOK(cb) || !SvROK(cb)))
    50          
288 0           croak("can't supply an undef handler");
289 238 100         if (server->request_cb_cv)
290 81           SvREFCNT_dec(server->request_cb_cv);
291 238           server->request_cb_cv = newSVsv(cb);
292 238           server->request_cb_is_psgi = ix;
293             trace("assigned %s request handler %p\n",
294             server->request_cb_is_psgi?"PSGI":"Feersum", server->request_cb_cv);
295             }
296              
297             void
298             graceful_shutdown (struct feer_server *server, SV *cb)
299             PROTOTYPE: $&
300             PPCODE:
301             {
302             int i;
303 9 50         if (!IsCodeRef(cb))
    50          
304 0           croak("must supply a code reference");
305 9 100         if (unlikely(server->shutting_down))
306 1           croak("already shutting down");
307 8           server->shutdown_cb_cv = newSVsv(cb);
308             trace("shutting down, handler=%p, active=%d\n", SvRV(cb), server->active_conns);
309              
310 8           server->shutting_down = 1;
311 16 100         for (i = 0; i < server->n_listeners; i++) {
312 8           struct feer_listen *lsnr = &server->listeners[i];
313 8           ev_io_stop(feersum_ev_loop, &lsnr->accept_w);
314 8           ev_timer_stop(feersum_ev_loop, &lsnr->emfile_w);
315             #ifdef __linux__
316 8 50         if (lsnr->epoll_fd >= 0) {
317 0 0         if (unlikely(close(lsnr->epoll_fd) < 0))
318 0           trouble("close(epoll_fd) fd=%d: %s\n", lsnr->epoll_fd, strerror(errno));
319 0           lsnr->epoll_fd = -1;
320             // In epoll_exclusive mode, accept_w.fd is the epoll fd (now closed)
321             // We still need to close the actual listen socket
322 0 0         if (lsnr->fd >= 0) {
323 0 0         if (unlikely(close(lsnr->fd) < 0))
324 0           trouble("close(listen fd) fd=%d: %s\n", lsnr->fd, strerror(errno));
325 0           lsnr->fd = -1;
326             }
327             } else
328             #endif
329             {
330 8 50         if (lsnr->accept_w.fd >= 0) {
331 8 50         if (unlikely(close(lsnr->accept_w.fd) < 0))
332 0           trouble("close(accept_w.fd) fd=%d: %s\n", lsnr->accept_w.fd, strerror(errno));
333 8           ev_io_set(&lsnr->accept_w, -1, EV_READ);
334 8           lsnr->fd = -1;
335             }
336             }
337             #ifdef FEERSUM_HAS_TLS
338 8 100         if (lsnr->tls_ctx_ref) {
339 2           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
340 2           lsnr->tls_ctx_ref = NULL;
341             }
342             #endif
343             }
344              
345             /* Close idle keepalive connections — they won't get new requests */
346 8 50         while (feer_server_recycle_idle_conn(server))
347             ;
348              
349 8 100         if (server->active_conns <= 0 && server->shutdown_cb_cv) {
    50          
350             trace("shutdown is immediate\n");
351 7           invoke_shutdown_cb(aTHX_ server);
352             }
353             }
354              
355             double
356             read_timeout (struct feer_server *server, ...)
357             PROTOTYPE: $;$
358             CODE:
359             {
360 8032 100         if (items > 1) {
361 6020           double val = SvNV(ST(1));
362 6020 100         if (!(val > 0.0))
363 3           croak("must set a positive (non-zero) value for the timeout");
364             trace("set read_timeout %f\n", val);
365 6017           server->read_timeout = val;
366             }
367 8029 100         RETVAL = server->read_timeout;
368             }
369             OUTPUT:
370             RETVAL
371              
372             double
373             header_timeout (struct feer_server *server, ...)
374             PROTOTYPE: $;$
375             CODE:
376             {
377 19 100         if (items > 1) {
378 16           double val = SvNV(ST(1));
379 16 100         if (val < 0.0)
380 1           croak("header_timeout must be non-negative (0 to disable)");
381             trace("set header_timeout %f\n", val);
382 15           server->header_timeout = val;
383             }
384 18 50         RETVAL = server->header_timeout;
385             }
386             OUTPUT:
387             RETVAL
388              
389             double
390             write_timeout (struct feer_server *server, ...)
391             PROTOTYPE: $;$
392             CODE:
393             {
394 7 100         if (items > 1) {
395 4           double val = SvNV(ST(1));
396 4 100         if (val < 0.0)
397 1           croak("write_timeout must be non-negative (0 to disable)");
398             trace("set write_timeout %f\n", val);
399 3           server->write_timeout = val;
400             }
401 6 50         RETVAL = server->write_timeout;
402             }
403             OUTPUT:
404             RETVAL
405              
406             void
407             set_keepalive (struct feer_server *server, SV *set)
408             PPCODE:
409             {
410             trace("set keepalive %d\n", SvTRUE(set));
411 76           server->is_keepalive = SvTRUE(set);
412             }
413              
414             void
415             set_reverse_proxy (struct feer_server *server, SV *set)
416             PPCODE:
417             {
418             trace("set reverse_proxy %d\n", SvTRUE(set));
419 13           server->use_reverse_proxy = SvTRUE(set);
420             }
421              
422             int
423             get_reverse_proxy (struct feer_server *server)
424             CODE:
425             {
426 4 50         RETVAL = server->use_reverse_proxy;
427             }
428             OUTPUT:
429             RETVAL
430              
431             void
432             set_psgix_io (struct feer_server *server, SV *set)
433             PPCODE:
434             {
435 0           server->psgix_io = SvTRUE(set);
436             trace("set psgix_io %d\n", server->psgix_io);
437             }
438              
439             int
440             get_psgix_io (struct feer_server *server)
441             CODE:
442             {
443 0 0         RETVAL = server->psgix_io;
444             }
445             OUTPUT:
446             RETVAL
447              
448             void
449             set_proxy_protocol (struct feer_server *server, SV *set)
450             PPCODE:
451             {
452             trace("set proxy_protocol %d\n", SvTRUE(set));
453 50           server->use_proxy_protocol = SvTRUE(set);
454             }
455              
456             int
457             get_proxy_protocol (struct feer_server *server)
458             CODE:
459             {
460 4 50         RETVAL = server->use_proxy_protocol;
461             }
462             OUTPUT:
463             RETVAL
464              
465             void
466             set_epoll_exclusive (struct feer_server *server, SV *set)
467             PPCODE:
468             {
469             #if defined(__linux__) && defined(EPOLLEXCLUSIVE)
470             trace("set epoll_exclusive %d (native mode)\n", SvTRUE(set));
471 14           server->use_epoll_exclusive = SvTRUE(set) ? 1 : 0;
472             #else
473             PERL_UNUSED_VAR(server);
474             if (SvTRUE(set))
475             warn("EPOLLEXCLUSIVE is not available (requires Linux 4.5+)");
476             #endif
477             }
478              
479             int
480             get_epoll_exclusive (struct feer_server *server)
481             CODE:
482             {
483             #if defined(__linux__) && defined(EPOLLEXCLUSIVE)
484 10 50         RETVAL = server->use_epoll_exclusive ? 1 : 0;
485             #else
486             PERL_UNUSED_VAR(server);
487             RETVAL = 0;
488             #endif
489             }
490             OUTPUT:
491             RETVAL
492              
493             int
494             read_priority (struct feer_server *server, ...)
495             ALIAS:
496             write_priority = 1
497             accept_priority = 2
498             PROTOTYPE: $;$
499             CODE:
500             {
501             static const char *names[] = {"read", "write", "accept"};
502 18020           int *field = ix == 2 ? &server->accept_priority
503 30036 100         : ix == 1 ? &server->write_priority
504 12016 100         : &server->read_priority;
505 18020 100         if (items > 1) {
506 18012           int new_priority = SvIV(ST(1));
507 18012 100         if (new_priority < EV_MINPRI) new_priority = EV_MINPRI;
508 18012 100         if (new_priority > EV_MAXPRI) new_priority = EV_MAXPRI;
509             trace("set %s_priority %d\n", names[ix], new_priority);
510 18012           *field = new_priority;
511             }
512 18020 100         RETVAL = *field;
513             }
514             OUTPUT:
515             RETVAL
516              
517             int
518             max_accept_per_loop (struct feer_server *server, ...)
519             PROTOTYPE: $;$
520             CODE:
521             {
522 14 100         if (items > 1) {
523 6           int new_max = SvIV(ST(1));
524 6 100         if (new_max < 1) new_max = 1;
525             trace("set max_accept_per_loop %d\n", new_max);
526 6           server->max_accept_per_loop = new_max;
527             }
528 14 50         RETVAL = server->max_accept_per_loop;
529             }
530             OUTPUT:
531             RETVAL
532              
533             int
534             active_conns (struct feer_server *server)
535             CODE:
536 11 100         RETVAL = server->active_conns;
537             OUTPUT:
538             RETVAL
539              
540             int
541             max_connections (struct feer_server *server, ...)
542             PROTOTYPE: $;$
543             CODE:
544             {
545 19 100         if (items > 1) {
546 12           int new_max = SvIV(ST(1));
547 12 50         if (new_max < 0) new_max = 0; // 0 means unlimited
548             trace("set max_connections %d\n", new_max);
549 12           server->max_connections = new_max;
550             }
551 19 50         RETVAL = server->max_connections;
552             }
553             OUTPUT:
554             RETVAL
555              
556             size_t
557             max_read_buf (struct feer_server *server, ...)
558             PROTOTYPE: $;$
559             CODE:
560             {
561 0 0         if (items > 1) {
562 0           size_t new_max = SvUV(ST(1));
563 0 0         if (new_max == 0) new_max = MAX_READ_BUF;
564 0           server->max_read_buf = new_max;
565             }
566 0 0         RETVAL = server->max_read_buf;
567             }
568             OUTPUT:
569             RETVAL
570              
571             size_t
572             max_body_len (struct feer_server *server, ...)
573             PROTOTYPE: $;$
574             CODE:
575             {
576 2 50         if (items > 1) {
577 2           size_t new_max = SvUV(ST(1));
578 2 100         if (new_max == 0) new_max = MAX_BODY_LEN;
579 2           server->max_body_len = new_max;
580             }
581 2 50         RETVAL = server->max_body_len;
582             }
583             OUTPUT:
584             RETVAL
585              
586             size_t
587             max_uri_len (struct feer_server *server, ...)
588             PROTOTYPE: $;$
589             CODE:
590             {
591 7 100         if (items > 1) {
592 4           size_t new_max = SvUV(ST(1));
593 4 100         if (new_max == 0) new_max = MAX_URI_LEN;
594 4           server->max_uri_len = new_max;
595             }
596 7 50         RETVAL = server->max_uri_len;
597             }
598             OUTPUT:
599             RETVAL
600              
601             size_t
602             wbuf_low_water (struct feer_server *server, ...)
603             PROTOTYPE: $;$
604             CODE:
605             {
606 10 100         if (items > 1) {
607 7           SV *val = ST(1);
608 7 100         if (SvNV(val) < 0.0)
609 1           croak("wbuf_low_water must be non-negative");
610 6           server->wbuf_low_water = SvUV(val);
611             }
612 9 50         RETVAL = server->wbuf_low_water;
613             }
614             OUTPUT:
615             RETVAL
616              
617             void
618             set_multiprocess (struct feer_server *server, SV *set)
619             PPCODE:
620             {
621 3           server->multiprocess = SvTRUE(set);
622             }
623              
624             int
625             max_h2_concurrent_streams (struct feer_server *server, ...)
626             PROTOTYPE: $;$
627             CODE:
628             {
629             #ifdef FEERSUM_HAS_H2
630             if (items > 1) {
631             int n = SvIV(ST(1));
632             if (n < 1) n = 1;
633             if (n > 10000) n = 10000;
634             server->max_h2_concurrent_streams = n;
635             }
636             RETVAL = server->max_h2_concurrent_streams;
637             #else
638             PERL_UNUSED_VAR(server);
639 0 0         if (items > 1)
640 0           warn("H2 not compiled in, max_h2_concurrent_streams ignored");
641 0 0         RETVAL = 0;
642             #endif
643             }
644             OUTPUT:
645             RETVAL
646              
647             UV
648             total_requests (struct feer_server *server)
649             CODE:
650 3 50         RETVAL = server->total_requests;
651             OUTPUT:
652             RETVAL
653              
654             unsigned int
655             max_connection_reqs (struct feer_server *server, ...)
656             PROTOTYPE: $;$
657             PREINIT:
658 1 50         IV new_max_connection_reqs = 0;
659             CODE:
660             {
661 1 50         if (items > 1) {
662 1           new_max_connection_reqs = SvIV(ST(1));
663 1 50         if (new_max_connection_reqs < 0) {
664 0           croak("must set a non-negative value (0 for unlimited)");
665             }
666             trace("set max requests per connection %u\n", (unsigned int)new_max_connection_reqs);
667 1           server->max_connection_reqs = (unsigned int)new_max_connection_reqs;
668             }
669 1 50         RETVAL = server->max_connection_reqs;
670             }
671             OUTPUT:
672             RETVAL
673              
674             void
675             _xs_destroy (struct feer_server *server)
676             PPCODE:
677             {
678             int i;
679             trace3("DESTROY server\n");
680             /* Stop accept watchers to prevent use-after-free if server is GC'd
681             * without unlisten() or graceful_shutdown() being called first. */
682 208           ev_prepare_stop(feersum_ev_loop, &server->ep);
683 208           ev_check_stop(feersum_ev_loop, &server->ec);
684 208           ev_idle_stop(feersum_ev_loop, &server->ei);
685 208 100         if (server->request_cb_cv)
686 157           SvREFCNT_dec(server->request_cb_cv);
687 208 50         if (server->shutdown_cb_cv)
688 0           SvREFCNT_dec(server->shutdown_cb_cv);
689 407 100         for (i = 0; i < server->n_listeners; i++) {
690 199           struct feer_listen *lsnr = &server->listeners[i];
691 199           ev_io_stop(feersum_ev_loop, &lsnr->accept_w);
692 199           ev_timer_stop(feersum_ev_loop, &lsnr->emfile_w);
693             #ifdef __linux__
694 199 50         if (lsnr->epoll_fd >= 0) {
695 0           close(lsnr->epoll_fd);
696 0           lsnr->epoll_fd = -1;
697             }
698             #endif
699 199 50         if (lsnr->server_name)
700 199           SvREFCNT_dec(lsnr->server_name);
701 199 50         if (lsnr->server_port)
702 199           SvREFCNT_dec(lsnr->server_port);
703             #ifdef FEERSUM_HAS_TLS
704 199 100         if (lsnr->tls_ctx_ref) {
705 77           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
706 77           lsnr->tls_ctx_ref = NULL;
707             }
708             #endif
709             }
710 208 100         if (server->watchers_initialized && --date_timer_refs <= 0) {
    100          
711 112           ev_timer_stop(feersum_ev_loop, &date_timer);
712 112           date_timer_refs = 0;
713             }
714             }
715              
716             void
717             set_tls (struct feer_server *server, ...)
718             PPCODE:
719             {
720             #ifdef FEERSUM_HAS_TLS
721 101           const char *cert_file = NULL;
722 101           const char *key_file = NULL;
723 101           int listener_idx = -1; /* -1 means last-added listener (default) */
724 101           int h2 = 0;
725             int i;
726              
727 101 100         if (items < 3 || (items - 1) % 2 != 0)
    50          
728 1           croak("set_tls requires key => value pairs (cert_file => $path, key_file => $path)");
729              
730 300 100         for (i = 1; i < items; i += 2) {
731 202           const char *key = SvPV_nolen(ST(i));
732 202           SV *val = ST(i + 1);
733 202 100         if (strcmp(key, "cert_file") == 0)
734 97           cert_file = SvPV_nolen(val);
735 105 100         else if (strcmp(key, "key_file") == 0)
736 97           key_file = SvPV_nolen(val);
737 8 100         else if (strcmp(key, "listener") == 0)
738 6           listener_idx = SvIV(val);
739 2 50         else if (strcmp(key, "h2") == 0)
740 0           h2 = SvTRUE(val) ? 1 : 0;
741             else
742 2           croak("set_tls: unknown option '%s'", key);
743             }
744              
745 98 100         if (!cert_file) croak("set_tls: cert_file is required");
746 95 100         if (!key_file) croak("set_tls: key_file is required");
747              
748 92 100         if (server->n_listeners == 0)
749 3           croak("set_tls: no listeners configured (call use_socket/accept_on_fd first)");
750              
751             /* Resolve listener index */
752 89 50         if (listener_idx < -1)
753 0           croak("set_tls: listener index %d out of range (0..%d or -1)",
754             listener_idx, server->n_listeners - 1);
755 89 100         if (listener_idx < 0)
756 83           listener_idx = server->n_listeners - 1;
757 89 100         if (listener_idx >= server->n_listeners)
758 2           croak("set_tls: listener index %d out of range (0..%d)",
759             listener_idx, server->n_listeners - 1);
760              
761 87           struct feer_listen *lsnr = &server->listeners[listener_idx];
762              
763 87           ptls_context_t *new_ctx = feer_tls_create_context(aTHX_ cert_file, key_file, h2);
764 87 100         if (!new_ctx)
765 5           croak("set_tls: failed to create TLS context");
766 82 100         if (lsnr->tls_ctx_ref)
767 1           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
768 82           lsnr->tls_ctx_ref = feer_tls_ctx_ref_new(new_ctx);
769              
770             trace("TLS enabled on listener %d (h2=%d)\n", listener_idx, h2);
771             #else
772             PERL_UNUSED_VAR(server);
773             croak("set_tls: Feersum was not compiled with TLS support (need picotls submodule + OpenSSL; see Alien::OpenSSL)");
774             #endif
775             }
776              
777             int
778             has_tls (struct feer_server *server)
779             CODE:
780             {
781             PERL_UNUSED_VAR(server);
782             #ifdef FEERSUM_HAS_TLS
783 55 50         RETVAL = 1;
784             #else
785             RETVAL = 0;
786             #endif
787             }
788             OUTPUT:
789             RETVAL
790              
791             int
792             has_h2 (struct feer_server *server)
793             CODE:
794             {
795             PERL_UNUSED_VAR(server);
796             #ifdef FEERSUM_HAS_H2
797             RETVAL = 1;
798             #else
799 15 50         RETVAL = 0;
800             #endif
801             }
802             OUTPUT:
803             RETVAL
804              
805             BOOT:
806             {
807 136           feer_stash = gv_stashpv("Feersum", 1);
808 136           feer_conn_stash = gv_stashpv("Feersum::Connection", 1);
809 136           feer_conn_writer_stash = gv_stashpv("Feersum::Connection::Writer",1);
810 136           feer_conn_reader_stash = gv_stashpv("Feersum::Connection::Reader",1);
811 136 50         I_EV_API("Feersum");
    50          
    50          
812              
813 136           const char *env_fl_max = getenv("FEERSUM_FREELIST_MAX");
814 136 50         if (env_fl_max) {
815 0           FEERSUM_FREELIST_MAX = atoi(env_fl_max);
816             }
817              
818             // Allocate default server (backed by a blessed Perl SV)
819 136           default_server = new_feer_server(aTHX);
820             // Keep an extra refcount so the default server is never GC'd
821 136           SvREFCNT_inc_void_NN(default_server->self);
822              
823 136           psgi_ver = newAV();
824 136           av_extend(psgi_ver, 1); // pre-allocate for 2 elements (psgi.version = [1, 1])
825 136           av_push(psgi_ver, newSViv(1));
826 136           av_push(psgi_ver, newSViv(1));
827 136           SvREADONLY_on((SV*)psgi_ver);
828              
829 136           psgi_serv10 = newSVpvs("HTTP/1.0");
830 136           SvREADONLY_on(psgi_serv10);
831 136           psgi_serv11 = newSVpvs("HTTP/1.1");
832 136           SvREADONLY_on(psgi_serv11);
833              
834 136           method_GET = newSVpvs("GET");
835 136           SvREADONLY_on(method_GET);
836 136           method_POST = newSVpvs("POST");
837 136           SvREADONLY_on(method_POST);
838 136           method_HEAD = newSVpvs("HEAD");
839 136           SvREADONLY_on(method_HEAD);
840 136           method_PUT = newSVpvs("PUT");
841 136           SvREADONLY_on(method_PUT);
842 136           method_PATCH = newSVpvs("PATCH");
843 136           SvREADONLY_on(method_PATCH);
844 136           method_DELETE = newSVpvs("DELETE");
845 136           SvREADONLY_on(method_DELETE);
846 136           method_OPTIONS = newSVpvs("OPTIONS");
847 136           SvREADONLY_on(method_OPTIONS);
848              
849 136           status_200 = newSVpvs("200 OK");
850 136           SvREADONLY_on(status_200);
851 136           status_201 = newSVpvs("201 Created");
852 136           SvREADONLY_on(status_201);
853 136           status_204 = newSVpvs("204 No Content");
854 136           SvREADONLY_on(status_204);
855 136           status_301 = newSVpvs("301 Moved Permanently");
856 136           SvREADONLY_on(status_301);
857 136           status_302 = newSVpvs("302 Found");
858 136           SvREADONLY_on(status_302);
859 136           status_304 = newSVpvs("304 Not Modified");
860 136           SvREADONLY_on(status_304);
861 136           status_400 = newSVpvs("400 Bad Request");
862 136           SvREADONLY_on(status_400);
863 136           status_404 = newSVpvs("404 Not Found");
864 136           SvREADONLY_on(status_404);
865 136           status_500 = newSVpvs("500 Internal Server Error");
866 136           SvREADONLY_on(status_500);
867              
868 136           empty_query_sv = newSVpvs("");
869 136           SvREADONLY_on(empty_query_sv);
870              
871 136           Zero(&psgix_io_vtbl, 1, MGVTBL);
872 136           psgix_io_vtbl.svt_get = psgix_io_svt_get;
873 136           newCONSTSUB(feer_stash, "HEADER_NORM_SKIP", newSViv(HEADER_NORM_SKIP));
874 136           newCONSTSUB(feer_stash, "HEADER_NORM_UPCASE", newSViv(HEADER_NORM_UPCASE));
875 136           newCONSTSUB(feer_stash, "HEADER_NORM_LOCASE", newSViv(HEADER_NORM_LOCASE));
876 136           newCONSTSUB(feer_stash, "HEADER_NORM_UPCASE_DASH", newSViv(HEADER_NORM_UPCASE_DASH));
877 136           newCONSTSUB(feer_stash, "HEADER_NORM_LOCASE_DASH", newSViv(HEADER_NORM_LOCASE_DASH));
878              
879             trace3("Feersum booted, iomatrix %lu, FEERSUM_IOMATRIX_SIZE=%u, "
880             "feer_req %lu, feer_conn %lu\n",
881             (long unsigned int)sizeof(struct iomatrix),
882             (unsigned int)FEERSUM_IOMATRIX_SIZE,
883             (long unsigned int)sizeof(struct feer_req),
884             (long unsigned int)sizeof(struct feer_conn)
885             );
886             }
887              
888             INCLUDE: feersum_conn.xs