line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package IO::Stream::HTTP::Persistent; |
2
|
6
|
|
|
6
|
|
570578
|
use 5.010001; |
|
6
|
|
|
|
|
59
|
|
3
|
6
|
|
|
6
|
|
32
|
use warnings; |
|
6
|
|
|
|
|
23
|
|
|
6
|
|
|
|
|
181
|
|
4
|
6
|
|
|
6
|
|
28
|
use strict; |
|
6
|
|
|
|
|
14
|
|
|
6
|
|
|
|
|
131
|
|
5
|
6
|
|
|
6
|
|
953
|
use utf8; |
|
6
|
|
|
|
|
32
|
|
|
6
|
|
|
|
|
33
|
|
6
|
6
|
|
|
6
|
|
127
|
use Carp; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
415
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = 'v0.2.2'; |
9
|
|
|
|
|
|
|
|
10
|
6
|
|
|
6
|
|
46
|
use Scalar::Util qw( dualvar ); |
|
6
|
|
|
|
|
17
|
|
|
6
|
|
|
|
|
299
|
|
11
|
6
|
|
|
6
|
|
2299
|
use Data::Alias 0.08; |
|
6
|
|
|
|
|
3871
|
|
|
6
|
|
|
|
|
281
|
|
12
|
6
|
|
|
6
|
|
761
|
use IO::Stream::const; |
|
6
|
|
|
|
|
13343
|
|
|
6
|
|
|
|
|
29
|
|
13
|
|
|
|
|
|
|
|
14
|
6
|
|
|
6
|
|
911
|
use constant HTTP_SENT => 1<<16; |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
268
|
|
15
|
6
|
|
|
6
|
|
33
|
use constant HTTP_RECV => 1<<17; |
|
6
|
|
|
|
|
10
|
|
|
6
|
|
|
|
|
298
|
|
16
|
|
|
|
|
|
|
|
17
|
6
|
|
|
6
|
|
29
|
use constant HTTP_EREQINCOMPLETE => dualvar(-10000, 'incomplete HTTP request headers'); |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
308
|
|
18
|
6
|
|
|
6
|
|
29
|
use constant HTTP_ERESINCOMPLETE => dualvar(-10001, 'incomplete HTTP response'); |
|
6
|
|
|
|
|
12
|
|
|
6
|
|
|
|
|
995
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# |
22
|
|
|
|
|
|
|
# Export constants. |
23
|
|
|
|
|
|
|
# |
24
|
|
|
|
|
|
|
# Usage: use IO::Stream::HTTP::Persistent qw( :ALL :DEFAULT :Event :Error HTTP_RECV ... ) |
25
|
|
|
|
|
|
|
# |
26
|
|
|
|
|
|
|
my %TAGS = ( |
27
|
|
|
|
|
|
|
Event => [ qw( HTTP_SENT HTTP_RECV ) ], |
28
|
|
|
|
|
|
|
Error => [ qw( HTTP_EREQINCOMPLETE HTTP_ERESINCOMPLETE ) ], |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
$TAGS{ALL} = $TAGS{DEFAULT} = [ map { @{$_} } values %TAGS ]; |
31
|
|
|
|
|
|
|
my %KNOWN = map { $_ => 1 } @{ $TAGS{ALL} }; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub import { |
34
|
6
|
|
|
6
|
|
41
|
my (undef, @p) = @_; |
35
|
6
|
50
|
|
|
|
31
|
if (!@p) { |
36
|
6
|
|
|
|
|
23
|
@p = (':DEFAULT'); |
37
|
|
|
|
|
|
|
} |
38
|
6
|
50
|
|
|
|
10
|
@p = map { /\A:(\w+)\z/xms ? @{ $TAGS{$1} || [] } : $_ } @p; |
|
6
|
50
|
|
|
|
36
|
|
|
6
|
|
|
|
|
46
|
|
39
|
6
|
|
|
|
|
15
|
my $pkg = caller; |
40
|
6
|
|
|
6
|
|
36
|
no strict 'refs'; |
|
6
|
|
|
|
|
9
|
|
|
6
|
|
|
|
|
7462
|
|
41
|
6
|
|
|
|
|
13
|
for my $const (@p) { |
42
|
24
|
50
|
|
|
|
51
|
next if !$KNOWN{$const}; |
43
|
24
|
|
|
|
|
34
|
*{"${pkg}::$const"} = \&{$const}; |
|
24
|
|
|
|
|
70
|
|
|
24
|
|
|
|
|
46
|
|
44
|
|
|
|
|
|
|
} |
45
|
6
|
|
|
|
|
1032
|
return; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub new { |
50
|
4
|
|
|
4
|
1
|
3289
|
my ($class) = @_; |
51
|
4
|
|
|
|
|
44
|
my $self = bless { |
52
|
|
|
|
|
|
|
out_buf => q{}, # modified on: OUT |
53
|
|
|
|
|
|
|
out_pos => undef, # modified on: OUT |
54
|
|
|
|
|
|
|
out_bytes => 0, # modified on: OUT |
55
|
|
|
|
|
|
|
in_buf => q{}, # modified on: IN |
56
|
|
|
|
|
|
|
in_bytes => 0, # modified on: IN |
57
|
|
|
|
|
|
|
ip => undef, # modified on: RESOLVED |
58
|
|
|
|
|
|
|
is_eof => undef, # modified on: EOF |
59
|
|
|
|
|
|
|
out_sizes => [], # modified on: HTTP_SENT |
60
|
|
|
|
|
|
|
in_sizes => [], # modified on: HTTP_RECV |
61
|
|
|
|
|
|
|
_out_len => 0, # current length of {out_buf} |
62
|
|
|
|
|
|
|
# used to detect how many bytes was added to |
63
|
|
|
|
|
|
|
# {out_buf} in write() and increase {_out_todo} |
64
|
|
|
|
|
|
|
_out_todo => 0, # size of incomplete request at end of {out_buf} |
65
|
|
|
|
|
|
|
# used to find complete requests appended to |
66
|
|
|
|
|
|
|
# {out_buf} and add their sizes to {_out_queue} |
67
|
|
|
|
|
|
|
# can be negative, if we detected size |
68
|
|
|
|
|
|
|
# of next request but it isn't appended to |
69
|
|
|
|
|
|
|
# {out_buf} completely yet |
70
|
|
|
|
|
|
|
_out_queue => [], # sizes of unsent complete requests in {out_buf} |
71
|
|
|
|
|
|
|
# will be moved to {out_sizes} after sending |
72
|
|
|
|
|
|
|
_out_sent => 0, # how many bytes of {_out_queue}[0] already sent |
73
|
|
|
|
|
|
|
# if it become >= {_out_queue}[0] then it's |
74
|
|
|
|
|
|
|
# time to move from {_out_queue} to {out_sizes} |
75
|
|
|
|
|
|
|
_out_broken => 0, # if true, disable HTTP_SENT and {out_sizes} support |
76
|
|
|
|
|
|
|
_in_todo => 0, # size of incomplete response at end of {in_buf} |
77
|
|
|
|
|
|
|
# used to find complete responses appended to |
78
|
|
|
|
|
|
|
# {in_buf} and add their sizes to {in_sizes} |
79
|
|
|
|
|
|
|
_wait_eof => 0, # flag: response end expected on EOF |
80
|
|
|
|
|
|
|
_wait_length=> 0, # expected response length |
81
|
|
|
|
|
|
|
_wait_chunk => 0, # known partial response length before next |
82
|
|
|
|
|
|
|
# chunk header (or end of response sign) |
83
|
|
|
|
|
|
|
}, $class; |
84
|
4
|
|
|
|
|
35
|
return $self; |
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub PREPARE { |
88
|
4
|
|
|
4
|
0
|
511
|
my ($self, $fh, $host, $port) = @_; |
89
|
4
|
|
|
|
|
11
|
for (qw( out_buf out_pos in_buf ip is_eof )) { |
90
|
20
|
|
|
|
|
52
|
alias $self->{$_} = $self->{_master}->{$_}; |
91
|
|
|
|
|
|
|
} |
92
|
4
|
|
|
|
|
25
|
$self->{_slave}->PREPARE($fh, $host, $port); |
93
|
4
|
|
|
|
|
33
|
return; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub WRITE { |
97
|
4
|
|
|
4
|
|
44
|
my ($self) = @_; |
98
|
4
|
|
|
|
|
16
|
my $m = $self->{_master}; |
99
|
|
|
|
|
|
|
|
100
|
4
|
|
|
|
|
10
|
my $l = length $self->{out_buf}; |
101
|
4
|
|
|
|
|
8
|
$self->{_out_todo} += $l - $self->{_out_len}; |
102
|
4
|
|
|
|
|
7
|
$self->{_out_len} = $l; |
103
|
|
|
|
|
|
|
|
104
|
4
|
|
66
|
|
|
38
|
while (!$self->{_out_broken} && $self->{_out_todo} > 0) { |
105
|
6
|
|
|
|
|
22
|
pos $self->{out_buf} = $self->{_out_len} - $self->{_out_todo}; |
106
|
6
|
50
|
|
|
|
50
|
if ($self->{out_buf} =~ /\G((?:[^\r\n]+\r?\n)+\r?\n)/xms) { |
107
|
6
|
|
|
|
|
29
|
my $h = $1; |
108
|
6
|
50
|
|
|
|
26
|
my $c_len = $h =~ /^Content-Length:\s*(\d+)\s*\n/ixms ? $1 : 0; |
109
|
6
|
|
|
|
|
14
|
my $size = length($h) + $c_len; |
110
|
6
|
|
|
|
|
8
|
push @{ $self->{_out_queue} }, $size; |
|
6
|
|
|
|
|
13
|
|
111
|
6
|
|
|
|
|
24
|
$self->{_out_todo} -= $size; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
else { |
114
|
0
|
|
|
|
|
0
|
$self->{_out_broken} = 1; |
115
|
0
|
|
|
|
|
0
|
$m->EVENT(0, HTTP_EREQINCOMPLETE); |
116
|
0
|
|
|
|
|
0
|
last; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
4
|
|
|
|
|
22
|
$self->{_slave}->WRITE(); |
121
|
4
|
|
|
|
|
29
|
return; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub EVENT { ## no critic (ProhibitExcessComplexity) |
125
|
13
|
|
|
13
|
0
|
2358
|
my ($self, $e, $err) = @_; |
126
|
13
|
|
|
|
|
25
|
my $m = $self->{_master}; |
127
|
|
|
|
|
|
|
|
128
|
13
|
100
|
|
|
|
38
|
if ($e & OUT) { |
129
|
4
|
|
|
|
|
10
|
$self->{_out_len} = length $self->{out_buf}; |
130
|
|
|
|
|
|
|
|
131
|
4
|
|
|
|
|
8
|
$m->{out_bytes} += $self->{out_bytes}; |
132
|
4
|
|
|
|
|
32
|
$self->{_out_sent} += $self->{out_bytes}; |
133
|
4
|
|
|
|
|
7
|
$self->{out_bytes} = 0; |
134
|
|
|
|
|
|
|
|
135
|
4
|
|
66
|
|
|
6
|
while (@{ $self->{_out_queue} } && $self->{_out_sent} >= $self->{_out_queue}[0]) { |
|
10
|
|
|
|
|
38
|
|
136
|
6
|
|
|
|
|
10
|
$e |= HTTP_SENT; |
137
|
6
|
|
|
|
|
11
|
$self->{_out_sent} -= $self->{_out_queue}[0]; |
138
|
6
|
|
|
|
|
14
|
push @{ $self->{out_sizes} }, shift @{ $self->{_out_queue} }; |
|
6
|
|
|
|
|
11
|
|
|
6
|
|
|
|
|
12
|
|
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
13
|
100
|
|
|
|
28
|
if ($e & IN) { |
143
|
4
|
|
|
|
|
10
|
$m->{in_bytes} += $self->{in_bytes}; |
144
|
4
|
|
|
|
|
8
|
$self->{_in_todo} += $self->{in_bytes}; |
145
|
4
|
|
|
|
|
8
|
$self->{in_bytes} = 0; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
13
|
|
|
|
|
32
|
while ($self->{_in_todo} > 0) { |
149
|
6
|
50
|
|
|
|
21
|
if ($e & IN) { |
150
|
6
|
50
|
33
|
|
|
57
|
if (!$self->{_wait_eof} && !$self->{_wait_length} && !$self->{_wait_chunk}) { |
|
|
|
33
|
|
|
|
|
151
|
6
|
|
|
|
|
21
|
pos $self->{in_buf} = length($self->{in_buf}) - $self->{_in_todo}; |
152
|
6
|
50
|
|
|
|
48
|
if ($self->{in_buf} =~ /\G((?:[^\r\n]+\r?\n)+\r?\n)/xms) { |
153
|
6
|
|
|
|
|
16
|
my $h = $1; |
154
|
6
|
50
|
|
|
|
30
|
if ($h =~ /^Content-Length:\s*(\d+)\s*\n/ixms) { |
|
|
0
|
|
|
|
|
|
155
|
6
|
|
|
|
|
19
|
$self->{_wait_length} = length($h) + $1; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
elsif ($h =~ /^Transfer-Encoding:\s*chunked\s*\n/ixms) { |
158
|
0
|
|
|
|
|
0
|
$self->{_wait_chunk} = length $h; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
else { |
161
|
0
|
|
|
|
|
0
|
$self->{_wait_eof} = 1; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
6
|
|
33
|
|
|
26
|
while ($self->{_wait_chunk} && $self->{_in_todo} > $self->{_wait_chunk}) { |
166
|
0
|
|
|
|
|
0
|
pos $self->{in_buf} = length($self->{in_buf}) - $self->{_in_todo} + $self->{_wait_chunk}; |
167
|
0
|
0
|
|
|
|
0
|
if ($self->{in_buf} =~ /\G((?:\r?\n)?([\dA-Fa-f]+)[ \t]*\r?\n)/xms) { |
168
|
0
|
|
|
|
|
0
|
my $chunk = hex $2; |
169
|
0
|
|
|
|
|
0
|
$self->{_wait_chunk} += length($1) + $chunk; |
170
|
0
|
0
|
|
|
|
0
|
next if $chunk > 0; |
171
|
0
|
|
|
|
|
0
|
$self->{_wait_length} = $self->{_wait_chunk}; |
172
|
0
|
|
|
|
|
0
|
$self->{_wait_chunk} = 0; |
173
|
|
|
|
|
|
|
} |
174
|
0
|
|
|
|
|
0
|
last; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} |
177
|
6
|
50
|
|
|
|
15
|
if ($e & EOF) { |
178
|
0
|
0
|
|
|
|
0
|
if ($self->{_wait_eof}) { |
179
|
0
|
|
|
|
|
0
|
$self->{_wait_length} = $self->{_in_todo}; |
180
|
0
|
|
|
|
|
0
|
$self->{_wait_eof} = 0; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
6
|
50
|
33
|
|
|
25
|
if ($self->{_wait_length} && $self->{_in_todo} >= $self->{_wait_length}) { |
184
|
6
|
|
|
|
|
9
|
$self->{_in_todo} -= $self->{_wait_length}; |
185
|
6
|
|
|
|
|
8
|
push @{ $self->{in_sizes} }, $self->{_wait_length}; |
|
6
|
|
|
|
|
11
|
|
186
|
6
|
|
|
|
|
24
|
$self->{_wait_length} = 0; |
187
|
6
|
|
|
|
|
14
|
$e |= HTTP_RECV; |
188
|
6
|
|
|
|
|
13
|
next; |
189
|
|
|
|
|
|
|
} |
190
|
0
|
|
|
|
|
0
|
last; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
13
|
100
|
|
|
|
44
|
if ($e & EOF) { |
194
|
1
|
50
|
|
|
|
3
|
if ($self->{_in_todo}) { |
195
|
0
|
|
0
|
|
|
0
|
$err ||= HTTP_ERESINCOMPLETE; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
13
|
|
|
|
|
44
|
$m->EVENT($e, $err); |
200
|
13
|
|
|
|
|
6613
|
return; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
205
|
|
|
|
|
|
|
__END__ |