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