File Coverage

Feersum.xs
Criterion Covered Total %
statement 304 343 88.6
branch 171 236 72.4
condition n/a
subroutine n/a
pod n/a
total 475 579 82.0


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