line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Perlbal::Test::WebClient; |
4
|
|
|
|
|
|
|
|
5
|
57
|
|
|
57
|
|
3798
|
use strict; |
|
57
|
|
|
|
|
107
|
|
|
57
|
|
|
|
|
2093
|
|
6
|
57
|
|
|
57
|
|
292
|
use IO::Socket::INET; |
|
57
|
|
|
|
|
85
|
|
|
57
|
|
|
|
|
556
|
|
7
|
57
|
|
|
57
|
|
63707
|
use Perlbal::Test; |
|
57
|
|
|
|
|
107
|
|
|
57
|
|
|
|
|
5854
|
|
8
|
57
|
|
|
57
|
|
314
|
use HTTP::Response; |
|
57
|
|
|
|
|
86
|
|
|
57
|
|
|
|
|
1381
|
|
9
|
57
|
|
|
57
|
|
290
|
use Socket qw(MSG_NOSIGNAL IPPROTO_TCP TCP_NODELAY SOL_SOCKET); |
|
57
|
|
|
|
|
106
|
|
|
57
|
|
|
|
|
4568
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
require Exporter; |
12
|
57
|
|
|
57
|
|
293
|
use vars qw(@ISA @EXPORT $FLAG_NOSIGNAL); |
|
57
|
|
|
|
|
76
|
|
|
57
|
|
|
|
|
79231
|
|
13
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
14
|
|
|
|
|
|
|
@EXPORT = qw(new); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
$FLAG_NOSIGNAL = 0; |
17
|
|
|
|
|
|
|
eval { $FLAG_NOSIGNAL = MSG_NOSIGNAL; }; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# create a blank object |
20
|
|
|
|
|
|
|
sub new { |
21
|
11
|
|
|
11
|
0
|
6428
|
my $class = shift; |
22
|
11
|
|
|
|
|
40
|
my $self = {}; |
23
|
11
|
|
|
|
|
124
|
bless $self, $class; |
24
|
11
|
|
|
|
|
66
|
return $self; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# get/set what server we should be testing; "ip:port" generally |
28
|
|
|
|
|
|
|
sub server { |
29
|
11
|
|
|
11
|
0
|
119
|
my $self = shift; |
30
|
11
|
50
|
|
|
|
95
|
if (@_) { |
31
|
11
|
|
|
|
|
184
|
$self->{_sock} = undef; |
32
|
11
|
|
|
|
|
124
|
return $self->{server} = shift; |
33
|
|
|
|
|
|
|
} else { |
34
|
0
|
|
|
|
|
0
|
return $self->{server}; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# get/set what hostname we send with requests |
39
|
|
|
|
|
|
|
sub host { |
40
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
41
|
0
|
0
|
|
|
|
0
|
if (@_) { |
42
|
0
|
|
|
|
|
0
|
$self->{_sock} = undef; |
43
|
0
|
|
|
|
|
0
|
return $self->{host} = shift; |
44
|
|
|
|
|
|
|
} else { |
45
|
0
|
|
|
|
|
0
|
return $self->{host}; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# set which HTTP version to emulate; specify '1.0' or '1.1' |
50
|
|
|
|
|
|
|
sub http_version { |
51
|
11
|
|
|
11
|
0
|
66
|
my $self = shift; |
52
|
11
|
50
|
|
|
|
59
|
if (@_) { |
53
|
11
|
|
|
|
|
138
|
return $self->{http_version} = shift; |
54
|
|
|
|
|
|
|
} else { |
55
|
0
|
|
|
|
|
0
|
return $self->{http_version}; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# set on or off to enable or disable persistent connection |
60
|
|
|
|
|
|
|
sub keepalive { |
61
|
13
|
|
|
13
|
0
|
45007
|
my $self = shift; |
62
|
13
|
50
|
|
|
|
77
|
if (@_) { |
63
|
13
|
100
|
|
|
|
127
|
$self->{keepalive} = shift() ? 1 : 0; |
64
|
|
|
|
|
|
|
} |
65
|
13
|
|
|
|
|
63
|
return $self->{keepalive}; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# construct and send a request |
69
|
|
|
|
|
|
|
sub request { |
70
|
136
|
|
|
136
|
0
|
131709
|
my $self = shift; |
71
|
136
|
50
|
|
|
|
624
|
return undef unless $self->{server}; |
72
|
|
|
|
|
|
|
|
73
|
136
|
100
|
|
|
|
656
|
my $opts = ref $_[0] eq "HASH" ? shift : {}; |
74
|
136
|
|
|
|
|
1065
|
my $opt_headers = delete $opts->{'headers'}; |
75
|
136
|
|
|
|
|
5100
|
my $opt_host = delete $opts->{'host'}; |
76
|
136
|
|
|
|
|
337
|
my $opt_method = delete $opts->{'method'}; |
77
|
136
|
|
|
|
|
320
|
my $opt_content = delete $opts->{'content'}; |
78
|
136
|
|
|
|
|
339
|
my $opt_extra_rn = delete $opts->{'extra_rn'}; |
79
|
136
|
|
|
|
|
337
|
my $opt_return_reader = delete $opts->{'return_reader'}; |
80
|
136
|
|
|
|
|
397
|
my $opt_post_header_pause = delete $opts->{'post_header_pause'}; |
81
|
136
|
50
|
|
|
|
1335
|
die "Bogus options: " . join(", ", keys %$opts) if %$opts; |
82
|
|
|
|
|
|
|
|
83
|
136
|
|
|
|
|
355
|
my $cmds = join(',', map { eurl($_) } @_); |
|
138
|
|
|
|
|
1336
|
|
84
|
136
|
50
|
|
|
|
474
|
return undef unless $cmds; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
# keep-alive header if 1.0, also means add content-length header |
87
|
136
|
|
|
|
|
308
|
my $headers = ''; |
88
|
136
|
100
|
|
|
|
533
|
if ($self->{keepalive}) { |
89
|
131
|
|
|
|
|
1778
|
$headers .= "Connection: keep-alive\r\n"; |
90
|
|
|
|
|
|
|
} else { |
91
|
5
|
|
|
|
|
21
|
$headers .= "Connection: close\r\n"; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
136
|
100
|
|
|
|
392
|
if ($opt_headers) { |
95
|
5
|
|
|
|
|
13
|
$headers .= $opt_headers; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
136
|
100
|
66
|
|
|
1517
|
if (my $hostname = $opt_host || $self->{host}) { |
99
|
17
|
|
|
|
|
45
|
$headers .= "Host: $hostname\r\n"; |
100
|
|
|
|
|
|
|
} |
101
|
136
|
|
100
|
|
|
581
|
my $method = $opt_method || "GET"; |
102
|
136
|
|
|
|
|
313
|
my $body = ""; |
103
|
|
|
|
|
|
|
|
104
|
136
|
100
|
|
|
|
370
|
if ($opt_content) { |
105
|
74
|
|
|
|
|
220
|
$headers .= "Content-Length: " . length($opt_content) . "\r\n"; |
106
|
74
|
|
|
|
|
131
|
$body = $opt_content; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
136
|
100
|
|
|
|
354
|
if ($opt_extra_rn) { |
110
|
36
|
|
|
|
|
92
|
$body .= "\r\n"; # some browsers on POST send an extra \r\n that's not part of content-length |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
136
|
|
|
|
|
1621
|
my $send = "$method /$cmds HTTP/$self->{http_version}\r\n$headers\r\n"; |
114
|
|
|
|
|
|
|
|
115
|
136
|
100
|
|
|
|
398
|
unless ($opt_post_header_pause) { |
116
|
100
|
|
|
|
|
1027
|
$send .= $body; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
136
|
|
|
|
|
406
|
my $len = length $send; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# send setup |
122
|
136
|
|
|
|
|
287
|
my $rv; |
123
|
136
|
|
|
|
|
490
|
my $sock = delete $self->{_sock}; |
124
|
136
|
50
|
|
|
|
955
|
local $SIG{'PIPE'} = "IGNORE" unless $FLAG_NOSIGNAL; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
### send it cached |
127
|
136
|
100
|
|
|
|
555
|
if ($sock) { |
128
|
123
|
|
|
|
|
21210
|
$rv = send($sock, $send, $FLAG_NOSIGNAL); |
129
|
123
|
50
|
33
|
|
|
1466
|
if ($! || ! defined $rv) { |
|
|
50
|
|
|
|
|
|
130
|
0
|
|
|
|
|
0
|
undef $self->{_sock}; |
131
|
|
|
|
|
|
|
} elsif ($rv != $len) { |
132
|
0
|
|
|
|
|
0
|
return undef; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# failing that, send it through a new socket |
137
|
136
|
100
|
|
|
|
533
|
unless ($rv) { |
138
|
13
|
|
|
|
|
59
|
$self->{_reqdone} = 0; |
139
|
|
|
|
|
|
|
|
140
|
13
|
50
|
|
|
|
415
|
$sock = IO::Socket::INET->new( |
141
|
|
|
|
|
|
|
PeerAddr => $self->{server}, |
142
|
|
|
|
|
|
|
Timeout => 3, |
143
|
|
|
|
|
|
|
) or return undef; |
144
|
13
|
50
|
|
|
|
57748
|
setsockopt($sock, IPPROTO_TCP, TCP_NODELAY, pack("l", 1)) or die "failed to set sockopt: $!\n"; |
145
|
|
|
|
|
|
|
|
146
|
13
|
|
|
|
|
10129
|
$rv = send($sock, $send, $FLAG_NOSIGNAL); |
147
|
13
|
50
|
33
|
|
|
312
|
if ($! || $rv != $len) { |
148
|
0
|
|
|
|
|
0
|
return undef; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
136
|
100
|
|
|
|
425
|
if ($opt_post_header_pause) { |
153
|
36
|
|
|
|
|
27410881
|
select undef, undef, undef, $opt_post_header_pause; |
154
|
36
|
|
|
|
|
217
|
my $len = length $body; |
155
|
36
|
50
|
|
|
|
257
|
if ($len) { |
156
|
36
|
|
|
|
|
18889
|
my $rv = send($sock, $body, $FLAG_NOSIGNAL); |
157
|
36
|
50
|
33
|
|
|
1028
|
if ($! || ! defined $rv) { |
|
|
50
|
|
|
|
|
|
158
|
0
|
|
|
|
|
0
|
undef $self->{_sock}; |
159
|
|
|
|
|
|
|
} elsif ($rv != $len) { |
160
|
0
|
|
|
|
|
0
|
return undef; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
my $parse_it = sub { |
166
|
136
|
|
|
136
|
|
904
|
my ($resp, $firstline) = resp_from_sock($sock); |
167
|
|
|
|
|
|
|
|
168
|
136
|
|
50
|
|
|
1965
|
my $conhdr = $resp->header("Connection") || ""; |
169
|
136
|
100
|
33
|
|
|
16703
|
if (($firstline =~ m!\bHTTP/1\.1\b! && $conhdr !~ m!\bclose\b!i) || |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
170
|
|
|
|
|
|
|
($firstline =~ m!\bHTTP/1\.0\b! && $conhdr =~ m!\bkeep-alive\b!i)) { |
171
|
130
|
|
|
|
|
1072
|
$self->{_sock} = $sock; |
172
|
130
|
|
|
|
|
460
|
$self->{_reqdone}++; |
173
|
|
|
|
|
|
|
} else { |
174
|
6
|
|
|
|
|
32
|
$self->{_reqdone} = 0; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
136
|
|
|
|
|
3393
|
return $resp; |
178
|
136
|
|
|
|
|
2524
|
}; |
179
|
|
|
|
|
|
|
|
180
|
136
|
50
|
|
|
|
668
|
if ($opt_return_reader) { |
181
|
0
|
|
|
|
|
0
|
return $parse_it; |
182
|
|
|
|
|
|
|
} else { |
183
|
136
|
|
|
|
|
474
|
return $parse_it->(); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub reqdone { |
188
|
89
|
|
|
89
|
0
|
125376
|
my $self = shift; |
189
|
89
|
|
|
|
|
616
|
return $self->{_reqdone}; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# general purpose URL escaping function |
193
|
|
|
|
|
|
|
sub eurl { |
194
|
138
|
|
|
138
|
0
|
341
|
my $a = $_[0]; |
195
|
138
|
|
|
|
|
430
|
$a =~ s/([^a-zA-Z0-9_\,\-.\/\\\: ])/uc sprintf("%%%02x",ord($1))/eg; |
|
0
|
|
|
|
|
0
|
|
196
|
138
|
|
|
|
|
352
|
$a =~ tr/ /+/; |
197
|
138
|
|
|
|
|
1866
|
return $a; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
1; |