line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTTP::Daemon; # git description: v6.13-4-ge6492b6 |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: A simple http server class |
4
|
|
|
|
|
|
|
|
5
|
5
|
|
|
5
|
|
176067
|
use strict; |
|
5
|
|
|
|
|
52
|
|
|
5
|
|
|
|
|
128
|
|
6
|
5
|
|
|
5
|
|
20
|
use warnings; |
|
5
|
|
|
|
|
5
|
|
|
5
|
|
|
|
|
174
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '6.14'; |
9
|
|
|
|
|
|
|
|
10
|
5
|
|
|
5
|
|
2315
|
use Socket (); |
|
5
|
|
|
|
|
16514
|
|
|
5
|
|
|
|
|
171
|
|
11
|
5
|
|
|
5
|
|
2672
|
use IO::Socket::IP; |
|
5
|
|
|
|
|
146176
|
|
|
5
|
|
|
|
|
24
|
|
12
|
|
|
|
|
|
|
our @ISA = qw(IO::Socket::IP); |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $PROTO = "HTTP/1.1"; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $DEBUG; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub new { |
19
|
4
|
|
|
4
|
1
|
5049
|
my ($class, %args) = @_; |
20
|
4
|
|
50
|
|
|
176
|
$args{Listen} ||= 5; |
21
|
4
|
|
50
|
|
|
151
|
$args{Proto} ||= 'tcp'; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Handle undefined or empty local address the same way as |
24
|
|
|
|
|
|
|
# IO::Socket::INET -- use unspecified address |
25
|
4
|
|
|
|
|
26
|
for my $key (qw(LocalAddr LocalHost)) { |
26
|
8
|
50
|
33
|
|
|
125
|
if (exists $args{$key} && (!defined $args{$key} || $args{$key} eq '')) { |
|
|
|
66
|
|
|
|
|
27
|
0
|
|
|
|
|
0
|
delete $args{$key}; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
} |
30
|
4
|
|
|
|
|
205
|
return $class->SUPER::new(%args); |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub accept { |
34
|
6
|
|
|
6
|
1
|
4698
|
my $self = shift; |
35
|
6
|
|
50
|
|
|
144
|
my $pkg = shift || "HTTP::Daemon::ClientConn"; |
36
|
6
|
|
|
|
|
99
|
my ($sock, $peer) = $self->SUPER::accept($pkg); |
37
|
6
|
50
|
|
|
|
4012297
|
if ($sock) { |
38
|
6
|
|
|
|
|
16
|
${*$sock}{'httpd_daemon'} = $self; |
|
6
|
|
|
|
|
55
|
|
39
|
6
|
50
|
|
|
|
53
|
return wantarray ? ($sock, $peer) : $sock; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
else { |
42
|
0
|
|
|
|
|
0
|
return; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub url { |
47
|
9
|
|
|
9
|
1
|
2953
|
my $self = shift; |
48
|
|
|
|
|
|
|
|
49
|
9
|
|
|
|
|
69
|
my $host = $self->sockhost; |
50
|
9
|
|
|
|
|
676
|
$host =~ s/%/%25/g; |
51
|
9
|
100
|
|
|
|
53
|
$host = "127.0.0.1" if $host eq "0.0.0.0"; |
52
|
9
|
50
|
|
|
|
36
|
$host = "::1" if $host eq "::"; |
53
|
9
|
100
|
|
|
|
55
|
$host = "[$host]" if $self->sockdomain == Socket::AF_INET6; |
54
|
|
|
|
|
|
|
|
55
|
9
|
|
|
|
|
147
|
my $url = $self->_default_scheme . "://" . $host; |
56
|
9
|
|
|
|
|
52
|
my $port = $self->sockport; |
57
|
9
|
50
|
|
|
|
388
|
$url .= ":$port" if $port != $self->_default_port; |
58
|
9
|
|
|
|
|
16
|
$url .= "/"; |
59
|
9
|
|
|
|
|
234
|
$url; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub _default_port { |
63
|
9
|
|
|
9
|
|
73
|
80; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub _default_scheme { |
67
|
9
|
|
|
9
|
|
58
|
"http"; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub product_tokens { |
71
|
6
|
|
|
6
|
1
|
17
|
"libwww-perl-daemon/$HTTP::Daemon::VERSION"; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
package # hide from PAUSE |
75
|
|
|
|
|
|
|
HTTP::Daemon::ClientConn; |
76
|
|
|
|
|
|
|
|
77
|
5
|
|
|
5
|
|
4259
|
use strict; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
145
|
|
78
|
5
|
|
|
5
|
|
24
|
use warnings; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
172
|
|
79
|
|
|
|
|
|
|
|
80
|
5
|
|
|
5
|
|
22
|
use IO::Socket::IP (); |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
284
|
|
81
|
|
|
|
|
|
|
our @ISA = qw(IO::Socket::IP); |
82
|
|
|
|
|
|
|
our $DEBUG; |
83
|
|
|
|
|
|
|
*DEBUG = \$HTTP::Daemon::DEBUG; |
84
|
|
|
|
|
|
|
|
85
|
5
|
|
|
5
|
|
2653
|
use HTTP::Request (); |
|
5
|
|
|
|
|
85566
|
|
|
5
|
|
|
|
|
113
|
|
86
|
5
|
|
|
5
|
|
2155
|
use HTTP::Response (); |
|
5
|
|
|
|
|
29734
|
|
|
5
|
|
|
|
|
130
|
|
87
|
5
|
|
|
5
|
|
38
|
use HTTP::Status; |
|
5
|
|
|
|
|
18
|
|
|
5
|
|
|
|
|
1044
|
|
88
|
5
|
|
|
5
|
|
2117
|
use HTTP::Date qw(time2str); |
|
5
|
|
|
|
|
19013
|
|
|
5
|
|
|
|
|
314
|
|
89
|
5
|
|
|
5
|
|
2107
|
use LWP::MediaTypes qw(guess_media_type); |
|
5
|
|
|
|
|
69115
|
|
|
5
|
|
|
|
|
374
|
|
90
|
5
|
|
|
5
|
|
40
|
use Carp (); |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
16499
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# "\r\n" is not portable |
93
|
|
|
|
|
|
|
my $CRLF = "\015\012"; |
94
|
|
|
|
|
|
|
my $HTTP_1_0 = _http_version("HTTP/1.0"); |
95
|
|
|
|
|
|
|
my $HTTP_1_1 = _http_version("HTTP/1.1"); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub get_request { |
99
|
6
|
|
|
6
|
|
150
|
my ($self, $only_headers) = @_; |
100
|
6
|
50
|
|
|
|
30
|
if (${*$self}{'httpd_nomore'}) { |
|
6
|
|
|
|
|
47
|
|
101
|
0
|
|
|
|
|
0
|
$self->reason("No more requests from this connection"); |
102
|
0
|
|
|
|
|
0
|
return; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
6
|
|
|
|
|
119
|
$self->reason(""); |
106
|
6
|
|
|
|
|
10
|
my $buf = ${*$self}{'httpd_rbuf'}; |
|
6
|
|
|
|
|
35
|
|
107
|
6
|
50
|
|
|
|
24
|
$buf = "" unless defined $buf; |
108
|
|
|
|
|
|
|
|
109
|
6
|
|
|
|
|
18
|
my $timeout = ${*$self}{'io_socket_timeout'}; |
|
6
|
|
|
|
|
28
|
|
110
|
6
|
|
|
|
|
48
|
my $fdset = ""; |
111
|
6
|
|
|
|
|
90
|
vec($fdset, $self->fileno, 1) = 1; |
112
|
6
|
|
|
|
|
111
|
local ($_); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
READ_HEADER: |
115
|
6
|
|
|
|
|
13
|
while (1) { |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# loop until we have the whole header in $buf |
118
|
12
|
|
|
|
|
114
|
$buf =~ s/^(?:\015?\012)+//; # ignore leading blank lines |
119
|
12
|
100
|
|
|
|
113
|
if ($buf =~ /\012/) { # potential, has at least one line |
|
|
50
|
|
|
|
|
|
120
|
6
|
50
|
|
|
|
57
|
if ($buf =~ /^\w+[^\012]+HTTP\/\d+\.\d+\015?\012/) { |
121
|
6
|
50
|
|
|
|
50
|
if ($buf =~ /\015?\012\015?\012/) { |
|
|
0
|
|
|
|
|
|
122
|
6
|
|
|
|
|
32
|
last READ_HEADER; # we have it |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
elsif (length($buf) > 16 * 1024) { |
125
|
0
|
|
|
|
|
0
|
$self->send_error(413); # REQUEST_ENTITY_TOO_LARGE |
126
|
0
|
|
|
|
|
0
|
$self->reason("Very long header"); |
127
|
0
|
|
|
|
|
0
|
return; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
else { |
131
|
0
|
|
|
|
|
0
|
last READ_HEADER; # HTTP/0.9 client |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
elsif (length($buf) > 16 * 1024) { |
135
|
0
|
|
|
|
|
0
|
$self->send_error(414); # REQUEST_URI_TOO_LARGE |
136
|
0
|
|
|
|
|
0
|
$self->reason("Very long first line"); |
137
|
0
|
|
|
|
|
0
|
return; |
138
|
|
|
|
|
|
|
} |
139
|
6
|
50
|
|
|
|
34
|
print STDERR "Need more data for complete header\n" if $DEBUG; |
140
|
6
|
50
|
|
|
|
62
|
return unless $self->_need_more($buf, $timeout, $fdset); |
141
|
|
|
|
|
|
|
} |
142
|
6
|
50
|
|
|
|
75
|
if ($buf !~ s/^(\S+)[ \t]+(\S+)(?:[ \t]+(HTTP\/\d+\.\d+))?[^\012]*\012//) { |
143
|
0
|
|
|
|
|
0
|
${*$self}{'httpd_client_proto'} = _http_version("HTTP/1.0"); |
|
0
|
|
|
|
|
0
|
|
144
|
0
|
|
|
|
|
0
|
$self->send_error(400); # BAD_REQUEST |
145
|
0
|
|
|
|
|
0
|
$self->reason("Bad request line: $buf"); |
146
|
0
|
|
|
|
|
0
|
return; |
147
|
|
|
|
|
|
|
} |
148
|
6
|
|
|
|
|
47
|
my $method = $1; |
149
|
6
|
|
|
|
|
42
|
my $uri = $2; |
150
|
6
|
|
50
|
|
|
33
|
my $proto = $3 || "HTTP/0.9"; |
151
|
6
|
50
|
|
|
|
27
|
$uri = "http://$uri" if $method eq "CONNECT"; |
152
|
6
|
|
|
|
|
70
|
$uri = $HTTP::URI_CLASS->new($uri, $self->daemon->url); |
153
|
6
|
|
|
|
|
21225
|
my $r = HTTP::Request->new($method, $uri); |
154
|
6
|
|
|
|
|
844
|
$r->protocol($proto); |
155
|
6
|
|
|
|
|
59
|
${*$self}{'httpd_client_proto'} = $proto = _http_version($proto); |
|
6
|
|
|
|
|
32
|
|
156
|
6
|
|
|
|
|
11
|
${*$self}{'httpd_head'} = ($method eq "HEAD"); |
|
6
|
|
|
|
|
22
|
|
157
|
|
|
|
|
|
|
|
158
|
6
|
50
|
|
|
|
15
|
if ($proto >= $HTTP_1_0) { |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# we expect to find some headers |
161
|
6
|
|
|
|
|
16
|
my ($key, $val); |
162
|
|
|
|
|
|
|
HEADER: |
163
|
6
|
|
|
|
|
64
|
while ($buf =~ s/^([^\012]*)\012//) { |
164
|
29
|
|
|
|
|
55
|
$_ = $1; |
165
|
29
|
|
|
|
|
107
|
s/\015$//; |
166
|
29
|
100
|
|
|
|
100
|
if (/^([^:\s]+)\s*:\s*(.*)/) { |
|
|
50
|
|
|
|
|
|
167
|
23
|
100
|
|
|
|
105
|
$r->push_header($key, $val) if $key; |
168
|
23
|
|
|
|
|
956
|
($key, $val) = ($1, $2); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
elsif (/^\s+(.*)/) { |
171
|
0
|
|
|
|
|
0
|
$val .= " $1"; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
else { |
174
|
6
|
|
|
|
|
14
|
last HEADER; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
6
|
50
|
|
|
|
20
|
$r->push_header($key, $val) if $key; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
6
|
|
|
|
|
197
|
my $conn = $r->header('Connection'); |
181
|
6
|
50
|
|
|
|
387
|
if ($proto >= $HTTP_1_1) { |
182
|
6
|
100
|
66
|
|
|
36
|
${*$self}{'httpd_nomore'}++ if $conn && lc($conn) =~ /\bclose\b/; |
|
1
|
|
|
|
|
6
|
|
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
else { |
185
|
0
|
0
|
0
|
|
|
0
|
${*$self}{'httpd_nomore'}++ |
|
0
|
|
|
|
|
0
|
|
186
|
|
|
|
|
|
|
unless $conn && lc($conn) =~ /\bkeep-alive\b/; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
6
|
50
|
|
|
|
10
|
if ($only_headers) { |
190
|
0
|
|
|
|
|
0
|
${*$self}{'httpd_rbuf'} = $buf; |
|
0
|
|
|
|
|
0
|
|
191
|
0
|
|
|
|
|
0
|
return $r; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# Find out how much content to read |
195
|
6
|
|
|
|
|
17
|
my $te = $r->header('Transfer-Encoding'); |
196
|
6
|
|
|
|
|
207
|
my $ct = $r->header('Content-Type'); |
197
|
6
|
|
|
|
|
202
|
my $len = $r->header('Content-Length'); |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# Act on the Expect header, if it's there |
200
|
6
|
|
|
|
|
194
|
for my $e ($r->header('Expect')) { |
201
|
0
|
0
|
|
|
|
0
|
if (lc($e) eq '100-continue') { |
202
|
0
|
|
|
|
|
0
|
$self->send_status_line(100); |
203
|
0
|
|
|
|
|
0
|
$self->send_crlf; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
else { |
206
|
0
|
|
|
|
|
0
|
$self->send_error(417); |
207
|
0
|
|
|
|
|
0
|
$self->reason("Unsupported Expect header value"); |
208
|
0
|
|
|
|
|
0
|
return; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
6
|
100
|
66
|
|
|
238
|
if ($te && lc($te) eq 'chunked') { |
|
|
50
|
33
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# Handle chunked transfer encoding |
215
|
4
|
|
|
|
|
7
|
my $body = ""; |
216
|
|
|
|
|
|
|
CHUNK: |
217
|
4
|
|
|
|
|
7
|
while (1) { |
218
|
1031
|
50
|
|
|
|
1525
|
print STDERR "Chunked\n" if $DEBUG; |
219
|
1031
|
50
|
|
|
|
3364
|
if ($buf =~ s/^([^\012]*)\012//) { |
220
|
1031
|
|
|
|
|
1851
|
my $chunk_head = $1; |
221
|
1031
|
50
|
|
|
|
2048
|
unless ($chunk_head =~ /^([0-9A-Fa-f]+)/) { |
222
|
0
|
|
|
|
|
0
|
$self->send_error(400); |
223
|
0
|
|
|
|
|
0
|
$self->reason("Bad chunk header $chunk_head"); |
224
|
0
|
|
|
|
|
0
|
return; |
225
|
|
|
|
|
|
|
} |
226
|
1031
|
|
|
|
|
1473
|
my $size = hex($1); |
227
|
1031
|
100
|
|
|
|
1347
|
last CHUNK if $size == 0; |
228
|
|
|
|
|
|
|
|
229
|
1027
|
|
|
|
|
1156
|
my $missing = $size - length($buf) + 2; # 2=CRLF at chunk end |
230
|
|
|
|
|
|
|
# must read until we have a complete chunk |
231
|
1027
|
|
|
|
|
1455
|
while ($missing > 0) { |
232
|
555
|
50
|
|
|
|
805
|
print STDERR "Need $missing more bytes\n" if $DEBUG; |
233
|
555
|
|
|
|
|
922
|
my $n = $self->_need_more($buf, $timeout, $fdset); |
234
|
555
|
50
|
|
|
|
917
|
return unless $n; |
235
|
555
|
|
|
|
|
904
|
$missing -= $n; |
236
|
|
|
|
|
|
|
} |
237
|
1027
|
|
|
|
|
3374
|
$body .= substr($buf, 0, $size); |
238
|
1027
|
|
|
|
|
1725
|
substr($buf, 0, $size + 2) = ''; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
else { |
242
|
|
|
|
|
|
|
# need more data in order to have a complete chunk header |
243
|
0
|
0
|
|
|
|
0
|
return unless $self->_need_more($buf, $timeout, $fdset); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
} |
246
|
4
|
|
|
|
|
14
|
$r->content($body); |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# pretend it was a normal entity body |
249
|
4
|
|
|
|
|
1033
|
$r->remove_header('Transfer-Encoding'); |
250
|
4
|
|
|
|
|
147
|
$r->header('Content-Length', length($body)); |
251
|
|
|
|
|
|
|
|
252
|
4
|
|
|
|
|
167
|
my ($key, $val); |
253
|
|
|
|
|
|
|
FOOTER: |
254
|
4
|
|
|
|
|
5
|
while (1) { |
255
|
4
|
50
|
|
|
|
19
|
if ($buf !~ /\012/) { |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# need at least one line to look at |
258
|
0
|
0
|
|
|
|
0
|
return unless $self->_need_more($buf, $timeout, $fdset); |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
else { |
261
|
4
|
|
|
|
|
11
|
$buf =~ s/^([^\012]*)\012//; |
262
|
4
|
|
|
|
|
9
|
$_ = $1; |
263
|
4
|
|
|
|
|
13
|
s/\015$//; |
264
|
4
|
50
|
|
|
|
11
|
if (/^([\w\-]+)\s*:\s*(.*)/) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
265
|
0
|
0
|
|
|
|
0
|
$r->push_header($key, $val) if $key; |
266
|
0
|
|
|
|
|
0
|
($key, $val) = ($1, $2); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
elsif (/^\s+(.*)/) { |
269
|
0
|
|
|
|
|
0
|
$val .= " $1"; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
elsif (!length) { |
272
|
4
|
|
|
|
|
9
|
last FOOTER; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
else { |
275
|
0
|
|
|
|
|
0
|
$self->reason("Bad footer syntax"); |
276
|
0
|
|
|
|
|
0
|
return; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
} |
280
|
4
|
50
|
|
|
|
10
|
$r->push_header($key, $val) if $key; |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
elsif ($te) { |
284
|
0
|
|
|
|
|
0
|
$self->send_error(501); # Unknown transfer encoding |
285
|
0
|
|
|
|
|
0
|
$self->reason("Unknown transfer encoding '$te'"); |
286
|
0
|
|
|
|
|
0
|
return; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
elsif ($len) { |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# Plain body specified by "Content-Length" |
292
|
1
|
|
|
|
|
4
|
my $missing = $len - length($buf); |
293
|
1
|
|
|
|
|
4
|
while ($missing > 0) { |
294
|
0
|
0
|
|
|
|
0
|
print "Need $missing more bytes of content\n" if $DEBUG; |
295
|
0
|
|
|
|
|
0
|
my $n = $self->_need_more($buf, $timeout, $fdset); |
296
|
0
|
0
|
|
|
|
0
|
return unless $n; |
297
|
0
|
|
|
|
|
0
|
$missing -= $n; |
298
|
|
|
|
|
|
|
} |
299
|
1
|
50
|
|
|
|
3
|
if (length($buf) > $len) { |
300
|
0
|
|
|
|
|
0
|
$r->content(substr($buf, 0, $len)); |
301
|
0
|
|
|
|
|
0
|
substr($buf, 0, $len) = ''; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
else { |
304
|
1
|
|
|
|
|
5
|
$r->content($buf); |
305
|
1
|
|
|
|
|
34
|
$buf = ''; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
elsif ($ct && $ct =~ m/^multipart\/\w+\s*;.*boundary\s*=\s*("?)(\w+)\1/i) { |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# Handle multipart content type |
311
|
0
|
|
|
|
|
0
|
my $boundary = "$CRLF--$2--"; |
312
|
0
|
|
|
|
|
0
|
my $index; |
313
|
0
|
|
|
|
|
0
|
while (1) { |
314
|
0
|
|
|
|
|
0
|
$index = index($buf, $boundary); |
315
|
0
|
0
|
|
|
|
0
|
last if $index >= 0; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# end marker not yet found |
318
|
0
|
0
|
|
|
|
0
|
return unless $self->_need_more($buf, $timeout, $fdset); |
319
|
|
|
|
|
|
|
} |
320
|
0
|
|
|
|
|
0
|
$index += length($boundary); |
321
|
0
|
|
|
|
|
0
|
$r->content(substr($buf, 0, $index)); |
322
|
0
|
|
|
|
|
0
|
substr($buf, 0, $index) = ''; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
} |
325
|
6
|
|
|
|
|
11
|
${*$self}{'httpd_rbuf'} = $buf; |
|
6
|
|
|
|
|
25
|
|
326
|
|
|
|
|
|
|
|
327
|
6
|
|
|
|
|
34
|
$r; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
sub _need_more { |
331
|
561
|
|
|
561
|
|
643
|
my $self = shift; |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
#my($buf,$timeout,$fdset) = @_; |
334
|
561
|
50
|
|
|
|
741
|
if ($_[1]) { |
335
|
0
|
|
|
|
|
0
|
my ($timeout, $fdset) = @_[1, 2]; |
336
|
0
|
0
|
|
|
|
0
|
print STDERR "select(,,,$timeout)\n" if $DEBUG; |
337
|
0
|
|
|
|
|
0
|
my $n = select($fdset, undef, undef, $timeout); |
338
|
0
|
0
|
|
|
|
0
|
unless ($n) { |
339
|
0
|
0
|
|
|
|
0
|
$self->reason(defined($n) ? "Timeout" : "select: $!"); |
340
|
0
|
|
|
|
|
0
|
return; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} |
343
|
561
|
50
|
|
|
|
747
|
print STDERR "sysread()\n" if $DEBUG; |
344
|
561
|
|
|
|
|
4904
|
my $n = sysread($self, $_[0], 2048, length($_[0])); |
345
|
561
|
0
|
|
|
|
1284
|
$self->reason(defined($n) ? "Client closed" : "sysread: $!") unless $n; |
|
|
50
|
|
|
|
|
|
346
|
561
|
|
|
|
|
963
|
$n; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub read_buffer { |
350
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
351
|
0
|
|
|
|
|
0
|
my $old = ${*$self}{'httpd_rbuf'}; |
|
0
|
|
|
|
|
0
|
|
352
|
0
|
0
|
|
|
|
0
|
if (@_) { |
353
|
0
|
|
|
|
|
0
|
${*$self}{'httpd_rbuf'} = shift; |
|
0
|
|
|
|
|
0
|
|
354
|
|
|
|
|
|
|
} |
355
|
0
|
|
|
|
|
0
|
$old; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
sub reason { |
359
|
6
|
|
|
6
|
|
36
|
my $self = shift; |
360
|
6
|
|
|
|
|
18
|
my $old = ${*$self}{'httpd_reason'}; |
|
6
|
|
|
|
|
19
|
|
361
|
6
|
50
|
|
|
|
37
|
if (@_) { |
362
|
6
|
|
|
|
|
22
|
${*$self}{'httpd_reason'} = shift; |
|
6
|
|
|
|
|
84
|
|
363
|
|
|
|
|
|
|
} |
364
|
6
|
|
|
|
|
24
|
$old; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub proto_ge { |
368
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
369
|
0
|
|
|
|
|
0
|
${*$self}{'httpd_client_proto'} >= _http_version(shift); |
|
0
|
|
|
|
|
0
|
|
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub _http_version { |
373
|
16
|
|
|
16
|
|
44
|
local ($_) = shift; |
374
|
16
|
50
|
|
|
|
148
|
return 0 unless m,^(?:HTTP/)?(\d+)\.(\d+)$,i; |
375
|
16
|
|
|
|
|
70
|
$1 * 1000 + $2; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
sub antique_client { |
379
|
18
|
|
|
18
|
|
23
|
my $self = shift; |
380
|
18
|
|
|
|
|
26
|
${*$self}{'httpd_client_proto'} < $HTTP_1_0; |
|
18
|
|
|
|
|
65
|
|
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub force_last_request { |
384
|
6
|
|
|
6
|
|
30
|
my $self = shift; |
385
|
6
|
|
|
|
|
7
|
${*$self}{'httpd_nomore'}++; |
|
6
|
|
|
|
|
24
|
|
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
sub head_request { |
389
|
6
|
|
|
6
|
|
13
|
my $self = shift; |
390
|
6
|
|
|
|
|
7
|
${*$self}{'httpd_head'}; |
|
6
|
|
|
|
|
46
|
|
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
sub send_status_line { |
395
|
6
|
|
|
6
|
|
44
|
my ($self, $status, $message, $proto) = @_; |
396
|
6
|
50
|
|
|
|
20
|
return if $self->antique_client; |
397
|
6
|
|
50
|
|
|
42
|
$status ||= RC_OK; |
398
|
6
|
|
50
|
|
|
52
|
$message ||= status_message($status) || ""; |
|
|
|
33
|
|
|
|
|
399
|
6
|
|
50
|
|
|
101
|
$proto ||= $HTTP::Daemon::PROTO || "HTTP/1.1"; |
|
|
|
33
|
|
|
|
|
400
|
6
|
|
|
|
|
451
|
print $self "$proto $status $message$CRLF"; |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub send_crlf { |
404
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
405
|
0
|
|
|
|
|
0
|
print $self $CRLF; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub send_basic_header { |
409
|
6
|
|
|
6
|
|
131
|
my $self = shift; |
410
|
6
|
50
|
|
|
|
14
|
return if $self->antique_client; |
411
|
6
|
|
|
|
|
28
|
$self->send_status_line(@_); |
412
|
6
|
|
|
|
|
61
|
print $self "Date: ", time2str(time), $CRLF; |
413
|
6
|
|
|
|
|
311
|
my $product = $self->daemon->product_tokens; |
414
|
6
|
50
|
|
|
|
197
|
print $self "Server: $product$CRLF" if $product; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub send_header { |
418
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
419
|
0
|
|
|
|
|
0
|
while (@_) { |
420
|
0
|
|
|
|
|
0
|
my ($k, $v) = splice(@_, 0, 2); |
421
|
0
|
0
|
|
|
|
0
|
$v = "" unless defined($v); |
422
|
0
|
|
|
|
|
0
|
print $self "$k: $v$CRLF"; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub send_response { |
427
|
6
|
|
|
6
|
|
1955
|
my $self = shift; |
428
|
6
|
|
|
|
|
14
|
my $res = shift; |
429
|
6
|
50
|
|
|
|
17
|
if (!ref $res) { |
430
|
0
|
|
0
|
|
|
0
|
$res ||= RC_OK; |
431
|
0
|
|
|
|
|
0
|
$res = HTTP::Response->new($res, @_); |
432
|
|
|
|
|
|
|
} |
433
|
6
|
|
|
|
|
25
|
my $content = $res->content; |
434
|
6
|
|
|
|
|
60
|
my $chunked; |
435
|
6
|
50
|
|
|
|
19
|
unless ($self->antique_client) { |
436
|
6
|
|
|
|
|
49
|
my $code = $res->code; |
437
|
6
|
|
|
|
|
56
|
$self->send_basic_header($code, $res->message, $res->protocol); |
438
|
6
|
50
|
33
|
|
|
76
|
if ($code =~ /^(1\d\d|[23]04)$/) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# make sure content is empty |
441
|
0
|
|
|
|
|
0
|
$res->remove_header("Content-Length"); |
442
|
0
|
|
|
|
|
0
|
$content = ""; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
elsif ($res->request && $res->request->method eq "HEAD") { |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# probably OK |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
elsif (ref($content) eq "CODE") { |
449
|
0
|
0
|
|
|
|
0
|
if ($self->proto_ge("HTTP/1.1")) { |
450
|
0
|
|
|
|
|
0
|
$res->push_header("Transfer-Encoding" => "chunked"); |
451
|
0
|
|
|
|
|
0
|
$chunked++; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
else { |
454
|
0
|
|
|
|
|
0
|
$self->force_last_request; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
elsif (length($content)) { |
458
|
5
|
|
|
|
|
94
|
$res->header("Content-Length" => length($content)); |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
else { |
461
|
1
|
|
|
|
|
20
|
$self->force_last_request; |
462
|
1
|
|
|
|
|
6
|
$res->header('connection', 'close'); |
463
|
|
|
|
|
|
|
} |
464
|
6
|
|
|
|
|
327
|
print $self $res->headers_as_string($CRLF); |
465
|
6
|
|
|
|
|
610
|
print $self $CRLF; # separates headers and content |
466
|
|
|
|
|
|
|
} |
467
|
6
|
50
|
|
|
|
24
|
if ($self->head_request) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# no content |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
elsif (ref($content) eq "CODE") { |
472
|
0
|
|
|
|
|
0
|
while (1) { |
473
|
0
|
|
|
|
|
0
|
my $chunk = &$content(); |
474
|
0
|
0
|
0
|
|
|
0
|
last unless defined($chunk) && length($chunk); |
475
|
0
|
0
|
|
|
|
0
|
if ($chunked) { |
476
|
0
|
|
|
|
|
0
|
printf $self "%x%s%s%s", length($chunk), $CRLF, $chunk, $CRLF; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
else { |
479
|
0
|
|
|
|
|
0
|
print $self $chunk; |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
} |
482
|
0
|
0
|
|
|
|
0
|
print $self "0$CRLF$CRLF" if $chunked; # no trailers either |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
elsif (length $content) { |
485
|
5
|
|
|
|
|
145
|
print $self $content; |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
sub send_redirect { |
490
|
0
|
|
|
0
|
|
0
|
my ($self, $loc, $status, $content) = @_; |
491
|
0
|
|
0
|
|
|
0
|
$status ||= RC_MOVED_PERMANENTLY; |
492
|
0
|
0
|
|
|
|
0
|
Carp::croak("Status '$status' is not redirect") unless is_redirect($status); |
493
|
0
|
|
|
|
|
0
|
$self->send_basic_header($status); |
494
|
0
|
|
|
|
|
0
|
my $base = $self->daemon->url; |
495
|
0
|
0
|
|
|
|
0
|
$loc = $HTTP::URI_CLASS->new($loc, $base) unless ref($loc); |
496
|
0
|
|
|
|
|
0
|
$loc = $loc->abs($base); |
497
|
0
|
|
|
|
|
0
|
print $self "Location: $loc$CRLF"; |
498
|
|
|
|
|
|
|
|
499
|
0
|
0
|
|
|
|
0
|
if ($content) { |
500
|
0
|
0
|
|
|
|
0
|
my $ct = $content =~ /^\s* ? "text/html" : "text/plain"; |
501
|
0
|
|
|
|
|
0
|
print $self "Content-Type: $ct$CRLF"; |
502
|
|
|
|
|
|
|
} |
503
|
0
|
|
|
|
|
0
|
print $self $CRLF; |
504
|
0
|
0
|
0
|
|
|
0
|
print $self $content if $content && !$self->head_request; |
505
|
0
|
|
|
|
|
0
|
$self->force_last_request; # no use keeping the connection open |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub send_error { |
509
|
0
|
|
|
0
|
|
0
|
my ($self, $status, $error) = @_; |
510
|
0
|
|
0
|
|
|
0
|
$status ||= RC_BAD_REQUEST; |
511
|
0
|
0
|
|
|
|
0
|
Carp::croak("Status '$status' is not an error") unless is_error($status); |
512
|
0
|
|
|
|
|
0
|
my $mess = status_message($status); |
513
|
0
|
|
0
|
|
|
0
|
$error ||= ""; |
514
|
0
|
|
|
|
|
0
|
$mess = <
|
515
|
|
|
|
|
|
|
$status $mess |
516
|
|
|
|
|
|
|
$status $mess |
517
|
|
|
|
|
|
|
$error |
518
|
|
|
|
|
|
|
EOT |
519
|
0
|
0
|
|
|
|
0
|
unless ($self->antique_client) { |
520
|
0
|
|
|
|
|
0
|
$self->send_basic_header($status); |
521
|
0
|
|
|
|
|
0
|
print $self "Content-Type: text/html$CRLF"; |
522
|
0
|
|
|
|
|
0
|
print $self "Content-Length: " . length($mess) . $CRLF; |
523
|
0
|
|
|
|
|
0
|
print $self $CRLF; |
524
|
|
|
|
|
|
|
} |
525
|
0
|
0
|
|
|
|
0
|
print $self $mess unless $self->head_request; |
526
|
0
|
|
|
|
|
0
|
$status; |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
sub send_file_response { |
530
|
0
|
|
|
0
|
|
0
|
my ($self, $file) = @_; |
531
|
0
|
0
|
|
|
|
0
|
if (-d $file) { |
|
|
0
|
|
|
|
|
|
532
|
0
|
|
|
|
|
0
|
$self->send_dir($file); |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
elsif (-f _) { |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# plain file |
537
|
0
|
|
|
|
|
0
|
local (*F); |
538
|
0
|
0
|
|
|
|
0
|
sysopen(F, $file, 0) or return $self->send_error(RC_FORBIDDEN); |
539
|
0
|
|
|
|
|
0
|
binmode(F); |
540
|
0
|
|
|
|
|
0
|
my ($ct, $ce) = guess_media_type($file); |
541
|
0
|
|
|
|
|
0
|
my ($size, $mtime) = (stat _)[7, 9]; |
542
|
0
|
0
|
|
|
|
0
|
unless ($self->antique_client) { |
543
|
0
|
|
|
|
|
0
|
$self->send_basic_header; |
544
|
0
|
|
|
|
|
0
|
print $self "Content-Type: $ct$CRLF"; |
545
|
0
|
0
|
|
|
|
0
|
print $self "Content-Encoding: $ce$CRLF" if $ce; |
546
|
0
|
0
|
|
|
|
0
|
print $self "Content-Length: $size$CRLF" if $size; |
547
|
0
|
0
|
|
|
|
0
|
print $self "Last-Modified: ", time2str($mtime), "$CRLF" if $mtime; |
548
|
0
|
|
|
|
|
0
|
print $self $CRLF; |
549
|
|
|
|
|
|
|
} |
550
|
0
|
0
|
|
|
|
0
|
$self->send_file(\*F) unless $self->head_request; |
551
|
0
|
|
|
|
|
0
|
return RC_OK; |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
else { |
554
|
0
|
|
|
|
|
0
|
$self->send_error(RC_NOT_FOUND); |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
sub send_dir { |
559
|
0
|
|
|
0
|
|
0
|
my ($self, $dir) = @_; |
560
|
0
|
0
|
|
|
|
0
|
$self->send_error(RC_NOT_FOUND) unless -d $dir; |
561
|
0
|
|
|
|
|
0
|
$self->send_error(RC_NOT_IMPLEMENTED); |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
sub send_file { |
565
|
0
|
|
|
0
|
|
0
|
my ($self, $file) = @_; |
566
|
0
|
|
|
|
|
0
|
my $opened = 0; |
567
|
0
|
|
|
|
|
0
|
local (*FILE); |
568
|
0
|
0
|
|
|
|
0
|
if (!ref($file)) { |
569
|
0
|
0
|
|
|
|
0
|
open(FILE, $file) || return undef; |
570
|
0
|
|
|
|
|
0
|
binmode(FILE); |
571
|
0
|
|
|
|
|
0
|
$file = \*FILE; |
572
|
0
|
|
|
|
|
0
|
$opened++; |
573
|
|
|
|
|
|
|
} |
574
|
0
|
|
|
|
|
0
|
my $cnt = 0; |
575
|
0
|
|
|
|
|
0
|
my $buf = ""; |
576
|
0
|
|
|
|
|
0
|
my $n; |
577
|
0
|
|
|
|
|
0
|
while ($n = sysread($file, $buf, 8 * 1024)) { |
578
|
0
|
0
|
|
|
|
0
|
last if !$n; |
579
|
0
|
|
|
|
|
0
|
$cnt += $n; |
580
|
0
|
|
|
|
|
0
|
print $self $buf; |
581
|
|
|
|
|
|
|
} |
582
|
0
|
0
|
|
|
|
0
|
close($file) if $opened; |
583
|
0
|
|
|
|
|
0
|
$cnt; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
sub daemon { |
587
|
12
|
|
|
12
|
|
32
|
my $self = shift; |
588
|
12
|
|
|
|
|
31
|
${*$self}{'httpd_daemon'}; |
|
12
|
|
|
|
|
78
|
|
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
1; |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
__END__ |