| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Test::HTTP::AnyEvent::Server; |
|
2
|
|
|
|
|
|
|
# ABSTRACT: the async counterpart to Test::HTTP::Server |
|
3
|
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
11
|
|
|
11
|
|
83660
|
use feature qw(state switch); |
|
|
11
|
|
|
|
|
22
|
|
|
|
11
|
|
|
|
|
1123
|
|
|
6
|
11
|
|
|
11
|
|
61
|
use strict; |
|
|
11
|
|
|
|
|
19
|
|
|
|
11
|
|
|
|
|
338
|
|
|
7
|
11
|
|
|
11
|
|
57
|
use utf8; |
|
|
11
|
|
|
|
|
14
|
|
|
|
11
|
|
|
|
|
84
|
|
|
8
|
11
|
|
|
11
|
|
276
|
use warnings qw(all); |
|
|
11
|
|
|
|
|
19
|
|
|
|
11
|
|
|
|
|
397
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
11
|
|
|
11
|
|
7682
|
use AnyEvent; |
|
|
11
|
|
|
|
|
23533
|
|
|
|
11
|
|
|
|
|
315
|
|
|
11
|
11
|
|
|
11
|
|
13994
|
use AnyEvent::Handle; |
|
|
11
|
|
|
|
|
265553
|
|
|
|
11
|
|
|
|
|
646
|
|
|
12
|
11
|
|
|
11
|
|
13907
|
use AnyEvent::Log; |
|
|
11
|
|
|
|
|
160092
|
|
|
|
11
|
|
|
|
|
519
|
|
|
13
|
11
|
|
|
11
|
|
12444
|
use AnyEvent::Socket; |
|
|
11
|
|
|
|
|
206388
|
|
|
|
11
|
|
|
|
|
1974
|
|
|
14
|
11
|
|
|
11
|
|
150
|
use AnyEvent::Util; |
|
|
11
|
|
|
|
|
27
|
|
|
|
11
|
|
|
|
|
800
|
|
|
15
|
11
|
|
|
11
|
|
5362
|
use HTTP::Headers; |
|
|
11
|
|
|
|
|
47209
|
|
|
|
11
|
|
|
|
|
446
|
|
|
16
|
11
|
|
|
11
|
|
10601
|
use HTTP::Request; |
|
|
11
|
|
|
|
|
84119
|
|
|
|
11
|
|
|
|
|
385
|
|
|
17
|
11
|
|
|
11
|
|
5354
|
use HTTP::Response; |
|
|
11
|
|
|
|
|
38411
|
|
|
|
11
|
|
|
|
|
375
|
|
|
18
|
11
|
|
|
11
|
|
3103
|
use Moo; |
|
|
11
|
|
|
|
|
54956
|
|
|
|
11
|
|
|
|
|
98
|
|
|
19
|
11
|
|
|
11
|
|
16323
|
use MooX::Types::MooseLike::Base qw(:all); |
|
|
11
|
|
|
|
|
21893
|
|
|
|
11
|
|
|
|
|
12081
|
|
|
20
|
11
|
|
|
11
|
|
90
|
use POSIX; |
|
|
11
|
|
|
|
|
33
|
|
|
|
11
|
|
|
|
|
119
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
11
|
|
|
11
|
|
53869
|
no if ($] >= 5.017010), warnings => q(experimental); |
|
|
11
|
|
|
|
|
114
|
|
|
|
11
|
|
|
|
|
97
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#$AnyEvent::Log::FILTER->level('debug'); |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $VERSION = '0.007'; # VERSION |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my %pool; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
has address => (is => 'ro', isa => Str, default => sub { '127.0.0.1' }, writer => 'set_address'); |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
has port => (is => 'ro', isa => Int, writer => 'set_port'); |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
has maxconn => (is => 'ro', isa => Int, default => sub { 10 }); |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
has timeout => (is => 'ro', isa => Int, default => sub { 60 }); |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
has disable_proxy => (is => 'ro', isa => Bool, default => sub { 1 }); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
has forked => (is => 'ro', isa => Bool, default => sub { 0 }); |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
has forked_pid => (is => 'ro', isa => Int, writer => 'set_forked_pid'); |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
has server => (is => 'ro', isa => Ref, writer => 'set_server'); |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub BUILD { |
|
56
|
8
|
|
|
8
|
0
|
429
|
my ($self) = @_; |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
## no critic (RequireLocalizedPunctuationVars) |
|
59
|
8
|
50
|
|
|
|
214
|
@ENV{qw(no_proxy http_proxy ftp_proxy all_proxy)} = (q(localhost,127.0.0.1), (q()) x 3) |
|
60
|
|
|
|
|
|
|
if $self->disable_proxy; |
|
61
|
|
|
|
|
|
|
|
|
62
|
8
|
50
|
|
|
|
72
|
unless ($self->forked) { |
|
63
|
|
|
|
|
|
|
$self->set_server( |
|
64
|
|
|
|
|
|
|
$self->start_server(sub { |
|
65
|
8
|
|
|
8
|
|
1946
|
my (undef, $address, $port) = @_; |
|
66
|
8
|
|
|
|
|
52
|
$self->set_address($address); |
|
67
|
8
|
|
|
|
|
5090
|
$self->set_port($port); |
|
68
|
8
|
|
|
|
|
4893
|
AE::log info => |
|
69
|
|
|
|
|
|
|
"bound to http://$address:$port/"; |
|
70
|
|
|
|
|
|
|
}) |
|
71
|
8
|
|
|
|
|
91
|
); |
|
72
|
|
|
|
|
|
|
} else { |
|
73
|
0
|
|
|
|
|
0
|
my ($rh, $wh) = portable_pipe; |
|
74
|
|
|
|
|
|
|
|
|
75
|
0
|
|
|
|
|
0
|
given (fork) { |
|
76
|
0
|
|
|
|
|
0
|
when (undef) { |
|
77
|
0
|
|
|
|
|
0
|
AE::log fatal => |
|
78
|
|
|
|
|
|
|
"couldn't fork(): $!"; |
|
79
|
0
|
|
|
|
|
0
|
} when (0) { |
|
80
|
|
|
|
|
|
|
# child |
|
81
|
0
|
|
|
|
|
0
|
close $rh; |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $h = AnyEvent::Handle->new( |
|
84
|
|
|
|
|
|
|
fh => $wh, |
|
85
|
|
|
|
|
|
|
on_error => sub { |
|
86
|
0
|
|
|
0
|
|
0
|
AE::log fatal => |
|
87
|
|
|
|
|
|
|
"couldn't syswrite() to pipe: $!"; |
|
88
|
|
|
|
|
|
|
}, |
|
89
|
0
|
|
|
|
|
0
|
); |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
$self->set_server( |
|
92
|
|
|
|
|
|
|
$self->start_server(sub { |
|
93
|
0
|
|
|
0
|
|
0
|
my (undef, $address, $port) = @_; |
|
94
|
|
|
|
|
|
|
# have to postpone so the address/port gets actually bound |
|
95
|
0
|
|
|
|
|
0
|
AE::postpone { $h->push_write(join("\t", $address, $port)) }; |
|
|
0
|
|
|
|
|
0
|
|
|
96
|
|
|
|
|
|
|
}) |
|
97
|
0
|
|
|
|
|
0
|
); |
|
98
|
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
0
|
AE::cv->wait; |
|
100
|
0
|
|
|
|
|
0
|
POSIX::_exit(0); |
|
101
|
0
|
|
|
|
|
0
|
exit 1; |
|
102
|
0
|
|
|
|
|
0
|
} default { |
|
103
|
|
|
|
|
|
|
# parent |
|
104
|
0
|
|
|
|
|
0
|
my $pid = $_; |
|
105
|
0
|
|
|
|
|
0
|
close $wh; |
|
106
|
|
|
|
|
|
|
|
|
107
|
0
|
|
|
|
|
0
|
my $buf; |
|
108
|
0
|
|
|
|
|
0
|
my $len = sysread $rh, $buf, 65536; |
|
109
|
0
|
0
|
0
|
|
|
0
|
AE::log fatal => |
|
110
|
|
|
|
|
|
|
"couldn't sysread() from pipe: $!" |
|
111
|
|
|
|
|
|
|
if not defined $len or not $len; |
|
112
|
|
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
0
|
my ($address, $port) = split m{\t}x, $buf; |
|
114
|
0
|
|
|
|
|
0
|
$self->set_address($address); |
|
115
|
0
|
|
|
|
|
0
|
$self->set_port($port); |
|
116
|
0
|
|
|
|
|
0
|
$self->set_forked_pid($pid); |
|
117
|
0
|
|
|
|
|
0
|
AE::log info => |
|
118
|
|
|
|
|
|
|
"forked as $pid and bound to http://$address:$port/"; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
8
|
|
|
|
|
59915
|
return; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub DEMOLISH { |
|
127
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_; |
|
128
|
|
|
|
|
|
|
|
|
129
|
0
|
0
|
|
|
|
0
|
if ($self->forked) { |
|
130
|
0
|
|
|
|
|
0
|
my $pid = $self->forked_pid; |
|
131
|
0
|
|
|
|
|
0
|
kill 9 => $pid; |
|
132
|
0
|
|
|
|
|
0
|
AE::log info => |
|
133
|
|
|
|
|
|
|
"killed $pid"; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
0
|
return; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub uri { |
|
141
|
3
|
|
|
3
|
1
|
8111
|
my ($self) = @_; |
|
142
|
3
|
|
|
|
|
51
|
return sprintf('http://%s:%d/', $self->address, $self->port); |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub start_server { |
|
147
|
8
|
|
|
8
|
1
|
22
|
my ($self, $cb) = @_; |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
return tcp_server( |
|
150
|
|
|
|
|
|
|
$self->address => $self->port, |
|
151
|
|
|
|
|
|
|
sub { |
|
152
|
0
|
|
|
0
|
|
|
my ($fh, $host, $port) = @_; |
|
153
|
0
|
0
|
|
|
|
|
if (scalar keys %pool > $self->maxconn) { |
|
154
|
0
|
|
|
|
|
|
AE::log error => |
|
155
|
|
|
|
|
|
|
"deny connection from $host:$port (too many connections)\n"; |
|
156
|
0
|
|
|
|
|
|
return; |
|
157
|
|
|
|
|
|
|
} else { |
|
158
|
0
|
|
|
|
|
|
AE::log warn => |
|
159
|
|
|
|
|
|
|
"new connection from $host:$port\n"; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
|
|
162
|
0
|
|
|
|
|
|
my $h = AnyEvent::Handle->new( |
|
163
|
|
|
|
|
|
|
fh => $fh, |
|
164
|
|
|
|
|
|
|
on_eof => \&_cleanup, |
|
165
|
|
|
|
|
|
|
on_error => \&_cleanup, |
|
166
|
|
|
|
|
|
|
timeout => $self->timeout, |
|
167
|
|
|
|
|
|
|
); |
|
168
|
|
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
|
$pool{fileno($fh)} = $h; |
|
170
|
0
|
|
|
|
|
|
AE::log debug => |
|
171
|
|
|
|
|
|
|
sprintf "%d connection(s) in pool\n", scalar keys %pool; |
|
172
|
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
my ($req, $hdr); |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
$h->push_read(regex => qr{\015?\012}x, sub { |
|
176
|
|
|
|
|
|
|
#my ($h, $data) = @_; |
|
177
|
0
|
|
|
|
|
|
my (undef, $data) = @_; |
|
178
|
0
|
|
|
|
|
|
$data =~ s/\s+$//sx; |
|
179
|
0
|
|
|
|
|
|
$req = $data; |
|
180
|
0
|
|
|
|
|
|
AE::log debug => "request: [$req]\n"; |
|
181
|
0
|
|
|
|
|
|
}); |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
$h->push_read(regex => qr{(\015?\012){2}}x, sub { |
|
184
|
0
|
|
|
|
|
|
my ($_h, $data) = @_; |
|
185
|
0
|
|
|
|
|
|
$hdr = $data; |
|
186
|
0
|
|
|
|
|
|
AE::log debug => "got headers\n"; |
|
187
|
0
|
0
|
|
|
|
|
if ($hdr =~ m{\bContent-length:\s*(\d+)\b}isx) { |
|
188
|
0
|
|
|
|
|
|
AE::log debug => "expecting content\n"; |
|
189
|
|
|
|
|
|
|
$_h->push_read(chunk => int($1), sub { |
|
190
|
0
|
|
|
|
|
|
my ($__h, $__data) = @_; |
|
191
|
0
|
|
|
|
|
|
_reply($__h, $req, $hdr, $__data); |
|
192
|
0
|
|
|
|
|
|
}); |
|
193
|
|
|
|
|
|
|
} else { |
|
194
|
0
|
|
|
|
|
|
_reply($_h, $req, $hdr); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
0
|
|
|
|
|
|
}); |
|
197
|
8
|
|
|
|
|
146
|
} => $cb |
|
198
|
|
|
|
|
|
|
); |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _cleanup { |
|
203
|
|
|
|
|
|
|
#my ($h, $fatal, $msg) = @_; |
|
204
|
0
|
|
|
0
|
|
|
my ($h) = @_; |
|
205
|
0
|
|
|
|
|
|
AE::log debug => "closing connection\n"; |
|
206
|
0
|
|
|
|
|
|
my $r = eval { |
|
207
|
|
|
|
|
|
|
## no critic (ProhibitNoWarnings) |
|
208
|
11
|
|
|
11
|
|
21560
|
no warnings; |
|
|
11
|
|
|
|
|
54
|
|
|
|
11
|
|
|
|
|
10291
|
|
|
209
|
|
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
my $id = fileno($h->{fh}); |
|
211
|
0
|
|
|
|
|
|
delete $pool{$id}; |
|
212
|
0
|
|
|
|
|
|
shutdown $h->{fh}, 2; |
|
213
|
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
|
return 1; |
|
215
|
|
|
|
|
|
|
}; |
|
216
|
0
|
0
|
0
|
|
|
|
AE::log warn => "shutdown() aborted\n" |
|
217
|
|
|
|
|
|
|
if not defined $r or $@; |
|
218
|
0
|
|
|
|
|
|
$h->destroy; |
|
219
|
0
|
|
|
|
|
|
return; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub _reply { |
|
224
|
0
|
|
|
0
|
|
|
my ($h, $req, $hdr, $content) = @_; |
|
225
|
0
|
|
|
|
|
|
state $timer = {}; |
|
226
|
|
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
my $res = HTTP::Response->new( |
|
228
|
|
|
|
|
|
|
200 => 'OK', |
|
229
|
|
|
|
|
|
|
HTTP::Headers->new( |
|
230
|
|
|
|
|
|
|
Connection => 'close', |
|
231
|
|
|
|
|
|
|
Content_Type => 'text/plain', |
|
232
|
|
|
|
|
|
|
Server => __PACKAGE__ . "/$Test::HTTP::AnyEvent::Server::VERSION AnyEvent/$AE::VERSION Perl/$] ($^O)", |
|
233
|
|
|
|
|
|
|
) |
|
234
|
|
|
|
|
|
|
); |
|
235
|
0
|
|
|
|
|
|
$res->date(time); |
|
236
|
0
|
|
|
|
|
|
$res->protocol('HTTP/1.0'); |
|
237
|
|
|
|
|
|
|
|
|
238
|
0
|
0
|
|
|
|
|
if ($req =~ m{^(GET|POST)\s+(.+)\s+(HTTP/1\.[01])$}ix) { |
|
239
|
0
|
|
|
|
|
|
my ($method, $uri, $protocol) = ($1, $2, $3); |
|
240
|
0
|
|
|
|
|
|
AE::log debug => "sending response to $method ($protocol)\n"; |
|
241
|
0
|
|
|
|
|
|
for ($uri) { |
|
242
|
0
|
|
|
|
|
|
when (m{^/repeat/(\d+)/(.+)}x) { |
|
243
|
0
|
|
|
|
|
|
$res->content($2 x $1); |
|
244
|
0
|
|
|
|
|
|
} when (m{^/echo/head$}x) { |
|
245
|
0
|
|
|
|
|
|
$res->content( |
|
246
|
|
|
|
|
|
|
join( |
|
247
|
|
|
|
|
|
|
"\015\012", |
|
248
|
|
|
|
|
|
|
$req, |
|
249
|
|
|
|
|
|
|
$hdr, |
|
250
|
|
|
|
|
|
|
) |
|
251
|
|
|
|
|
|
|
); |
|
252
|
0
|
|
|
|
|
|
} when (m{^/echo/body$}x) { |
|
253
|
0
|
|
|
|
|
|
$res->content($content); |
|
254
|
0
|
|
|
|
|
|
} when (m{^/delay/(\d+)$}x) { |
|
255
|
0
|
|
|
|
|
|
$res->content(sprintf(qq(issued %s\n), scalar gmtime)); |
|
256
|
|
|
|
|
|
|
$timer->{$h} = AE::timer $1, 0, sub { |
|
257
|
0
|
|
|
0
|
|
|
delete $timer->{$h}; |
|
258
|
0
|
|
|
|
|
|
AE::log debug => "delayed response\n"; |
|
259
|
0
|
|
|
|
|
|
$h->push_write($res->as_string("\015\012")); |
|
260
|
0
|
|
|
|
|
|
_cleanup($h); |
|
261
|
0
|
|
|
|
|
|
}; |
|
262
|
0
|
|
|
|
|
|
return; |
|
263
|
0
|
|
|
|
|
|
} default { |
|
264
|
0
|
|
|
|
|
|
$res->code(404); |
|
265
|
0
|
|
|
|
|
|
$res->message('Not Found'); |
|
266
|
0
|
|
|
|
|
|
$res->content('Not Found'); |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
} else { |
|
270
|
0
|
|
|
|
|
|
AE::log error => "bad request\n"; |
|
271
|
0
|
|
|
|
|
|
$res->code(400); |
|
272
|
0
|
|
|
|
|
|
$res->message('Bad Request'); |
|
273
|
0
|
|
|
|
|
|
$res->content('Bad Request'); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
$h->push_write($res->as_string("\015\012")); |
|
277
|
0
|
|
|
|
|
|
_cleanup($h); |
|
278
|
0
|
|
|
|
|
|
return; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1; |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
__END__ |