File Coverage

bin/pagi-server
Criterion Covered Total %
statement 32 56 57.1
branch 8 34 23.5
condition 1 6 16.6
subroutine 5 5 100.0
pod n/a
total 46 101 45.5


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             # Handle compile-time options before loading any modules
4             # Future::XS must be loaded before Future, so check early
5             BEGIN {
6 1     1   4189 my @to_splice;
7 1         4 for my $i (0 .. $#ARGV) {
8 3 50       10 if ($ARGV[$i] eq '--future-xs') {
    100          
9 0         0 $ENV{PAGI_FUTURE_XS} = 1;
10 0         0 push @to_splice, $i;
11             }
12             elsif ($ARGV[$i] eq '--http2') {
13 1         10 $ENV{_PAGI_SERVER_HTTP2} = 1;
14 1         5 push @to_splice, $i;
15             }
16             }
17             # Remove processed flags in reverse order to preserve indices
18 1         1 for my $i (reverse @to_splice) {
19 1         3 splice @ARGV, $i, 1;
20             }
21              
22             # Capture original args for hot restart re-exec (USR2)
23 1 50       36 $ENV{PAGI_ARGV} = join("\0", @ARGV) unless exists $ENV{PAGI_ARGV};
24             }
25              
26 1     1   5 use strict;
  1         3  
  1         18  
27 1     1   2 use warnings;
  1         2  
  1         37  
28              
29 1     1   611 use Getopt::Long qw(GetOptionsFromArray :config pass_through no_auto_abbrev);
  1         12959  
  1         6  
30 1     1   792 use PAGI::Runner;
  1         7  
  1         2345  
31              
32             # Parse PAGI::Server-specific options before passing to Runner
33             # This keeps Runner server-agnostic
34 1         136527 my %server_opts;
35              
36             GetOptionsFromArray(
37             \@ARGV,
38             # Network (Unix socket / multi-listener)
39             'socket=s' => \$server_opts{socket},
40             'socket-mode=s' => \$server_opts{_socket_mode},
41             'listen=s@' => \$server_opts{_listen},
42              
43             # Workers/scaling
44             'w|workers=i' => \$server_opts{workers},
45             'reuseport' => \$server_opts{reuseport},
46             'max-requests=i' => \$server_opts{max_requests},
47             'max-connections=i' => \$server_opts{max_connections},
48              
49             # TLS
50             'ssl-cert=s' => \$server_opts{_ssl_cert},
51             'ssl-key=s' => \$server_opts{_ssl_key},
52              
53             # Timeouts
54             'timeout=i' => \$server_opts{timeout},
55             'shutdown-timeout=i' => \$server_opts{shutdown_timeout},
56             'request-timeout=i' => \$server_opts{request_timeout},
57             'ws-idle-timeout=i' => \$server_opts{ws_idle_timeout},
58             'sse-idle-timeout=i' => \$server_opts{sse_idle_timeout},
59             'heartbeat-timeout=i' => \$server_opts{heartbeat_timeout},
60              
61             # Limits
62             'max-body-size=i' => \$server_opts{max_body_size},
63             'max-header-size=i' => \$server_opts{max_header_size},
64             'max-header-count=i' => \$server_opts{max_header_count},
65             'max-receive-queue=i' => \$server_opts{max_receive_queue},
66             'max-ws-frame-size=i' => \$server_opts{max_ws_frame_size},
67             'b|listener-backlog=i' => \$server_opts{listener_backlog},
68              
69             # Misc
70             'log-level=s' => \$server_opts{log_level},
71             'sync-file-threshold=i' => \$server_opts{sync_file_threshold},
72             'access-log-format=s' => \$server_opts{access_log_format},
73 1         25 );
74              
75             # Build ssl hash if certs provided
76 1 50 33     1732 if ($server_opts{_ssl_cert} || $server_opts{_ssl_key}) {
77             die "--ssl-cert and --ssl-key must be specified together\n"
78 0 0 0     0 unless $server_opts{_ssl_cert} && $server_opts{_ssl_key};
79              
80             die "SSL cert not found: $server_opts{_ssl_cert}\n"
81 0 0       0 unless -f $server_opts{_ssl_cert};
82             die "SSL key not found: $server_opts{_ssl_key}\n"
83 0 0       0 unless -f $server_opts{_ssl_key};
84              
85             $server_opts{ssl} = {
86             cert_file => delete $server_opts{_ssl_cert},
87             key_file => delete $server_opts{_ssl_key},
88 0         0 };
89             }
90 1         2 delete $server_opts{_ssl_cert};
91 1         2 delete $server_opts{_ssl_key};
92              
93             # Handle --socket-mode (octal string to numeric)
94 1 50       3 if (defined $server_opts{_socket_mode}) {
95 0         0 my $mode_str = $server_opts{_socket_mode};
96 0 0       0 die "--socket-mode must be an octal value like 0660, got: $mode_str\n"
97             unless $mode_str =~ /^0[0-7]{3}$/;
98 0         0 $server_opts{socket_mode} = oct($mode_str);
99             }
100 1         2 delete $server_opts{_socket_mode};
101              
102             # Handle --listen (build listen array from repeated --listen flags)
103             # Detection: contains ':' -> TCP host:port, otherwise -> Unix socket path
104 1 50       3 if (my $listen_specs = delete $server_opts{_listen}) {
105 0         0 my @listeners;
106 0         0 for my $spec (@$listen_specs) {
107 0 0       0 if ($spec =~ m{^[./]}) {
    0          
    0          
108             # Starts with / or . — always a Unix socket path
109             # (even if it contains colons, e.g. /var/run/app:8080)
110 0         0 my %entry = (socket => $spec);
111             $entry{socket_mode} = $server_opts{socket_mode}
112 0 0       0 if defined $server_opts{socket_mode};
113 0         0 push @listeners, \%entry;
114             } elsif ($spec =~ /^\[([^\]]+)\]:(\d+)$/) {
115             # IPv6: [::1]:5000
116 0         0 push @listeners, { host => $1, port => int($2) };
117             } elsif ($spec =~ /^(.+):(\d+)$/) {
118             # IPv4/hostname: 127.0.0.1:5000
119 0         0 push @listeners, { host => $1, port => int($2) };
120             } else {
121             # Bare name without / or . prefix — treat as Unix socket
122 0         0 my %entry = (socket => $spec);
123             $entry{socket_mode} = $server_opts{socket_mode}
124 0 0       0 if defined $server_opts{socket_mode};
125 0         0 push @listeners, \%entry;
126             }
127             }
128 0         0 $server_opts{listen} = \@listeners;
129 0         0 delete $server_opts{socket_mode}; # Applied per-listener above
130             }
131              
132             # Handle workers (0 for single-process, >1 for multi-worker)
133 1 50       5 if (defined $server_opts{workers}) {
134 0 0       0 $server_opts{workers} = $server_opts{workers} > 1 ? $server_opts{workers} : 0;
135             }
136              
137             # Remove undefined options
138 1         11 %server_opts = map { $_ => $server_opts{$_} } grep { defined $server_opts{$_} } keys %server_opts;
  0         0  
  20         41  
139              
140             # Pass server options to Runner
141 1         11 PAGI::Runner->run(@ARGV, server_options => \%server_opts);
142              
143             __END__