File Coverage

blib/lib/HTTP/Proxy.pm
Criterion Covered Total %
statement 339 413 82.0
branch 112 160 70.0
condition 36 59 61.0
subroutine 51 53 96.2
pod 13 13 100.0
total 551 698 78.9


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