File Coverage

Feersum.xs
Criterion Covered Total %
statement 316 355 89.0
branch 179 244 73.3
condition n/a
subroutine n/a
pod n/a
total 495 599 82.6


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             UV
618             total_requests (struct feer_server *server)
619             CODE:
620 3 50         RETVAL = server->total_requests;
621             OUTPUT:
622             RETVAL
623              
624             unsigned int
625             max_connection_reqs (struct feer_server *server, ...)
626             PROTOTYPE: $;$
627             PREINIT:
628 1 50         IV new_max_connection_reqs = 0;
629             CODE:
630             {
631 1 50         if (items > 1) {
632 1           new_max_connection_reqs = SvIV(ST(1));
633 1 50         if (new_max_connection_reqs < 0) {
634 0           croak("must set a non-negative value (0 for unlimited)");
635             }
636             trace("set max requests per connection %u\n", (unsigned int)new_max_connection_reqs);
637 1           server->max_connection_reqs = (unsigned int)new_max_connection_reqs;
638             }
639 1 50         RETVAL = server->max_connection_reqs;
640             }
641             OUTPUT:
642             RETVAL
643              
644             void
645             _xs_destroy (struct feer_server *server)
646             PPCODE:
647             {
648             int i;
649             trace3("DESTROY server\n");
650             /* Stop accept watchers to prevent use-after-free if server is GC'd
651             * without unlisten() or graceful_shutdown() being called first. */
652 208           ev_prepare_stop(feersum_ev_loop, &server->ep);
653 208           ev_check_stop(feersum_ev_loop, &server->ec);
654 208           ev_idle_stop(feersum_ev_loop, &server->ei);
655 208 100         if (server->request_cb_cv)
656 157           SvREFCNT_dec(server->request_cb_cv);
657 208 50         if (server->shutdown_cb_cv)
658 0           SvREFCNT_dec(server->shutdown_cb_cv);
659 407 100         for (i = 0; i < server->n_listeners; i++) {
660 199           struct feer_listen *lsnr = &server->listeners[i];
661 199           ev_io_stop(feersum_ev_loop, &lsnr->accept_w);
662 199           ev_timer_stop(feersum_ev_loop, &lsnr->emfile_w);
663             #ifdef __linux__
664 199 50         if (lsnr->epoll_fd >= 0) {
665 0           close(lsnr->epoll_fd);
666 0           lsnr->epoll_fd = -1;
667             }
668             #endif
669 199 50         if (lsnr->server_name)
670 199           SvREFCNT_dec(lsnr->server_name);
671 199 50         if (lsnr->server_port)
672 199           SvREFCNT_dec(lsnr->server_port);
673             #ifdef FEERSUM_HAS_TLS
674 199 100         if (lsnr->tls_ctx_ref) {
675 77           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
676 77           lsnr->tls_ctx_ref = NULL;
677             }
678             #endif
679             }
680 208 100         if (server->watchers_initialized && --date_timer_refs <= 0) {
    100          
681 112           ev_timer_stop(feersum_ev_loop, &date_timer);
682 112           date_timer_refs = 0;
683             }
684             }
685              
686             void
687             set_tls (struct feer_server *server, ...)
688             PPCODE:
689             {
690             #ifdef FEERSUM_HAS_TLS
691 101           const char *cert_file = NULL;
692 101           const char *key_file = NULL;
693 101           int listener_idx = -1; /* -1 means last-added listener (default) */
694 101           int h2 = 0;
695             int i;
696              
697 101 100         if (items < 3 || (items - 1) % 2 != 0)
    50          
698 1           croak("set_tls requires key => value pairs (cert_file => $path, key_file => $path)");
699              
700 300 100         for (i = 1; i < items; i += 2) {
701 202           const char *key = SvPV_nolen(ST(i));
702 202           SV *val = ST(i + 1);
703 202 100         if (strcmp(key, "cert_file") == 0)
704 97           cert_file = SvPV_nolen(val);
705 105 100         else if (strcmp(key, "key_file") == 0)
706 97           key_file = SvPV_nolen(val);
707 8 100         else if (strcmp(key, "listener") == 0)
708 6           listener_idx = SvIV(val);
709 2 50         else if (strcmp(key, "h2") == 0)
710 0           h2 = SvTRUE(val) ? 1 : 0;
711             else
712 2           croak("set_tls: unknown option '%s'", key);
713             }
714              
715 98 100         if (!cert_file) croak("set_tls: cert_file is required");
716 95 100         if (!key_file) croak("set_tls: key_file is required");
717              
718 92 100         if (server->n_listeners == 0)
719 3           croak("set_tls: no listeners configured (call use_socket/accept_on_fd first)");
720              
721             /* Resolve listener index */
722 89 50         if (listener_idx < -1)
723 0           croak("set_tls: listener index %d out of range (0..%d or -1)",
724             listener_idx, server->n_listeners - 1);
725 89 100         if (listener_idx < 0)
726 83           listener_idx = server->n_listeners - 1;
727 89 100         if (listener_idx >= server->n_listeners)
728 2           croak("set_tls: listener index %d out of range (0..%d)",
729             listener_idx, server->n_listeners - 1);
730              
731 87           struct feer_listen *lsnr = &server->listeners[listener_idx];
732              
733 87           ptls_context_t *new_ctx = feer_tls_create_context(aTHX_ cert_file, key_file, h2);
734 87 100         if (!new_ctx)
735 5           croak("set_tls: failed to create TLS context");
736 82 100         if (lsnr->tls_ctx_ref)
737 1           feer_tls_ctx_ref_dec(lsnr->tls_ctx_ref);
738 82           lsnr->tls_ctx_ref = feer_tls_ctx_ref_new(new_ctx);
739              
740             trace("TLS enabled on listener %d (h2=%d)\n", listener_idx, h2);
741             #else
742             PERL_UNUSED_VAR(server);
743             croak("set_tls: Feersum was not compiled with TLS support (need picotls submodule + OpenSSL; see Alien::OpenSSL)");
744             #endif
745             }
746              
747             int
748             has_tls (struct feer_server *server)
749             CODE:
750             {
751             PERL_UNUSED_VAR(server);
752             #ifdef FEERSUM_HAS_TLS
753 55 50         RETVAL = 1;
754             #else
755             RETVAL = 0;
756             #endif
757             }
758             OUTPUT:
759             RETVAL
760              
761             int
762             has_h2 (struct feer_server *server)
763             CODE:
764             {
765             PERL_UNUSED_VAR(server);
766             #ifdef FEERSUM_HAS_H2
767             RETVAL = 1;
768             #else
769 15 50         RETVAL = 0;
770             #endif
771             }
772             OUTPUT:
773             RETVAL
774              
775             BOOT:
776             {
777 136           feer_stash = gv_stashpv("Feersum", 1);
778 136           feer_conn_stash = gv_stashpv("Feersum::Connection", 1);
779 136           feer_conn_writer_stash = gv_stashpv("Feersum::Connection::Writer",1);
780 136           feer_conn_reader_stash = gv_stashpv("Feersum::Connection::Reader",1);
781 136 50         I_EV_API("Feersum");
    50          
    50          
782              
783 136           const char *env_fl_max = getenv("FEERSUM_FREELIST_MAX");
784 136 50         if (env_fl_max) {
785 0           FEERSUM_FREELIST_MAX = atoi(env_fl_max);
786             }
787              
788             // Allocate default server (backed by a blessed Perl SV)
789 136           default_server = new_feer_server(aTHX);
790             // Keep an extra refcount so the default server is never GC'd
791 136           SvREFCNT_inc_void_NN(default_server->self);
792              
793 136           psgi_ver = newAV();
794 136           av_extend(psgi_ver, 1); // pre-allocate for 2 elements (psgi.version = [1, 1])
795 136           av_push(psgi_ver, newSViv(1));
796 136           av_push(psgi_ver, newSViv(1));
797 136           SvREADONLY_on((SV*)psgi_ver);
798              
799 136           psgi_serv10 = newSVpvs("HTTP/1.0");
800 136           SvREADONLY_on(psgi_serv10);
801 136           psgi_serv11 = newSVpvs("HTTP/1.1");
802 136           SvREADONLY_on(psgi_serv11);
803              
804 136           method_GET = newSVpvs("GET");
805 136           SvREADONLY_on(method_GET);
806 136           method_POST = newSVpvs("POST");
807 136           SvREADONLY_on(method_POST);
808 136           method_HEAD = newSVpvs("HEAD");
809 136           SvREADONLY_on(method_HEAD);
810 136           method_PUT = newSVpvs("PUT");
811 136           SvREADONLY_on(method_PUT);
812 136           method_PATCH = newSVpvs("PATCH");
813 136           SvREADONLY_on(method_PATCH);
814 136           method_DELETE = newSVpvs("DELETE");
815 136           SvREADONLY_on(method_DELETE);
816 136           method_OPTIONS = newSVpvs("OPTIONS");
817 136           SvREADONLY_on(method_OPTIONS);
818              
819 136           status_200 = newSVpvs("200 OK");
820 136           SvREADONLY_on(status_200);
821 136           status_201 = newSVpvs("201 Created");
822 136           SvREADONLY_on(status_201);
823 136           status_204 = newSVpvs("204 No Content");
824 136           SvREADONLY_on(status_204);
825 136           status_301 = newSVpvs("301 Moved Permanently");
826 136           SvREADONLY_on(status_301);
827 136           status_302 = newSVpvs("302 Found");
828 136           SvREADONLY_on(status_302);
829 136           status_304 = newSVpvs("304 Not Modified");
830 136           SvREADONLY_on(status_304);
831 136           status_400 = newSVpvs("400 Bad Request");
832 136           SvREADONLY_on(status_400);
833 136           status_404 = newSVpvs("404 Not Found");
834 136           SvREADONLY_on(status_404);
835 136           status_500 = newSVpvs("500 Internal Server Error");
836 136           SvREADONLY_on(status_500);
837              
838 136           empty_query_sv = newSVpvs("");
839 136           SvREADONLY_on(empty_query_sv);
840              
841 136           Zero(&psgix_io_vtbl, 1, MGVTBL);
842 136           psgix_io_vtbl.svt_get = psgix_io_svt_get;
843 136           newCONSTSUB(feer_stash, "HEADER_NORM_SKIP", newSViv(HEADER_NORM_SKIP));
844 136           newCONSTSUB(feer_stash, "HEADER_NORM_UPCASE", newSViv(HEADER_NORM_UPCASE));
845 136           newCONSTSUB(feer_stash, "HEADER_NORM_LOCASE", newSViv(HEADER_NORM_LOCASE));
846 136           newCONSTSUB(feer_stash, "HEADER_NORM_UPCASE_DASH", newSViv(HEADER_NORM_UPCASE_DASH));
847 136           newCONSTSUB(feer_stash, "HEADER_NORM_LOCASE_DASH", newSViv(HEADER_NORM_LOCASE_DASH));
848              
849             trace3("Feersum booted, iomatrix %lu, FEERSUM_IOMATRIX_SIZE=%u, "
850             "feer_req %lu, feer_conn %lu\n",
851             (long unsigned int)sizeof(struct iomatrix),
852             (unsigned int)FEERSUM_IOMATRIX_SIZE,
853             (long unsigned int)sizeof(struct feer_req),
854             (long unsigned int)sizeof(struct feer_conn)
855             );
856             }
857              
858             INCLUDE: feersum_conn.xs