| 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__ |