line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Perlbal::Test::WebServer; |
4
|
|
|
|
|
|
|
|
5
|
57
|
|
|
57
|
|
49674
|
use strict; |
|
57
|
|
|
|
|
121
|
|
|
57
|
|
|
|
|
2047
|
|
6
|
57
|
|
|
57
|
|
274
|
use IO::Socket::INET; |
|
57
|
|
|
|
|
104
|
|
|
57
|
|
|
|
|
732
|
|
7
|
57
|
|
|
57
|
|
109919
|
use HTTP::Request; |
|
57
|
|
|
|
|
56530
|
|
|
57
|
|
|
|
|
1774
|
|
8
|
57
|
|
|
57
|
|
364
|
use Socket qw(MSG_NOSIGNAL IPPROTO_TCP TCP_NODELAY SOL_SOCKET); |
|
57
|
|
|
|
|
104
|
|
|
57
|
|
|
|
|
7882
|
|
9
|
57
|
|
|
57
|
|
306
|
use Perlbal::Test; |
|
57
|
|
|
|
|
86
|
|
|
57
|
|
|
|
|
6262
|
|
10
|
|
|
|
|
|
|
|
11
|
57
|
|
|
57
|
|
68561
|
use Perlbal::Test::WebClient; |
|
57
|
|
|
|
|
154
|
|
|
57
|
|
|
|
|
122591
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Exporter; |
14
|
57
|
|
|
57
|
|
445
|
use vars qw(@ISA @EXPORT); |
|
57
|
|
|
|
|
120
|
|
|
57
|
|
|
|
|
204237
|
|
15
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
16
|
|
|
|
|
|
|
@EXPORT = qw(start_webserver); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our @webserver_pids; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my $testpid; # of the test suite's main program, the one running the HTTP client |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
END { |
23
|
|
|
|
|
|
|
# ensure we kill off the webserver |
24
|
57
|
100
|
100
|
57
|
|
7072074
|
kill 9, @webserver_pids if $testpid && $testpid == $$; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub start_webserver { |
29
|
78
|
|
|
78
|
0
|
5817
|
my $port = new_port(); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# dummy mode |
32
|
78
|
50
|
|
|
|
508
|
if ($ENV{'TEST_PERLBAL_USE_EXISTING'}) { |
33
|
0
|
|
|
|
|
0
|
return $port; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
78
|
|
|
|
|
793
|
$testpid = $$; |
37
|
|
|
|
|
|
|
|
38
|
78
|
100
|
|
|
|
232499
|
if (my $child = fork) { |
39
|
|
|
|
|
|
|
# i am parent, wait for child to startup |
40
|
43
|
|
|
|
|
1512
|
push @webserver_pids, $child; |
41
|
43
|
|
|
|
|
6881
|
my $sock = wait_on_child($child, $port); |
42
|
43
|
50
|
|
|
|
334
|
die "Unable to spawn webserver on port $port\n" |
43
|
|
|
|
|
|
|
unless $sock; |
44
|
43
|
|
|
|
|
4390
|
print $sock "GET /reqdecr,status HTTP/1.0\r\n\r\n"; |
45
|
43
|
|
|
|
|
868879
|
my $line = <$sock>; |
46
|
43
|
0
|
33
|
|
|
3074
|
die "Didn't get 200 OK: " . (defined $line ? $line : "(undef)") |
|
|
50
|
|
|
|
|
|
47
|
|
|
|
|
|
|
unless $line && $line =~ /200 OK/; |
48
|
43
|
|
|
|
|
1548
|
return $port; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# i am child, start up |
52
|
35
|
50
|
|
|
|
579925
|
my $ssock = IO::Socket::INET->new(LocalPort => $port, ReuseAddr => 1, Listen => 3) |
53
|
|
|
|
|
|
|
or die "Unable to start socket: $!\n"; |
54
|
35
|
|
|
|
|
61588
|
while (my $csock = $ssock->accept) { |
55
|
81
|
50
|
|
|
|
70053922
|
exit 0 unless $csock; |
56
|
81
|
100
|
|
|
|
219925
|
fork and next; # parent starts waiting for next request |
57
|
35
|
50
|
|
|
|
5640
|
setsockopt($csock, IPPROTO_TCP, TCP_NODELAY, pack("l", 1)) or die; |
58
|
35
|
|
|
|
|
4460
|
serve_client($csock); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub serve_client { |
63
|
35
|
|
|
35
|
0
|
1572
|
my $csock = shift; |
64
|
35
|
|
|
|
|
597
|
my $req_num = 0; |
65
|
35
|
|
|
|
|
456
|
my $did_options = 0; |
66
|
35
|
|
|
|
|
424
|
my @reqs; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
REQ: |
69
|
35
|
|
|
|
|
572
|
while (1) { |
70
|
156
|
|
|
|
|
1381
|
my $req = ''; |
71
|
156
|
|
|
|
|
1268
|
my $clen = undef; |
72
|
156
|
|
|
|
|
66295977
|
while (<$csock>) { |
73
|
695
|
|
|
|
|
3266
|
$req .= $_; |
74
|
695
|
100
|
|
|
|
6868
|
if (/^content-length:\s*(\d+)/i) { $clen = $1; }; |
|
86
|
|
|
|
|
815
|
|
75
|
695
|
100
|
66
|
|
|
17828
|
last if ! $_ || /^\r?\n/; |
76
|
|
|
|
|
|
|
} |
77
|
156
|
100
|
|
|
|
2536
|
exit 0 unless $req; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# parse out things we want to have |
80
|
151
|
|
|
|
|
750
|
my @cmds; |
81
|
151
|
|
|
|
|
2681
|
my $httpver = 0; # 0 = 1.0, 1 = 1.1, undef = neither |
82
|
151
|
|
|
|
|
709
|
my $method; |
83
|
151
|
100
|
|
|
|
3359
|
if ($req =~ m!^([A-Z]+) /?(\S+) HTTP/(1\.\d+)\r?\n?!) { |
84
|
150
|
|
|
|
|
1526
|
$method = $1; |
85
|
150
|
|
|
|
|
2134
|
my $cmds = durl($2); |
86
|
150
|
|
|
|
|
1716
|
@cmds = split(/\s*,\s*/, $cmds); |
87
|
150
|
|
|
|
|
356
|
$req_num++; |
88
|
150
|
0
|
|
|
|
2630
|
$httpver = ($3 eq '1.0' ? 0 : ($3 eq '1.1' ? 1 : undef)); |
|
|
50
|
|
|
|
|
|
89
|
|
|
|
|
|
|
} |
90
|
151
|
|
|
|
|
5581
|
my $msg = HTTP::Request->parse($req); |
91
|
151
|
|
|
|
|
3551741
|
my $keeping_alive = undef; |
92
|
|
|
|
|
|
|
|
93
|
151
|
|
|
|
|
538
|
my $body; |
94
|
151
|
100
|
|
|
|
795
|
if ($clen) { |
95
|
85
|
50
|
|
|
|
768
|
die "Can't read a body on a GET or HEAD" if $method =~ /^GET|HEAD$/; |
96
|
85
|
|
|
|
|
12040244
|
my $read = read $csock, $body, $clen; |
97
|
85
|
50
|
|
|
|
1135
|
die "Didn't read $clen bytes. Got $read." if $clen != $read; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
my $response = sub { |
101
|
151
|
|
|
151
|
|
1229
|
my %opts = @_; |
102
|
151
|
|
|
|
|
1307
|
my $code = delete $opts{code}; |
103
|
151
|
|
|
|
|
2062
|
my $codetext = delete $opts{codetext}; |
104
|
151
|
|
|
|
|
778
|
my $content = delete $opts{content}; |
105
|
151
|
|
|
|
|
527
|
my $ctype = delete $opts{type}; |
106
|
151
|
|
|
|
|
358
|
my $extra_hdr = delete $opts{headers}; |
107
|
151
|
50
|
|
|
|
870
|
die "unknown data in opts: %opts" if %opts; |
108
|
|
|
|
|
|
|
|
109
|
151
|
|
100
|
|
|
2074
|
$extra_hdr ||= ''; |
110
|
151
|
100
|
66
|
|
|
1225
|
$code ||= $content ? 200 : 200; |
111
|
151
|
|
33
|
|
|
4190
|
$codetext ||= { 200 => 'OK', 500 => 'Internal Server Error', 204 => "No Content" }->{$code}; |
112
|
151
|
|
100
|
|
|
919
|
$content ||= ""; |
113
|
|
|
|
|
|
|
|
114
|
151
|
|
|
|
|
486
|
my $clen = length $content; |
115
|
151
|
100
|
50
|
|
|
1741
|
$ctype ||= "text/plain" unless $code == 204; |
116
|
151
|
100
|
|
|
|
869
|
$extra_hdr .= "Content-Type: $ctype\r\n" if $ctype; |
117
|
|
|
|
|
|
|
|
118
|
151
|
|
|
|
|
347
|
my $hdr_keepalive = ""; |
119
|
|
|
|
|
|
|
|
120
|
151
|
100
|
|
|
|
822
|
unless (defined $keeping_alive) { |
121
|
149
|
|
100
|
|
|
1111
|
my $hdr_connection = $msg->header('Connection') || ''; |
122
|
149
|
50
|
|
|
|
13643
|
if ($httpver == 1) { |
123
|
0
|
0
|
|
|
|
0
|
if ($hdr_connection =~ /\bclose\b/i) { |
124
|
0
|
|
|
|
|
0
|
$keeping_alive = 0; |
125
|
|
|
|
|
|
|
} else { |
126
|
0
|
|
|
|
|
0
|
$keeping_alive = "1.1implicit"; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
} |
129
|
149
|
100
|
66
|
|
|
2582
|
if ($httpver == 0 && $hdr_connection =~ /\bkeep-alive\b/i) { |
130
|
121
|
|
|
|
|
311
|
$keeping_alive = "1.0keepalive"; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
151
|
100
|
|
|
|
421
|
if ($keeping_alive) { |
135
|
121
|
|
|
|
|
309
|
$hdr_keepalive = "Connection: keep-alive\n"; |
136
|
|
|
|
|
|
|
} else { |
137
|
30
|
|
|
|
|
614
|
$hdr_keepalive = "Connection: close\n"; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
151
|
|
|
|
|
3768
|
return "HTTP/1.0 $code $codetext\r\n" . |
141
|
|
|
|
|
|
|
$hdr_keepalive . |
142
|
|
|
|
|
|
|
"Content-Length: $clen\r\n" . |
143
|
|
|
|
|
|
|
$extra_hdr . |
144
|
|
|
|
|
|
|
"\r\n" . |
145
|
|
|
|
|
|
|
"$content"; |
146
|
151
|
|
|
|
|
4893
|
}; |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $send = sub { |
149
|
151
|
|
|
151
|
|
443
|
my $res = shift; |
150
|
151
|
|
|
|
|
30859
|
print $csock $res; |
151
|
151
|
100
|
|
|
|
44168
|
exit 0 unless $keeping_alive; |
152
|
151
|
|
|
|
|
1074
|
}; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# 500 if no commands were given or we don't know their HTTP version |
155
|
|
|
|
|
|
|
# or we didn't parse a proper HTTP request |
156
|
151
|
50
|
66
|
|
|
4621
|
unless (@cmds && defined $httpver && $msg) { |
|
|
|
66
|
|
|
|
|
157
|
1
|
|
|
|
|
40
|
print STDERR "500 response!\n"; |
158
|
1
|
|
|
|
|
6
|
$send->($response->(code => 500)); |
159
|
1
|
|
|
|
|
21
|
next REQ; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
150
|
100
|
|
|
|
968
|
if ($method eq "OPTIONS") { |
163
|
1
|
|
|
|
|
20
|
$did_options = 1; |
164
|
1
|
|
|
|
|
6
|
$send->($response->(code => 200)); |
165
|
1
|
|
|
|
|
72
|
next REQ; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# prepare a simple 200 to send; undef this if you want to control |
169
|
|
|
|
|
|
|
# your own output below |
170
|
149
|
|
|
|
|
503
|
my $to_send; |
171
|
|
|
|
|
|
|
|
172
|
149
|
|
|
|
|
874
|
foreach my $cmd (@cmds) { |
173
|
171
|
|
|
|
|
1390
|
$cmd =~ s/^\s+//; |
174
|
171
|
|
|
|
|
656
|
$cmd =~ s/\s+$//; |
175
|
|
|
|
|
|
|
|
176
|
171
|
100
|
|
|
|
836
|
if ($cmd =~ /^sleep:([\d\.]+)$/i) { |
177
|
4
|
|
|
|
|
25
|
my $sleeptime = $1; |
178
|
|
|
|
|
|
|
#print "I, $$, should sleep for $sleeptime.\n"; |
179
|
57
|
|
|
57
|
|
64641
|
use Time::HiRes; |
|
57
|
|
|
|
|
124752
|
|
|
57
|
|
|
|
|
289
|
|
180
|
4
|
|
|
|
|
32
|
my $t1 = Time::HiRes::time(); |
181
|
4
|
|
|
|
|
1406074
|
select undef, undef, undef, $1; |
182
|
4
|
|
|
|
|
40
|
my $t2 = Time::HiRes::time(); |
183
|
4
|
|
|
|
|
34
|
my $td = $t2 - $t1; |
184
|
|
|
|
|
|
|
#print "I, $$, slept for $td\n"; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
171
|
100
|
|
|
|
2070
|
if ($cmd =~ /^keepalive:([01])$/i) { |
188
|
2
|
|
|
|
|
13
|
$keeping_alive = $1; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
171
|
100
|
|
|
|
645
|
if ($cmd eq "status") { |
192
|
128
|
|
100
|
|
|
3608
|
my $len = $clen || 0; |
193
|
128
|
|
100
|
|
|
6241
|
my $bu = $msg->header('X-PERLBAL-BUFFERED-UPLOAD-REASON') || ''; |
194
|
128
|
|
|
|
|
33801
|
$to_send = $response->(content => |
195
|
|
|
|
|
|
|
"pid = $$\nreqnum = $req_num\nmethod = $method\n". |
196
|
|
|
|
|
|
|
"length = $len\nbuffered = $bu\noptions = $did_options\n"); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
171
|
100
|
|
|
|
1352
|
if ($cmd eq "reqdecr") { |
200
|
15
|
|
|
|
|
52
|
$req_num--; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
171
|
100
|
|
|
|
592
|
if ($cmd =~ /^kill:(\d+):(\w+)$/) { |
204
|
1
|
|
|
|
|
183
|
kill $2, $1; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
171
|
100
|
|
|
|
1387
|
if ($cmd =~ /^reproxy_url:(.+)/i) { |
208
|
8
|
|
|
|
|
161
|
$to_send = $response->(headers => "X-Reproxy-URL: $1\r\n", |
209
|
|
|
|
|
|
|
code => 204, |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
171
|
100
|
|
|
|
1206
|
if ($cmd =~ /^reproxy_url204:(.+)/i) { |
214
|
2
|
|
|
|
|
14
|
$to_send = $response->(headers => "X-Reproxy-URL: $1\r\n"); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
171
|
100
|
|
|
|
713
|
if ($cmd =~ /^reproxy_url_cached:(\d+):(.+)/i) { |
218
|
2
|
|
|
|
|
69
|
kill 'USR1', $testpid; |
219
|
2
|
|
|
|
|
29
|
$to_send = $response->(headers => |
220
|
|
|
|
|
|
|
"X-Reproxy-URL: $2\r\nX-Reproxy-Cache-For: $1; Last-Modified Content-Type\r\nLast-Modified: 199\r\nContent-Type: application/badger\r\n"); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
171
|
100
|
|
|
|
768
|
if ($cmd =~ /^reproxy_url_multi:((?:\d+:){2,})(\S+)/i) { |
224
|
1
|
|
|
|
|
5
|
my $ports = $1; |
225
|
1
|
|
|
|
|
3
|
my $path = $2; |
226
|
1
|
|
|
|
|
2
|
my @urls; |
227
|
1
|
|
|
|
|
5
|
foreach my $port (split(/:/, $ports)) { |
228
|
2
|
|
|
|
|
7
|
push @urls, "http://127.0.0.1:$port$path"; |
229
|
|
|
|
|
|
|
} |
230
|
1
|
|
|
|
|
7
|
$to_send = $response->(headers => "X-Reproxy-URL: @urls\r\n"); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
171
|
100
|
|
|
|
832
|
if ($cmd =~ /^reproxy_file:(.+)/i) { |
234
|
6
|
|
|
|
|
42
|
$to_send = $response->(headers => "X-Reproxy-File: $1\r\n"); |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
171
|
100
|
|
|
|
854
|
if ($cmd =~ /^subreq:(\d+)$/) { |
238
|
1
|
|
|
|
|
22
|
my $port = $1; |
239
|
1
|
|
|
|
|
55
|
my $wc = Perlbal::Test::WebClient->new; |
240
|
1
|
|
|
|
|
9
|
$wc->server("127.0.0.1:$port"); |
241
|
1
|
|
|
|
|
172
|
$wc->keepalive(0); |
242
|
1
|
|
|
|
|
6
|
$wc->http_version('1.0'); |
243
|
1
|
|
|
|
|
13
|
my $resp = $wc->request("status"); |
244
|
1
|
|
|
|
|
3
|
my $subpid; |
245
|
1
|
50
|
33
|
|
|
18
|
if ($resp && $resp->content =~ /^pid = (\d+)$/m) { |
246
|
1
|
|
|
|
|
25
|
$subpid = $1; |
247
|
|
|
|
|
|
|
} |
248
|
1
|
|
|
|
|
14
|
$to_send = $response->(content => "pid = $$\nsubpid = $subpid\nreqnum = $req_num\n"); |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
171
|
100
|
|
|
|
1032
|
if ($cmd =~ /^reflect_request_headers$/) { |
252
|
1
|
|
|
|
|
21
|
$to_send = $response->(content => $msg->headers->as_string); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
149
|
|
33
|
|
|
1740
|
$send->($to_send || $response->()); |
257
|
|
|
|
|
|
|
} # while(1) |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# de-url escape |
261
|
|
|
|
|
|
|
sub durl { |
262
|
150
|
|
|
150
|
0
|
1183
|
my ($a) = @_; |
263
|
150
|
|
|
|
|
1312
|
$a =~ tr/+/ /; |
264
|
150
|
|
|
|
|
1074
|
$a =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|
0
|
|
|
|
|
0
|
|
265
|
150
|
|
|
|
|
746
|
return $a; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
1; |