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