File Coverage

blib/lib/Plack/Handler/AnyEvent/HTTPD.pm
Criterion Covered Total %
statement 21 89 23.6
branch 0 26 0.0
condition 0 10 0.0
subroutine 7 17 41.1
pod 0 3 0.0
total 28 145 19.3


line stmt bran cond sub pod time code
1             package Plack::Handler::AnyEvent::HTTPD;
2              
3 1     1   21813 use strict;
  1         2  
  1         31  
4 1     1   23 use 5.008_001;
  1         3  
  1         62  
5             our $VERSION = '0.03';
6              
7 1     1   780 use Plack::Util;
  1         15248  
  1         31  
8 1     1   17882 use HTTP::Status;
  1         6756  
  1         459  
9 1     1   1190 use URI::Escape;
  1         2007  
  1         357  
10              
11             sub new {
12 0     0 0   my($class, %args) = @_;
13 0           bless {%args}, $class;
14             }
15              
16             sub register_service {
17 0     0 0   my($self, $app) = @_;
18              
19 0   0       my $httpd = Plack::Handler::AnyEvent::HTTPD::Server->new(
20             port => $self->{port} || 9000,
21             host => $self->{host},
22             request_timeout => $self->{request_timeout},
23             app => $app,
24             );
25              
26 0 0         $self->{server_ready}->({
27             port => $httpd->port,
28             host => $httpd->host,
29             server_software => 'AnyEvent::HTTPD',
30             }) if $self->{server_ready};
31              
32 0           $self->{_httpd} = $httpd;
33             }
34              
35             sub run {
36 0     0 0   my $self = shift;
37 0           $self->register_service(@_);
38              
39 0           $self->{_httpd}->run;
40             }
41              
42             package Plack::Handler::AnyEvent::HTTPD::Server;
43 1     1   12 use parent qw(AnyEvent::HTTPD::HTTPServer);
  1         2  
  1         8  
44              
45             sub new {
46 0     0     my $class = shift;
47 0           my $self = $class->SUPER::new(
48             connection_class => 'Plack::Handler::AnyEvent::HTTPD::Connection',
49             @_,
50             );
51             $self->reg_cb(
52             connect => sub {
53 0     0     my($self, $con) = @_;
54 0           Scalar::Util::weaken($self);
55             $self->{conns}->{$con} = $con->reg_cb(
56             request => sub {
57 0           my($con, $meth, $url, $hdr, $cont) = @_;
58 0           $self->handle_psgi_request($con, $meth, $url, $hdr, $cont);
59             },
60 0           );
61             },
62             disconnect => sub {
63 0     0     my($self, $con) = @_;
64 0           $con->unreg_cb(delete $self->{conns}->{$con});
65             },
66 0           );
67              
68 0   0       $self->{state} ||= {};
69              
70 0           $self;
71             }
72              
73             sub handle_psgi_request {
74 0     0     my($self, $con, $meth, $url, $hdr, $cont) = @_;
75              
76 0           my($path_info, $query) = split /\?/, $url, 2;
77              
78             my $env = {
79             REMOTE_ADDR => $con->{host},
80             SERVER_PORT => $self->port,
81             SERVER_NAME => $self->host,
82             SCRIPT_NAME => '',
83             REQUEST_METHOD => $meth,
84             PATH_INFO => URI::Escape::uri_unescape($path_info),
85             REQUEST_URI => $url,
86             QUERY_STRING => $query,
87             SERVER_PROTOCOL => 'HTTP/1.0', # no way to get this from HTTPConnection
88             'psgi.version' => [ 1, 1 ],
89             'psgi.errors' => *STDERR,
90             'psgi.url_scheme' => 'http',
91             'psgi.nonblocking' => Plack::Util::TRUE,
92             'psgi.streaming' => Plack::Util::TRUE,
93             'psgi.run_once' => Plack::Util::FALSE,
94             'psgi.multithread' => Plack::Util::FALSE,
95             'psgi.multiprocess' => Plack::Util::FALSE,
96 0           'psgi.input' => do {
97 0 0         open my $input, "<", \(ref $cont ? '' : $cont);
98 0           $input;
99             },
100             'psgix.io' => $con->{fh},
101             };
102              
103 0           $env->{CONTENT_TYPE} = delete $hdr->{'content-type'};
104 0           $env->{CONTENT_LENGTH} = delete $hdr->{'content-length'};
105              
106 0           while (my($key, $val) = each %$hdr) {
107 0           $key =~ tr/-/_/;
108 0           $env->{"HTTP_" . uc $key} = $val;
109             }
110              
111 0           my $res = Plack::Util::run_app($self->{app}, $env);
112              
113 0           Scalar::Util::weaken($con);
114             my $respond = sub {
115 0     0     my $res = shift;
116              
117 0           my %headers;
118 0           while ( my($key, $val) = splice @{$res->[1]}, 0, 2) {
  0            
119 0 0         $headers{$key} = exists $headers{$key} ? "$headers{$key}, $val" : $val;
120             }
121 0           my @res = ($res->[0], HTTP::Status::status_message($res->[0]), \%headers);
122              
123 0 0         if (defined $res->[2]) {
124 0           my $content;
125 0           Plack::Util::foreach($res->[2], sub { $content .= $_[0] });
  0            
126              
127             # Work around AnyEvent::HTTPD bugs that it sets
128             # Content-Length even when it's not necessary
129 0 0 0       if (!$content && Plack::Util::status_with_no_entity_body($res->[0])) {
130 0 0         $content = sub { $_[0]->(undef) if $_[0] };
  0            
131             }
132              
133 0 0         $con->response(@res, $content) if $con;
134              
135 0           return;
136             } else {
137             # Probably unnecessary, but in case ->write is
138             # called before the poll callback is execute.
139 0           my @buf;
140 0           my $data_cb = sub { push @buf, $_[0] };
  0            
141             $con->response(@res, sub {
142             # TODO $data_cb = undef -> Client Disconnect
143 0           $data_cb = shift;
144 0 0 0       if ($data_cb && @buf) {
145 0           $data_cb->($_) for @buf;
146 0           @buf = ()
147             }
148 0 0         }) if $con;
149              
150             return Plack::Util::inline_object
151 0 0         write => sub { $data_cb->($_[0]) if $data_cb },
152 0 0         close => sub { $data_cb->(undef) if $data_cb };
  0            
153             }
154 0           };
155              
156 0 0         ref $res eq 'CODE' ? $res->($respond) : $respond->($res);
157             }
158              
159             sub run {
160 0     0     my $self = shift;
161 0           $self->{cv} = AE::cv;
162 0           $self->{cv}->recv;
163             }
164              
165             package Plack::Handler::AnyEvent::HTTPD::Connection;
166 1     1   151022 use parent qw(AnyEvent::HTTPD::HTTPConnection);
  1         4  
  1         9  
167              
168             # Don't parse content
169             sub handle_request {
170 0     0     my($self, $method, $uri, $hdr, $cont) = @_;
171              
172 0 0         if( $hdr->{connection} ) {
173 0           $self->{keep_alive} = ($hdr->{connection} =~ /keep-alive/io);
174             }
175 0           $self->event(request => $method, $uri, $hdr, $cont);
176             }
177              
178             package Plack::Handler::AnyEvent::HTTPD;
179              
180             1;
181             __END__