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__ |