File Coverage

blib/lib/Plack/Handler/Net/Async/HTTP/Server.pm
Criterion Covered Total %
statement 11 54 20.3
branch 0 22 0.0
condition 0 5 0.0
subroutine 4 7 57.1
pod 2 2 100.0
total 17 90 18.8


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2013-2024 -- leonerd@leonerd.org.uk
5              
6             package Plack::Handler::Net::Async::HTTP::Server 0.15;
7              
8 1     1   738 use v5.14;
  1         5  
9 1     1   6 use warnings;
  1         3  
  1         52  
10              
11 1     1   659 use Net::Async::HTTP::Server::PSGI;
  1         17  
  1         51  
12 1     1   1170 use IO::Async::Loop;
  1         13674  
  1         1032  
13              
14             =head1 NAME
15              
16             C - HTTP handler for Plack using L
17              
18             =head1 SYNOPSIS
19              
20             use Plack::Handler::Net::Async::HTTP::Server;
21              
22             my $handler = Plack::Handler::Net::Async::HTTP::Server->new(
23             listen => [ ":8080" ],
24             );
25              
26             sub psgi_app { ... }
27              
28             $handler->run( \&psgi_app );
29              
30             =head1 DESCRIPTION
31              
32             This module allows L to run a L application as a standalone
33             HTTP daemon under L, by using L.
34              
35             plackup -s Net::Async::HTTP::Server --listen ":8080" application.psgi
36              
37             This is internally implemented using L;
38             further information on environment etc.. is documented there.
39              
40             If L is available, this handler supports accepting connections
41             via C
42              
43             plackup -s Net::Async::HTTP::Server --ssl ...
44              
45             Or per-listen argument by appending C<:SSL>, as
46              
47             plackup -s Net::Async::HTTP::Server --listen ":8443:SSL" ...
48              
49             Any other options whose names start C will be passed on to the SSL
50             listen method.
51              
52             =cut
53              
54             =head1 CONSTRUCTOR
55              
56             =head2 new
57              
58             $handler = Plack::Handler::Net::Async::HTTP::Server->new( %args );
59              
60             Returns a new instance of a C
61             object. Takes the following named arguments:
62              
63             =over 4
64              
65             =item listen => ARRAY of STRING
66              
67             Reference to an array containing listen string specifications. Each string
68             gives a port number and optional hostname, given as C<:port> or C.
69              
70             =item server_ready => CODE
71              
72             Reference to code to invoke when the server is set up and listening, ready to
73             accept connections. It is invoked with a HASH reference containing the
74             following details:
75              
76             $server_ready->( {
77             host => HOST,
78             port => SERVICE,
79             server_software => NAME,
80             } )
81              
82             =item socket => STRING
83              
84             Gives a UNIX socket path to listen on, instead of a TCP socket.
85              
86             =item queuesize => INT
87              
88             Optional. If provided, sets the C queue size for creating listening
89             sockets. If missing, a default of 10 is used.
90              
91             =back
92              
93             =cut
94              
95             sub new
96             {
97 0     0 1   my $class = shift;
98 0           my %opts = @_;
99              
100 0           delete $opts{host};
101 0           delete $opts{port};
102 0           delete $opts{socket};
103              
104             my $self = bless {
105 0           map { $_ => delete $opts{$_} } qw( listen server_ready queuesize ),
  0            
106             }, $class;
107              
108             # Grab all of the SSL options
109 0 0         $self->{ssl} = 1 if exists $opts{ssl}; delete $opts{ssl};
  0            
110 0           $self->{$_} = delete $opts{$_} for grep m/^ssl_/, keys %opts;
111              
112 0 0         keys %opts and die "Unrecognised keys " . join( ", ", sort keys %opts );
113              
114 0           return $self;
115             }
116              
117             =head1 METHODS
118              
119             =cut
120              
121             =head2 run
122              
123             $handler->run( $psgi_app );
124              
125             Creates the HTTP-listening socket or sockets, and runs the given PSGI
126             application for received requests.
127              
128             =cut
129              
130             sub run
131             {
132 0     0 1   my $self = shift;
133 0           my ( $app ) = @_;
134              
135 0           my $loop = IO::Async::Loop->new;
136 0   0       my $queuesize = $self->{queuesize} || 10;
137              
138 0           foreach my $listen ( @{ $self->{listen} } ) {
  0            
139 0           my $httpserver = Net::Async::HTTP::Server::PSGI->new(
140             app => $app,
141             );
142              
143 0           $loop->add( $httpserver );
144              
145             # IPv6 addresses contain colons. They'll be wrapped in [] brackets
146 0           my $host;
147             my $path;
148              
149 0 0         if( $listen =~ s/^\[([0-9a-f:]+)\]://i ) {
    0          
    0          
150 0           $host = $1;
151             }
152             elsif( $listen =~ s/^([^:]+?):// ) {
153 0           $host = $1;
154             }
155             elsif( $listen =~ s/^:// ) {
156             # OK
157             }
158             else {
159 0           $path = $listen;
160             }
161              
162 0 0         if( defined $path ) {
163 0           require IO::Socket::UNIX;
164              
165 0 0         unlink $path if -e $path;
166              
167 0 0         my $socket = IO::Socket::UNIX->new(
168             Local => $path,
169             Listen => $queuesize,
170             ) or die "Cannot listen on $path - $!";
171              
172 0           $httpserver->configure( handle => $socket );
173             }
174             else {
175 0           my ( $service, $ssl ) = split m/:/, $listen;
176 0   0       $ssl ||= $self->{ssl};
177              
178 0           my %SSL_args;
179 0 0         if( $ssl ) {
180 0           require IO::Async::SSL;
181 0           %SSL_args = (
182             extensions => [qw( SSL )],
183             );
184              
185 0           foreach my $key ( grep m/^ssl_/, keys %$self ) {
186 0           my $val = $self->{$key};
187             # IO::Async::Listener extension wants uppercase "SSL"
188 0           $key =~ s/^ssl/SSL/;
189              
190 0           $SSL_args{$key} = $val;
191             };
192             }
193              
194             $httpserver->listen(
195             host => $host,
196             service => $service,
197             socktype => "stream",
198             queuesize => $queuesize,
199              
200             %SSL_args,
201              
202             on_notifier => sub {
203             $self->{server_ready}->( {
204             host => $host,
205             port => $service,
206             proto => $ssl ? "https" : "http",
207             server_software => ref $self,
208 0 0   0     } ) if $self->{server_ready};
    0          
209             },
210 0           )->get;
211             }
212             }
213              
214 0           $loop->run;
215             }
216              
217             =head1 SEE ALSO
218              
219             =over 4
220              
221             =item *
222              
223             L - serve HTTP with L
224              
225             =item *
226              
227             L - Perl Superglue for Web frameworks and Web Servers (PSGI toolkit)
228              
229             =back
230              
231             =head1 AUTHOR
232              
233             Paul Evans
234              
235             =cut
236              
237             0x55AA;