line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTTP::Proxy; |
2
|
|
|
|
|
|
|
$HTTP::Proxy::VERSION = '0.303'; |
3
|
69
|
|
|
69
|
|
2837836
|
use HTTP::Daemon; |
|
69
|
|
|
|
|
2624626
|
|
|
69
|
|
|
|
|
835
|
|
4
|
69
|
|
|
69
|
|
30592
|
use HTTP::Date qw(time2str); |
|
69
|
|
|
|
|
102
|
|
|
69
|
|
|
|
|
2565
|
|
5
|
69
|
|
|
69
|
|
15521
|
use LWP::UserAgent; |
|
69
|
|
|
|
|
476613
|
|
|
69
|
|
|
|
|
1211
|
|
6
|
69
|
|
|
69
|
|
27749
|
use LWP::ConnCache; |
|
69
|
|
|
|
|
54750
|
|
|
69
|
|
|
|
|
1610
|
|
7
|
69
|
|
|
69
|
|
310
|
use Fcntl ':flock'; # import LOCK_* constants |
|
69
|
|
|
|
|
75
|
|
|
69
|
|
|
|
|
7774
|
|
8
|
69
|
|
|
69
|
|
100305
|
use IO::Select; |
|
69
|
|
|
|
|
70802
|
|
|
69
|
|
|
|
|
2457
|
|
9
|
69
|
|
|
69
|
|
25294
|
use Sys::Hostname; # hostname() |
|
69
|
|
|
|
|
51501
|
|
|
69
|
|
|
|
|
2802
|
|
10
|
69
|
|
|
69
|
|
309
|
use Socket qw( SOL_SOCKET SO_SNDBUF SO_RCVBUF ); |
|
69
|
|
|
|
|
73
|
|
|
69
|
|
|
|
|
3609
|
|
11
|
69
|
|
|
69
|
|
289
|
use Carp; |
|
69
|
|
|
|
|
66
|
|
|
69
|
|
|
|
|
2253
|
|
12
|
|
|
|
|
|
|
|
13
|
69
|
|
|
69
|
|
628
|
use strict; |
|
69
|
|
|
|
|
73
|
|
|
69
|
|
|
|
|
1593
|
|
14
|
69
|
|
|
|
|
6581
|
use vars qw( $VERSION @METHODS |
15
|
69
|
|
|
69
|
|
192
|
@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS ); |
|
69
|
|
|
|
|
52
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
require Exporter; |
18
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
19
|
|
|
|
|
|
|
@EXPORT = (); # no export by default |
20
|
|
|
|
|
|
|
@EXPORT_OK = qw( ERROR NONE PROXY STATUS PROCESS SOCKET HEADERS FILTERS |
21
|
|
|
|
|
|
|
DATA CONNECT ENGINE ALL ); |
22
|
|
|
|
|
|
|
%EXPORT_TAGS = ( log => [@EXPORT_OK] ); # only one tag |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $CRLF = "\015\012"; # "\r\n" is not portable |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# standard filters |
27
|
69
|
|
|
69
|
|
23180
|
use HTTP::Proxy::HeaderFilter::standard; |
|
69
|
|
|
|
|
95
|
|
|
69
|
|
|
|
|
1642
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# constants used for logging |
30
|
69
|
|
|
69
|
|
254
|
use constant ERROR => -1; # always log |
|
69
|
|
|
|
|
63
|
|
|
69
|
|
|
|
|
3581
|
|
31
|
69
|
|
|
69
|
|
219
|
use constant NONE => 0; # never log |
|
69
|
|
|
|
|
64
|
|
|
69
|
|
|
|
|
2130
|
|
32
|
69
|
|
|
69
|
|
217
|
use constant PROXY => 1; # proxy information |
|
69
|
|
|
|
|
70
|
|
|
69
|
|
|
|
|
2098
|
|
33
|
69
|
|
|
69
|
|
218
|
use constant STATUS => 2; # HTTP status |
|
69
|
|
|
|
|
75
|
|
|
69
|
|
|
|
|
1883
|
|
34
|
69
|
|
|
69
|
|
216
|
use constant PROCESS => 4; # sub-process life (and death) |
|
69
|
|
|
|
|
60
|
|
|
69
|
|
|
|
|
2210
|
|
35
|
69
|
|
|
69
|
|
219
|
use constant SOCKET => 8; # low-level connections |
|
69
|
|
|
|
|
79
|
|
|
69
|
|
|
|
|
1972
|
|
36
|
69
|
|
|
69
|
|
199
|
use constant HEADERS => 16; # HTTP headers |
|
69
|
|
|
|
|
68
|
|
|
69
|
|
|
|
|
1987
|
|
37
|
69
|
|
|
69
|
|
208
|
use constant FILTERS => 32; # Messages from filters |
|
69
|
|
|
|
|
76
|
|
|
69
|
|
|
|
|
1921
|
|
38
|
69
|
|
|
69
|
|
212
|
use constant DATA => 64; # Data received by the filters |
|
69
|
|
|
|
|
74
|
|
|
69
|
|
|
|
|
1787
|
|
39
|
69
|
|
|
69
|
|
200
|
use constant CONNECT => 128; # Data transmitted by the CONNECT method |
|
69
|
|
|
|
|
670
|
|
|
69
|
|
|
|
|
2035
|
|
40
|
69
|
|
|
69
|
|
209
|
use constant ENGINE => 256; # Internal information from the Engine |
|
69
|
|
|
|
|
60
|
|
|
69
|
|
|
|
|
1864
|
|
41
|
69
|
|
|
69
|
|
211
|
use constant ALL => 511; # All of the above |
|
69
|
|
|
|
|
68
|
|
|
69
|
|
|
|
|
1963
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# modules that need those constants to be defined |
44
|
69
|
|
|
69
|
|
21648
|
use HTTP::Proxy::Engine; |
|
69
|
|
|
|
|
105
|
|
|
69
|
|
|
|
|
1236
|
|
45
|
69
|
|
|
69
|
|
19902
|
use HTTP::Proxy::FilterStack; |
|
69
|
|
|
|
|
86
|
|
|
69
|
|
|
|
|
32220
|
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Methods we can forward |
48
|
|
|
|
|
|
|
my %METHODS; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# HTTP (RFC 2616) |
51
|
|
|
|
|
|
|
$METHODS{http} = [qw( CONNECT DELETE GET HEAD OPTIONS POST PUT TRACE )]; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# WebDAV (RFC 2518) |
54
|
|
|
|
|
|
|
$METHODS{webdav} = [ |
55
|
|
|
|
|
|
|
@{ $METHODS{http} }, |
56
|
|
|
|
|
|
|
qw( COPY LOCK MKCOL MOVE PROPFIND PROPPATCH UNLOCK ) |
57
|
|
|
|
|
|
|
]; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Delta-V (RFC 3253) |
60
|
|
|
|
|
|
|
$METHODS{deltav} = [ |
61
|
|
|
|
|
|
|
@{ $METHODS{webdav} }, |
62
|
|
|
|
|
|
|
qw( BASELINE-CONTROL CHECKIN CHECKOUT LABEL MERGE MKACTIVITY |
63
|
|
|
|
|
|
|
MKWORKSPACE REPORT UNCHECKOUT UPDATE VERSION-CONTROL ), |
64
|
|
|
|
|
|
|
]; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# the whole method list |
67
|
|
|
|
|
|
|
@METHODS = HTTP::Proxy->known_methods(); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# useful regexes (from RFC 2616 BNF grammar) |
70
|
|
|
|
|
|
|
my %RX; |
71
|
|
|
|
|
|
|
$RX{token} = qr/[-!#\$%&'*+.0-9A-Z^_`a-z|~]+/; |
72
|
|
|
|
|
|
|
$RX{mime} = qr($RX{token}/$RX{token}); |
73
|
|
|
|
|
|
|
$RX{method} = '(?:' . join ( '|', @METHODS ) . ')'; |
74
|
|
|
|
|
|
|
$RX{method} = qr/$RX{method}/; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub new { |
77
|
73
|
|
|
73
|
1
|
5107424
|
my $class = shift; |
78
|
73
|
|
|
|
|
306
|
my %params = @_; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# some defaults |
81
|
73
|
|
|
|
|
977
|
my %defaults = ( |
82
|
|
|
|
|
|
|
agent => undef, |
83
|
|
|
|
|
|
|
chunk => 4096, |
84
|
|
|
|
|
|
|
daemon => undef, |
85
|
|
|
|
|
|
|
host => 'localhost', |
86
|
|
|
|
|
|
|
logfh => *STDERR, |
87
|
|
|
|
|
|
|
logmask => NONE, |
88
|
|
|
|
|
|
|
max_connections => 0, |
89
|
|
|
|
|
|
|
max_keep_alive_requests => 10, |
90
|
|
|
|
|
|
|
port => 8080, |
91
|
|
|
|
|
|
|
stash => {}, |
92
|
|
|
|
|
|
|
timeout => 60, |
93
|
|
|
|
|
|
|
x_forwarded_for => 1, |
94
|
|
|
|
|
|
|
); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# non modifiable defaults |
97
|
73
|
|
|
|
|
357
|
my $self = bless { conn => 0, loop => 1 }, $class; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# support for deprecated stuff |
100
|
|
|
|
|
|
|
{ |
101
|
73
|
|
|
|
|
109
|
my %convert = ( |
|
73
|
|
|
|
|
1800
|
|
102
|
|
|
|
|
|
|
maxchild => 'max_clients', |
103
|
|
|
|
|
|
|
maxconn => 'max_connections', |
104
|
|
|
|
|
|
|
maxserve => 'max_keep_alive_requests', |
105
|
|
|
|
|
|
|
); |
106
|
73
|
|
|
|
|
394
|
while( my ($old, $new) = each %convert ) { |
107
|
219
|
100
|
|
|
|
751
|
if( exists $params{$old} ) { |
108
|
5
|
|
|
|
|
10
|
$params{$new} = delete $params{$old}; |
109
|
5
|
|
|
|
|
811
|
carp "$old is deprecated, please use $new"; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# get attributes |
115
|
|
|
|
|
|
|
$self->{$_} = exists $params{$_} ? delete( $params{$_} ) : $defaults{$_} |
116
|
73
|
100
|
|
|
|
1643
|
for keys %defaults; |
117
|
|
|
|
|
|
|
|
118
|
73
|
50
|
|
|
|
290
|
if (!defined $self->{via}) { |
119
|
73
|
50
|
|
|
|
421
|
$self->{via} = hostname() . ($self->{port} != 80 ? ':' . $self->{port} : '') . " (HTTP::Proxy/$VERSION)"; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# choose an engine with the remaining parameters |
123
|
73
|
|
|
|
|
1603
|
$self->{engine} = HTTP::Proxy::Engine->new( %params, proxy => $self ); |
124
|
73
|
|
|
|
|
501
|
$self->log( PROXY, "PROXY", "Selected engine " . ref $self->{engine} ); |
125
|
|
|
|
|
|
|
|
126
|
73
|
|
|
|
|
337
|
return $self; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub known_methods { |
130
|
74
|
|
|
74
|
1
|
431
|
my ( $class, @args ) = @_; |
131
|
|
|
|
|
|
|
|
132
|
74
|
100
|
|
|
|
353
|
@args = map { lc } @args ? @args : ( keys %METHODS ); |
|
214
|
|
|
|
|
399
|
|
133
|
|
|
|
|
|
|
exists $METHODS{$_} || carp "Method group $_ doesn't exist" |
134
|
74
|
|
50
|
|
|
580
|
for @args; |
135
|
74
|
|
|
|
|
206
|
my %seen; |
136
|
74
|
50
|
|
|
|
106
|
return grep { !$seen{$_}++ } map { @{ $METHODS{$_} || [] } } @args; |
|
3487
|
|
|
|
|
4667
|
|
|
214
|
|
|
|
|
124
|
|
|
214
|
|
|
|
|
852
|
|
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub timeout { |
140
|
65
|
|
|
65
|
1
|
1237
|
my $self = shift; |
141
|
65
|
|
|
|
|
112
|
my $old = $self->{timeout}; |
142
|
65
|
100
|
|
|
|
282
|
if (@_) { |
143
|
1
|
|
|
|
|
3
|
$self->{timeout} = shift; |
144
|
1
|
50
|
|
|
|
3
|
$self->agent->timeout( $self->{timeout} ) if $self->agent; |
145
|
|
|
|
|
|
|
} |
146
|
65
|
|
|
|
|
629
|
return $old; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub url { |
150
|
38
|
|
|
38
|
1
|
3098029
|
my $self = shift; |
151
|
38
|
100
|
|
|
|
299
|
if ( not defined $self->daemon ) { |
152
|
1
|
|
|
|
|
88
|
carp "HTTP daemon not started yet"; |
153
|
1
|
|
|
|
|
23
|
return undef; |
154
|
|
|
|
|
|
|
} |
155
|
37
|
|
|
|
|
150
|
return $self->daemon->url; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# normal accessors |
159
|
|
|
|
|
|
|
for my $attr ( qw( |
160
|
|
|
|
|
|
|
agent chunk daemon host logfh port request response hop_headers |
161
|
|
|
|
|
|
|
logmask via x_forwarded_for client_headers engine |
162
|
|
|
|
|
|
|
max_connections max_keep_alive_requests |
163
|
|
|
|
|
|
|
) |
164
|
|
|
|
|
|
|
) |
165
|
|
|
|
|
|
|
{ |
166
|
69
|
|
|
69
|
|
321
|
no strict 'refs'; |
|
69
|
|
|
|
|
66
|
|
|
69
|
|
|
|
|
5033
|
|
167
|
|
|
|
|
|
|
*{"HTTP::Proxy::$attr"} = sub { |
168
|
4277
|
|
|
4277
|
|
232242
|
my $self = shift; |
169
|
4277
|
|
|
|
|
7043
|
my $old = $self->{$attr}; |
170
|
4277
|
100
|
|
|
|
7017
|
$self->{$attr} = shift if @_; |
171
|
4277
|
|
|
|
|
20862
|
return $old; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# read-only accessors |
176
|
|
|
|
|
|
|
for my $attr (qw( conn loop client_socket )) { |
177
|
69
|
|
|
69
|
|
233
|
no strict 'refs'; |
|
69
|
|
|
|
|
72
|
|
|
69
|
|
|
|
|
5570
|
|
178
|
484
|
|
|
484
|
|
3187
|
*{"HTTP::Proxy::$attr"} = sub { $_[0]{$attr} } |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
4
|
|
|
4
|
1
|
2539
|
sub max_clients { shift->engine->max_clients( @_ ) } |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# deprecated methods are still supported |
184
|
|
|
|
|
|
|
{ |
185
|
|
|
|
|
|
|
my %convert = ( |
186
|
|
|
|
|
|
|
maxchild => 'max_clients', |
187
|
|
|
|
|
|
|
maxconn => 'max_connections', |
188
|
|
|
|
|
|
|
maxserve => 'max_keep_alive_requests', |
189
|
|
|
|
|
|
|
); |
190
|
|
|
|
|
|
|
while ( my ( $old, $new ) = each %convert ) { |
191
|
69
|
|
|
69
|
|
234
|
no strict 'refs'; |
|
69
|
|
|
|
|
67
|
|
|
69
|
|
|
|
|
189965
|
|
192
|
|
|
|
|
|
|
*$old = sub { |
193
|
3
|
|
|
3
|
|
287
|
carp "$old is deprecated, please use $new"; |
194
|
3
|
|
|
|
|
118
|
goto \&$new; |
195
|
|
|
|
|
|
|
}; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub stash { |
200
|
8
|
|
|
8
|
1
|
19
|
my $stash = shift->{stash}; |
201
|
8
|
100
|
|
|
|
30
|
return $stash unless @_; |
202
|
4
|
100
|
|
|
|
13
|
return $stash->{ $_[0] } if @_ == 1; |
203
|
1
|
|
|
|
|
4
|
return $stash->{ $_[0] } = $_[1]; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
6
|
|
|
6
|
1
|
61
|
sub new_connection { ++$_[0]{conn} } |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub start { |
209
|
36
|
|
|
36
|
1
|
112072
|
my $self = shift; |
210
|
|
|
|
|
|
|
|
211
|
36
|
|
|
|
|
1251
|
$self->init; |
212
|
36
|
|
|
0
|
|
2670
|
$SIG{INT} = $SIG{TERM} = sub { $self->{loop} = 0 }; |
|
0
|
|
|
|
|
0
|
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# the main loop |
215
|
36
|
|
|
|
|
896
|
my $engine = $self->engine; |
216
|
36
|
50
|
|
|
|
3341
|
$engine->start if $engine->can('start'); |
217
|
36
|
|
|
|
|
431
|
while( $self->loop ) { |
218
|
113
|
|
|
|
|
635
|
$engine->run; |
219
|
90
|
100
|
66
|
|
|
696
|
last if $self->max_connections && $self->conn >= $self->max_connections; |
220
|
|
|
|
|
|
|
} |
221
|
13
|
50
|
|
|
|
258
|
$engine->stop if $engine->can('stop'); |
222
|
|
|
|
|
|
|
|
223
|
13
|
|
|
|
|
41
|
$self->log( STATUS, "STATUS", |
224
|
|
|
|
|
|
|
"Processed " . $self->conn . " connection(s)" ); |
225
|
|
|
|
|
|
|
|
226
|
13
|
|
|
|
|
162
|
return $self->conn; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# semi-private init method |
230
|
|
|
|
|
|
|
sub init { |
231
|
122
|
|
|
122
|
1
|
547
|
my $self = shift; |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# must be run only once |
234
|
122
|
100
|
|
|
|
1161
|
return if $self->{_init}++; |
235
|
|
|
|
|
|
|
|
236
|
62
|
50
|
|
|
|
189
|
$self->_init_daemon if ( !defined $self->daemon ); |
237
|
62
|
50
|
|
|
|
179
|
$self->_init_agent if ( !defined $self->agent ); |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# specific agent config |
240
|
62
|
|
|
|
|
165
|
$self->agent->requests_redirectable( [] ); |
241
|
62
|
|
|
|
|
918
|
$self->agent->agent(''); # for TRACE support |
242
|
62
|
|
|
|
|
3090
|
$self->agent->protocols_allowed( [qw( http https ftp gopher )] ); |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# standard header filters |
245
|
62
|
|
|
|
|
885
|
$self->{headers}{request} = HTTP::Proxy::FilterStack->new; |
246
|
62
|
|
|
|
|
186
|
$self->{headers}{response} = HTTP::Proxy::FilterStack->new; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# the same standard filter is used to handle headers |
249
|
62
|
|
|
|
|
813
|
my $std = HTTP::Proxy::HeaderFilter::standard->new(); |
250
|
62
|
|
|
|
|
290
|
$std->proxy( $self ); |
251
|
62
|
|
|
75
|
|
527
|
$self->{headers}{request}->push( [ sub { 1 }, $std ] ); |
|
75
|
|
|
|
|
267
|
|
252
|
62
|
|
|
75
|
|
339
|
$self->{headers}{response}->push( [ sub { 1 }, $std ] ); |
|
75
|
|
|
|
|
293
|
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# standard body filters |
255
|
62
|
|
|
|
|
188
|
$self->{body}{request} = HTTP::Proxy::FilterStack->new(1); |
256
|
62
|
|
|
|
|
180
|
$self->{body}{response} = HTTP::Proxy::FilterStack->new(1); |
257
|
|
|
|
|
|
|
|
258
|
62
|
|
|
|
|
138
|
return; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# |
262
|
|
|
|
|
|
|
# private init methods |
263
|
|
|
|
|
|
|
# |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub _init_daemon { |
266
|
64
|
|
|
64
|
|
111
|
my $self = shift; |
267
|
64
|
|
|
|
|
174
|
my %args = ( |
268
|
|
|
|
|
|
|
LocalAddr => $self->host, |
269
|
|
|
|
|
|
|
LocalPort => $self->port, |
270
|
|
|
|
|
|
|
ReuseAddr => 1, |
271
|
|
|
|
|
|
|
); |
272
|
64
|
50
|
|
|
|
134
|
delete $args{LocalPort} unless $self->port; # 0 means autoselect |
273
|
64
|
50
|
|
|
|
486
|
my $daemon = HTTP::Daemon->new(%args) |
274
|
|
|
|
|
|
|
or die "Cannot initialize proxy daemon: $!"; |
275
|
64
|
|
|
|
|
26623
|
$self->daemon($daemon); |
276
|
|
|
|
|
|
|
|
277
|
64
|
|
|
|
|
121
|
return $daemon; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
sub _init_agent { |
281
|
64
|
|
|
64
|
|
102
|
my $self = shift; |
282
|
64
|
50
|
|
|
|
288
|
my $agent = LWP::UserAgent->new( |
283
|
|
|
|
|
|
|
env_proxy => 1, |
284
|
|
|
|
|
|
|
keep_alive => 2, |
285
|
|
|
|
|
|
|
parse_head => 0, |
286
|
|
|
|
|
|
|
timeout => $self->timeout, |
287
|
|
|
|
|
|
|
) |
288
|
|
|
|
|
|
|
or die "Cannot initialize proxy agent: $!"; |
289
|
64
|
|
|
|
|
448961
|
$self->agent($agent); |
290
|
64
|
|
|
|
|
100
|
return $agent; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# This is the internal "loop" that lets the child process process the |
294
|
|
|
|
|
|
|
# incoming connections. |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub serve_connections { |
297
|
29
|
|
|
29
|
1
|
1907
|
my ( $self, $conn ) = @_; |
298
|
29
|
|
|
|
|
193
|
my $response; |
299
|
29
|
|
|
|
|
466
|
$self->{client_socket} = $conn; # read-only |
300
|
29
|
|
|
|
|
1831
|
$self->log( SOCKET, "SOCKET", "New connection from " . $conn->peerhost |
301
|
|
|
|
|
|
|
. ":" . $conn->peerport ); |
302
|
|
|
|
|
|
|
|
303
|
29
|
|
|
|
|
238
|
my ( $last, $served ) = ( 0, 0 ); |
304
|
|
|
|
|
|
|
|
305
|
29
|
|
|
|
|
609
|
while ( $self->loop() ) { |
306
|
90
|
|
|
|
|
196
|
my $req; |
307
|
|
|
|
|
|
|
{ |
308
|
90
|
|
|
|
|
242
|
local $SIG{INT} = local $SIG{TERM} = 'DEFAULT'; |
|
90
|
|
|
|
|
1764
|
|
309
|
90
|
|
|
|
|
1038
|
$req = $conn->get_request(); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
90
|
|
|
|
|
2628589
|
$served++; |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# initialisation |
315
|
90
|
|
|
|
|
495
|
$self->request($req); |
316
|
90
|
|
|
|
|
385
|
$self->response(undef); |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# Got a request? |
319
|
90
|
100
|
|
|
|
321
|
unless ( defined $req ) { |
320
|
13
|
50
|
|
|
|
41
|
$self->log( SOCKET, "SOCKET", |
321
|
|
|
|
|
|
|
"Getting request failed: " . $conn->reason ) |
322
|
|
|
|
|
|
|
if $conn->reason ne 'No more requests from this connection'; |
323
|
13
|
|
|
|
|
267
|
return; |
324
|
|
|
|
|
|
|
} |
325
|
77
|
100
|
|
|
|
654
|
$self->log( STATUS, "REQUEST", $req->method . ' ' |
326
|
|
|
|
|
|
|
. ( $req->method eq 'CONNECT' ? $req->uri->host_port : $req->uri ) ); |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# can we forward this method? |
329
|
77
|
50
|
|
|
|
416
|
if ( !grep { $_ eq $req->method } @METHODS ) { |
|
2002
|
|
|
|
|
10673
|
|
330
|
0
|
|
|
|
|
0
|
$response = HTTP::Response->new( 501, 'Not Implemented' ); |
331
|
0
|
|
|
|
|
0
|
$response->content_type( "text/plain" ); |
332
|
0
|
|
|
|
|
0
|
$response->content( |
333
|
|
|
|
|
|
|
"Method " . $req->method . " is not supported by this proxy." ); |
334
|
0
|
|
|
|
|
0
|
$self->response($response); |
335
|
0
|
|
|
|
|
0
|
goto SEND; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# transparent proxying support |
339
|
77
|
100
|
|
|
|
929
|
if( not defined $req->uri->scheme ) { |
340
|
5
|
100
|
|
|
|
151
|
if( my $host = $req->header('Host') ) { |
341
|
4
|
|
|
|
|
132
|
$req->uri->scheme( 'http' ); |
342
|
4
|
|
|
|
|
434
|
$req->uri->host( $host ); |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
else { |
345
|
1
|
|
|
|
|
50
|
$response = HTTP::Response->new( 400, 'Bad request' ); |
346
|
1
|
|
|
|
|
120
|
$response->content_type( "text/plain" ); |
347
|
1
|
|
|
|
|
85
|
$response->content("Can't do transparent proxying without a Host: header."); |
348
|
1
|
|
|
|
|
30
|
$self->response($response); |
349
|
1
|
|
|
|
|
17
|
goto SEND; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# can we serve this protocol? |
354
|
76
|
100
|
|
|
|
3410
|
if ( !$self->is_protocol_supported( my $s = $req->uri->scheme ) ) |
355
|
|
|
|
|
|
|
{ |
356
|
|
|
|
|
|
|
# should this be 400 Bad Request? |
357
|
1
|
|
|
|
|
25
|
$response = HTTP::Response->new( 501, 'Not Implemented' ); |
358
|
1
|
|
|
|
|
130
|
$response->content_type( "text/plain" ); |
359
|
1
|
|
|
|
|
59
|
$response->content("Scheme $s is not supported by this proxy."); |
360
|
1
|
|
|
|
|
30
|
$self->response($response); |
361
|
1
|
|
|
|
|
11
|
goto SEND; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# select the request filters |
365
|
75
|
|
|
|
|
1063
|
$self->{$_}{request}->select_filters( $req ) for qw( headers body ); |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# massage the request |
368
|
75
|
|
|
|
|
333
|
$self->{headers}{request}->filter( $req->headers, $req ); |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# FIXME I don't know how to get the LWP::Protocol object... |
371
|
|
|
|
|
|
|
# NOTE: the request is always received in one piece |
372
|
75
|
|
|
|
|
515
|
$self->{body}{request}->filter( $req->content_ref, $req, undef ); |
373
|
75
|
|
|
|
|
331
|
$self->{body}{request}->eod; # end of data |
374
|
75
|
|
|
|
|
240
|
$self->log( HEADERS, "REQUEST", $req->headers->as_string ); |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# CONNECT method is a very special case |
377
|
75
|
100
|
66
|
|
|
219
|
if( ! defined $self->response and $req->method eq 'CONNECT' ) { |
378
|
1
|
|
|
|
|
19
|
$last = $self->_handle_CONNECT($served); |
379
|
1
|
50
|
|
|
|
6
|
return if $last; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# the header filters created a response, |
383
|
|
|
|
|
|
|
# we won't contact the origin server |
384
|
|
|
|
|
|
|
# FIXME should the response header and body be filtered? |
385
|
74
|
50
|
|
|
|
727
|
goto SEND if defined $self->response; |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# FIXME - don't forward requests to ourselves! |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# pop a response |
390
|
74
|
|
|
|
|
129
|
my ( $sent, $chunked ) = ( 0, 0 ); |
391
|
|
|
|
|
|
|
$response = $self->agent->simple_request( |
392
|
|
|
|
|
|
|
$req, |
393
|
|
|
|
|
|
|
sub { |
394
|
61
|
|
|
61
|
|
2377136
|
my ( $data, $response, $proto ) = @_; |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
# first time, filter the headers |
397
|
61
|
100
|
|
|
|
216
|
if ( !$sent ) { |
398
|
29
|
|
|
|
|
49
|
$sent++; |
399
|
29
|
|
|
|
|
140
|
$self->response( $response ); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# select the response filters |
402
|
|
|
|
|
|
|
$self->{$_}{response}->select_filters( $response ) |
403
|
29
|
|
|
|
|
246
|
for qw( headers body ); |
404
|
|
|
|
|
|
|
|
405
|
29
|
|
|
|
|
132
|
$self->{headers}{response} |
406
|
|
|
|
|
|
|
->filter( $response->headers, $response ); |
407
|
29
|
|
|
|
|
240
|
( $last, $chunked ) = |
408
|
|
|
|
|
|
|
$self->_send_response_headers( $served ); |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# filter and send the data |
412
|
61
|
|
|
|
|
310
|
$self->log( DATA, "DATA", |
413
|
|
|
|
|
|
|
"got " . length($data) . " bytes of body data" ); |
414
|
61
|
|
|
|
|
299
|
$self->{body}{response}->filter( \$data, $response, $proto ); |
415
|
61
|
100
|
|
|
|
164
|
if ($chunked) { |
416
|
49
|
50
|
|
|
|
1787
|
printf $conn "%x$CRLF%s$CRLF", length($data), $data |
417
|
|
|
|
|
|
|
if length($data); # the filter may leave nothing |
418
|
|
|
|
|
|
|
} |
419
|
12
|
|
|
|
|
920
|
else { print $conn $data; } |
420
|
|
|
|
|
|
|
}, |
421
|
74
|
|
|
|
|
174
|
$self->chunk |
422
|
|
|
|
|
|
|
); |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# remove the header added by LWP::UA before it sends the response back |
425
|
74
|
|
|
|
|
6008268
|
$response->remove_header('Client-Date'); |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# the callback is not called by LWP::UA->request |
428
|
|
|
|
|
|
|
# in some cases (HEAD, redirect, error responses have no body) |
429
|
74
|
100
|
|
|
|
1543
|
if ( !$sent ) { |
430
|
45
|
|
|
|
|
185
|
$self->response($response); |
431
|
|
|
|
|
|
|
$self->{$_}{response}->select_filters( $response ) |
432
|
45
|
|
|
|
|
356
|
for qw( headers body ); |
433
|
45
|
|
|
|
|
184
|
$self->{headers}{response} |
434
|
|
|
|
|
|
|
->filter( $response->headers, $response ); |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# do a last pass, in case there was something left in the buffers |
438
|
74
|
|
|
|
|
143
|
my $data = ""; # FIXME $protocol is undef here too |
439
|
74
|
|
|
|
|
348
|
$self->{body}{response}->filter_last( \$data, $response, undef ); |
440
|
74
|
50
|
|
|
|
211
|
if ( length $data ) { |
441
|
0
|
0
|
|
|
|
0
|
if ($chunked) { |
442
|
0
|
|
|
|
|
0
|
printf $conn "%x$CRLF%s$CRLF", length($data), $data; |
443
|
|
|
|
|
|
|
} |
444
|
0
|
|
|
|
|
0
|
else { print $conn $data; } |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# last chunk |
448
|
74
|
100
|
|
|
|
974
|
print $conn "0$CRLF$CRLF" if $chunked; # no trailers either |
449
|
74
|
|
|
|
|
210
|
$self->response($response); |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# what about X-Died and X-Content-Range? |
452
|
74
|
50
|
|
|
|
249
|
if( my $died = $response->header('X-Died') ) { |
453
|
0
|
|
|
|
|
0
|
$self->log( ERROR, "ERROR", $died ); |
454
|
0
|
|
|
|
|
0
|
$sent = 0; |
455
|
0
|
|
|
|
|
0
|
$response = HTTP::Response->new( 500, "Proxy filter error" ); |
456
|
0
|
|
|
|
|
0
|
$response->content_type( "text/plain" ); |
457
|
0
|
|
|
|
|
0
|
$response->content($died); |
458
|
0
|
|
|
|
|
0
|
$self->response($response); |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
SEND: |
462
|
|
|
|
|
|
|
|
463
|
76
|
|
|
|
|
2943
|
$response = $self->response ; |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# responses that weren't filtered through callbacks |
466
|
|
|
|
|
|
|
# (empty body or error) |
467
|
|
|
|
|
|
|
# FIXME some error response headers might not be filtered |
468
|
76
|
100
|
|
|
|
265
|
if ( !$sent ) { |
469
|
47
|
|
|
|
|
199
|
($last, $chunked) = $self->_send_response_headers( $served ); |
470
|
47
|
|
|
|
|
463
|
my $content = $response->content; |
471
|
47
|
100
|
|
|
|
622
|
if ($chunked) { |
472
|
37
|
100
|
|
|
|
1334
|
printf $conn "%x$CRLF%s$CRLF", length($content), $content |
473
|
|
|
|
|
|
|
if length($content); # the filter may leave nothing |
474
|
37
|
|
|
|
|
2438
|
print $conn "0$CRLF$CRLF"; |
475
|
|
|
|
|
|
|
} |
476
|
10
|
|
|
|
|
207
|
else { print $conn $content; } |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
# FIXME ftp, gopher |
480
|
76
|
50
|
66
|
|
|
328
|
$conn->print( $response->content ) |
|
|
|
33
|
|
|
|
|
481
|
|
|
|
|
|
|
if defined $req->uri->scheme |
482
|
|
|
|
|
|
|
and $req->uri->scheme =~ /^(?:ftp|gopher)$/ |
483
|
|
|
|
|
|
|
and $response->is_success; |
484
|
|
|
|
|
|
|
|
485
|
76
|
100
|
100
|
|
|
4009
|
$self->log( SOCKET, "SOCKET", "Connection closed by the proxy" ), last |
486
|
|
|
|
|
|
|
if $last || $served >= $self->max_keep_alive_requests; |
487
|
|
|
|
|
|
|
} |
488
|
15
|
50
|
66
|
|
|
98
|
$self->log( SOCKET, "SOCKET", "Connection closed by the client" ) |
489
|
|
|
|
|
|
|
if !$last |
490
|
|
|
|
|
|
|
and $served < $self->max_keep_alive_requests; |
491
|
15
|
|
|
|
|
70
|
$self->log( PROCESS, "PROCESS", "Served $served requests" ); |
492
|
15
|
|
|
|
|
147
|
$conn->close; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# INTERNAL METHOD |
496
|
|
|
|
|
|
|
# send the response headers for the proxy |
497
|
|
|
|
|
|
|
# expects $served (number of requests served) |
498
|
|
|
|
|
|
|
# returns $last and $chunked (last request served, chunked encoding) |
499
|
|
|
|
|
|
|
sub _send_response_headers { |
500
|
77
|
|
|
77
|
|
139
|
my ( $self, $served ) = @_; |
501
|
77
|
|
|
|
|
155
|
my ( $last, $chunked ) = ( 0, 0 ); |
502
|
77
|
|
|
|
|
260
|
my $conn = $self->client_socket; |
503
|
77
|
|
|
|
|
193
|
my $response = $self->response; |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
# correct headers |
506
|
77
|
100
|
|
|
|
539
|
$response->remove_header("Content-Length") |
507
|
|
|
|
|
|
|
if $self->{body}{response}->will_modify(); |
508
|
77
|
100
|
|
|
|
306
|
$response->header( Server => "HTTP::Proxy/$VERSION" ) |
509
|
|
|
|
|
|
|
unless $response->header( 'Server' ); |
510
|
77
|
100
|
|
|
|
2832
|
$response->header( Date => time2str(time) ) |
511
|
|
|
|
|
|
|
unless $response->header( 'Date' ); |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# this is adapted from HTTP::Daemon |
514
|
77
|
50
|
|
|
|
2621
|
if ( $conn->antique_client ) { $last++ } |
|
0
|
|
|
|
|
0
|
|
515
|
|
|
|
|
|
|
else { |
516
|
77
|
|
|
|
|
710
|
my $code = $response->code; |
517
|
77
|
|
|
|
|
659
|
$conn->send_status_line( $code, $response->message, |
518
|
|
|
|
|
|
|
$self->request()->protocol() ); |
519
|
77
|
100
|
100
|
|
|
14437
|
if ( $code =~ /^(1\d\d|[23]04)$/ ) { |
|
|
100
|
|
|
|
|
|
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
# make sure content is empty |
522
|
2
|
|
|
|
|
11
|
$response->remove_header("Content-Length"); |
523
|
2
|
|
|
|
|
66
|
$response->content(''); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
elsif ( $response->request && $response->request->method eq "HEAD" ) |
526
|
|
|
|
|
|
|
{ # probably OK, says HTTP::Daemon |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
else { |
529
|
71
|
100
|
|
|
|
1997
|
if ( $conn->proto_ge("HTTP/1.1") ) { |
530
|
65
|
|
|
|
|
1275
|
$chunked++; |
531
|
65
|
|
|
|
|
229
|
$response->push_header( "Transfer-Encoding" => "chunked" ); |
532
|
65
|
100
|
|
|
|
1654
|
$response->push_header( "Connection" => "close" ) |
533
|
|
|
|
|
|
|
if $served >= $self->max_keep_alive_requests; |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
else { |
536
|
6
|
|
|
|
|
138
|
$last++; |
537
|
6
|
|
|
|
|
20
|
$conn->force_last_request; |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
} |
540
|
77
|
|
|
|
|
950
|
print $conn $response->headers_as_string($CRLF); |
541
|
77
|
|
|
|
|
15490
|
print $conn $CRLF; # separates headers and content |
542
|
|
|
|
|
|
|
} |
543
|
77
|
|
|
|
|
603
|
$self->log( STATUS, "RESPONSE", $response->status_line ); |
544
|
77
|
|
|
|
|
446
|
$self->log( HEADERS, "RESPONSE", $response->headers->as_string ); |
545
|
77
|
|
|
|
|
243
|
return ($last, $chunked); |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# INTERNAL method |
549
|
|
|
|
|
|
|
# FIXME no man-in-the-middle for now |
550
|
|
|
|
|
|
|
sub _handle_CONNECT { |
551
|
1
|
|
|
1
|
|
2
|
my ($self, $served) = @_; |
552
|
1
|
|
|
|
|
1
|
my $last = 0; |
553
|
|
|
|
|
|
|
|
554
|
1
|
|
|
|
|
4
|
my $conn = $self->client_socket; |
555
|
1
|
|
|
|
|
2
|
my $req = $self->request; |
556
|
1
|
|
|
|
|
2
|
my $upstream; |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# connect upstream |
559
|
1
|
50
|
|
|
|
3
|
if ( my $up = $self->agent->proxy('http') ) { |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# clean up authentication info from proxy URL |
562
|
0
|
|
|
|
|
0
|
$up =~ s{^http://[^/\@]*\@}{http://}; |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
# forward to upstream proxy |
565
|
0
|
|
|
|
|
0
|
$self->log( PROXY, "PROXY", |
566
|
|
|
|
|
|
|
"Forwarding CONNECT request to next proxy: $up" ); |
567
|
0
|
|
|
|
|
0
|
my $response = $self->agent->simple_request($req); |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# check the upstream proxy's response |
570
|
0
|
|
|
|
|
0
|
my $code = $response->code; |
571
|
0
|
0
|
|
|
|
0
|
if ( $code == 407 ) { # don't forward Proxy Authentication requests |
|
|
0
|
|
|
|
|
|
572
|
0
|
|
|
|
|
0
|
my $response_407 = $response->as_string; |
573
|
0
|
|
|
|
|
0
|
$response_407 =~ s/^Client-.*$//mg; |
574
|
0
|
|
|
|
|
0
|
$response = HTTP::Response->new(502); |
575
|
0
|
|
|
|
|
0
|
$response->content_type("text/plain"); |
576
|
0
|
|
|
|
|
0
|
$response->content( "Upstream proxy ($up) " |
577
|
|
|
|
|
|
|
. "requested authentication:\n\n" |
578
|
|
|
|
|
|
|
. $response_407 ); |
579
|
0
|
|
|
|
|
0
|
$self->response($response); |
580
|
0
|
|
|
|
|
0
|
return $last; |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
elsif ( $code != 200 ) { # forward every other failure |
583
|
0
|
|
|
|
|
0
|
$self->response($response); |
584
|
0
|
|
|
|
|
0
|
return $last; |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
0
|
|
|
|
|
0
|
$upstream = $response->{client_socket}; |
588
|
|
|
|
|
|
|
} |
589
|
|
|
|
|
|
|
else { # direct connection |
590
|
1
|
|
|
|
|
54
|
$upstream = IO::Socket::INET->new( PeerAddr => $req->uri->host_port ); |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# no upstream socket obtained |
594
|
1
|
50
|
|
|
|
767
|
if( !$upstream ) { |
595
|
0
|
|
|
|
|
0
|
my $response = HTTP::Response->new( 500 ); |
596
|
0
|
|
|
|
|
0
|
$response->content_type( "text/plain" ); |
597
|
0
|
|
|
|
|
0
|
$response->content( "CONNECT failed: $@"); |
598
|
0
|
|
|
|
|
0
|
$self->response($response); |
599
|
0
|
|
|
|
|
0
|
return $last; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
|
602
|
1
|
|
|
|
|
18
|
$upstream->setsockopt( SOL_SOCKET, SO_SNDBUF, |
603
|
|
|
|
|
|
|
$conn->getsockopt( SOL_SOCKET, SO_RCVBUF ) ); |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
# send the response headers (FIXME more headers required?) |
606
|
1
|
|
|
|
|
51
|
my $response = HTTP::Response->new(200); |
607
|
1
|
|
|
|
|
73
|
$self->response($response); |
608
|
1
|
|
|
|
|
5
|
$self->{$_}{response}->select_filters( $response ) for qw( headers body ); |
609
|
|
|
|
|
|
|
|
610
|
1
|
|
|
|
|
9
|
$self->_send_response_headers( $served ); |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
# we now have a TCP connection |
613
|
1
|
|
|
|
|
2
|
$last = 1; |
614
|
|
|
|
|
|
|
|
615
|
1
|
|
|
|
|
25
|
my $select = IO::Select->new; |
616
|
1
|
|
|
|
|
83
|
for ( $conn, $upstream ) { |
617
|
2
|
|
|
|
|
66
|
$_->autoflush(1); |
618
|
2
|
|
|
|
|
77
|
$_->blocking(0); |
619
|
2
|
|
|
|
|
30
|
$select->add($_); |
620
|
|
|
|
|
|
|
} |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# loop while there is data |
623
|
1
|
|
|
|
|
21
|
while ( my @ready = $select->can_read ) { |
624
|
1
|
|
|
|
|
35
|
for (@ready) { |
625
|
2
|
|
|
|
|
3
|
my $data = ""; |
626
|
2
|
100
|
|
|
|
11
|
my ($sock, $peer, $from ) = $conn eq $_ |
627
|
|
|
|
|
|
|
? ( $conn, $upstream, "client" ) |
628
|
|
|
|
|
|
|
: ( $upstream, $conn, "server" ); |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# read the data |
631
|
2
|
|
|
|
|
17
|
my $read = $sock->sysread( $data, 4096 ); |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
# check for errors |
634
|
2
|
100
|
|
|
|
18
|
if(not defined $read ) { |
635
|
1
|
|
|
|
|
22
|
$self->log( ERROR, "CONNECT", "Read undef from $from ($!)" ); |
636
|
1
|
|
|
|
|
5
|
next; |
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
# end of connection |
640
|
1
|
50
|
|
|
|
2
|
if ( $read == 0 ) { |
641
|
1
|
|
|
|
|
7
|
$_->close for ( $sock, $peer ); |
642
|
1
|
|
|
|
|
89
|
$select->remove( $sock, $peer ); |
643
|
1
|
|
|
|
|
54
|
$self->log( SOCKET, "CONNECT", "Connection closed by the $from" ); |
644
|
1
|
|
|
|
|
9
|
$self->log( PROCESS, "PROCESS", "Served $served requests" ); |
645
|
1
|
|
|
|
|
2
|
next; |
646
|
|
|
|
|
|
|
} |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
# proxy the data |
649
|
0
|
|
|
|
|
0
|
$self->log( CONNECT, "CONNECT", "$read bytes received from $from" ); |
650
|
0
|
|
|
|
|
0
|
$peer->syswrite($data, length $data); |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
} |
653
|
1
|
|
|
|
|
8
|
$self->log( CONNECT, "CONNECT", "End of CONNECT proxyfication"); |
654
|
1
|
|
|
|
|
20
|
return $last; |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
sub push_filter { |
658
|
31
|
|
|
31
|
1
|
3202
|
my $self = shift; |
659
|
31
|
|
|
|
|
373
|
my %arg = ( |
660
|
|
|
|
|
|
|
mime => 'text/*', |
661
|
|
|
|
|
|
|
method => join( ',', @METHODS ), |
662
|
|
|
|
|
|
|
scheme => 'http', |
663
|
|
|
|
|
|
|
host => '', |
664
|
|
|
|
|
|
|
path => '', |
665
|
|
|
|
|
|
|
query => '', |
666
|
|
|
|
|
|
|
); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
# parse parameters |
669
|
31
|
|
|
|
|
112
|
for( my $i = 0; $i < @_ ; $i += 2 ) { |
670
|
54
|
100
|
|
|
|
310
|
next if $_[$i] !~ /^(mime|method|scheme|host|path|query)$/; |
671
|
19
|
|
|
|
|
37
|
$arg{$_[$i]} = $_[$i+1]; |
672
|
19
|
|
|
|
|
28
|
splice @_, $i, 2; |
673
|
19
|
|
|
|
|
35
|
$i -= 2; |
674
|
|
|
|
|
|
|
} |
675
|
31
|
100
|
|
|
|
264
|
croak "Odd number of arguments" if @_ % 2; |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
# the proxy must be initialised |
678
|
30
|
|
|
|
|
98
|
$self->init; |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
# prepare the variables for the closure |
681
|
30
|
|
|
|
|
103
|
my ( $mime, $method, $scheme, $host, $path, $query ) = |
682
|
|
|
|
|
|
|
@arg{qw( mime method scheme host path query )}; |
683
|
|
|
|
|
|
|
|
684
|
30
|
50
|
33
|
|
|
175
|
if ( defined $mime && $mime ne '' ) { |
685
|
30
|
100
|
|
|
|
211
|
$mime =~ m!/! or croak "Invalid MIME type definition: $mime"; |
686
|
29
|
|
|
|
|
151
|
$mime =~ s/\*/$RX{token}/; #turn it into a regex |
687
|
29
|
|
|
|
|
418
|
$mime = qr/^$mime(?:$|\s*;?)/; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
29
|
|
|
|
|
521
|
my @method = split /\s*,\s*/, $method; |
691
|
29
|
100
|
|
|
|
86
|
for (@method) { croak "Invalid method: $_" if !/$RX{method}/ } |
|
704
|
|
|
|
|
2172
|
|
692
|
28
|
50
|
|
|
|
170
|
$method = @method ? '(?:' . join ( '|', @method ) . ')' : ''; |
693
|
28
|
|
|
|
|
1314
|
$method = qr/^$method$/; |
694
|
|
|
|
|
|
|
|
695
|
28
|
|
|
|
|
119
|
my @scheme = split /\s*,\s*/, $scheme; |
696
|
28
|
|
|
|
|
43
|
for (@scheme) { |
697
|
28
|
100
|
|
|
|
90
|
croak "Unsupported scheme: $_" |
698
|
|
|
|
|
|
|
if !$self->is_protocol_supported($_); |
699
|
|
|
|
|
|
|
} |
700
|
27
|
50
|
|
|
|
117
|
$scheme = @scheme ? '(?:' . join ( '|', @scheme ) . ')' : ''; |
701
|
27
|
|
|
|
|
217
|
$scheme = qr/$scheme/; |
702
|
|
|
|
|
|
|
|
703
|
27
|
|
100
|
|
|
154
|
$host ||= '.*'; $host = qr/$host/i; |
|
27
|
|
|
|
|
121
|
|
704
|
27
|
|
50
|
|
|
117
|
$path ||= '.*'; $path = qr/$path/; |
|
27
|
|
|
|
|
89
|
|
705
|
27
|
|
50
|
|
|
109
|
$query ||= '.*'; $query = qr/$query/; |
|
27
|
|
|
|
|
87
|
|
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# push the filter and its match method on the correct stack |
708
|
27
|
|
|
|
|
73
|
while(@_) { |
709
|
31
|
|
|
|
|
59
|
my ($message, $filter ) = (shift, shift); |
710
|
31
|
100
|
|
|
|
240
|
croak "'$message' is not a filter stack" |
711
|
|
|
|
|
|
|
unless $message =~ /^(request|response)$/; |
712
|
|
|
|
|
|
|
|
713
|
30
|
100
|
66
|
|
|
592
|
croak "Not a Filter reference for filter queue $message" |
|
|
|
66
|
|
|
|
|
714
|
|
|
|
|
|
|
unless ref( $filter ) |
715
|
|
|
|
|
|
|
&& ( $filter->isa('HTTP::Proxy::HeaderFilter') |
716
|
|
|
|
|
|
|
|| $filter->isa('HTTP::Proxy::BodyFilter') ); |
717
|
|
|
|
|
|
|
|
718
|
29
|
|
|
|
|
34
|
my $stack; |
719
|
29
|
100
|
|
|
|
108
|
$stack = 'headers' if $filter->isa('HTTP::Proxy::HeaderFilter'); |
720
|
29
|
100
|
|
|
|
99
|
$stack = 'body' if $filter->isa('HTTP::Proxy::BodyFilter'); |
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
# MIME can only match on response |
723
|
29
|
|
|
|
|
42
|
my $mime = $mime; |
724
|
29
|
100
|
|
|
|
67
|
undef $mime if $message eq 'request'; |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# compute the match sub as a closure |
727
|
|
|
|
|
|
|
# for $self, $mime, $method, $scheme, $host, $path |
728
|
|
|
|
|
|
|
my $match = sub { |
729
|
18
|
50
|
50
|
18
|
|
109
|
return 0 |
|
|
|
33
|
|
|
|
|
730
|
|
|
|
|
|
|
if ( defined $mime ) |
731
|
|
|
|
|
|
|
&& ( $self->response->content_type || '' ) !~ $mime; |
732
|
18
|
50
|
50
|
|
|
643
|
return 0 if ( $self->{request}->method || '' ) !~ $method; |
733
|
18
|
50
|
50
|
|
|
249
|
return 0 if ( $self->{request}->uri->scheme || '' ) !~ $scheme; |
734
|
18
|
50
|
100
|
|
|
669
|
return 0 if ( $self->{request}->uri->authority || '' ) !~ $host; |
735
|
18
|
50
|
50
|
|
|
366
|
return 0 if ( $self->{request}->uri->path || '' ) !~ $path; |
736
|
18
|
50
|
50
|
|
|
300
|
return 0 if ( $self->{request}->uri->query || '' ) !~ $query; |
737
|
18
|
|
|
|
|
362
|
return 1; # it's a match |
738
|
29
|
|
|
|
|
134
|
}; |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
# push it on the corresponding FilterStack |
741
|
29
|
|
|
|
|
173
|
$self->{$stack}{$message}->push( [ $match, $filter ] ); |
742
|
29
|
|
|
|
|
144
|
$filter->proxy( $self ); |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
sub is_protocol_supported { |
747
|
104
|
|
|
104
|
1
|
1396
|
my ( $self, $scheme ) = @_; |
748
|
104
|
|
|
|
|
216
|
my $ok = 1; |
749
|
104
|
100
|
|
|
|
557
|
if ( !$self->agent->is_protocol_supported($scheme) ) { |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
# double check, in case a dummy scheme was added |
752
|
|
|
|
|
|
|
# to be handled directly by a filter |
753
|
2
|
|
|
|
|
90
|
$ok = 0; |
754
|
2
|
|
33
|
|
|
5
|
$scheme eq $_ && $ok++ for @{ $self->agent->protocols_allowed }; |
|
2
|
|
|
|
|
7
|
|
755
|
|
|
|
|
|
|
} |
756
|
104
|
|
|
|
|
288799
|
$ok; |
757
|
|
|
|
|
|
|
} |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
sub log { |
760
|
700
|
|
|
700
|
1
|
22180
|
my $self = shift; |
761
|
700
|
|
|
|
|
945
|
my $level = shift; |
762
|
700
|
|
|
|
|
2175
|
my $fh = $self->logfh; |
763
|
|
|
|
|
|
|
|
764
|
700
|
100
|
100
|
|
|
2152
|
return unless $self->logmask & $level || $level == ERROR; |
765
|
|
|
|
|
|
|
|
766
|
21
|
|
|
|
|
22
|
my ( $prefix, $msg ) = ( @_, '' ); |
767
|
21
|
|
|
|
|
40
|
my @lines = split /\n/, $msg; |
768
|
21
|
50
|
|
|
|
33
|
@lines = ('') if not @lines; |
769
|
|
|
|
|
|
|
|
770
|
21
|
|
|
|
|
50
|
flock( $fh, LOCK_EX ); |
771
|
21
|
|
|
|
|
531
|
print $fh "[" . localtime() . "] ($$) $prefix: $_\n" for @lines; |
772
|
21
|
|
|
|
|
57
|
flock( $fh, LOCK_UN ); |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
1; |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
__END__ |