3 # http proxy for archives
7 use HTTP::Proxy qw ( :log );
8 use HTTP::Proxy::BodyFilter::save;
9 use HTTP::Proxy::BodyFilter::simple;
10 use HTTP::Proxy::HeaderFilter::simple;
12 use Data::Dump qw(dump);
15 my ( $dir, $name, $value ) = @_;
17 mkdir 'var' unless -e 'var';
18 mkdir "var/$dir" unless -e "var/$dir";
19 open(my $fh, '>>', "var/$dir/$name") || die $!;
29 use CGI::Util qw( unescape );
33 # get the command-line parameters
40 my $args = '(' . join( '|', keys %args ) . ')';
41 for ( my $i = 0 ; $i < @ARGV ; $i += 2 ) {
42 if ( $ARGV[$i] =~ /$args/o ) {
43 if ( ref $args{$1} ) {
44 push @{ $args{$1} }, $ARGV[ $i + 1 ];
47 $args{$1} = $ARGV[ $i + 1 ];
49 splice( @ARGV, $i, 2 );
55 # the headers we want to see
57 qw( Content-Type Set-Cookie Set-Cookie2 WWW-Authenticate Location ),
61 ( qw( Cookie Cookie2 Referer Referrer Authorization ), @{ $args{header} } );
63 # NOTE: Body request filters always receive the request body in one pass
64 my $post_filter = HTTP::Proxy::BodyFilter::simple->new(
65 begin => sub { $_[0]->{binary} = 0; },
67 my ( $self, $dataref, $message, $protocol, $buffer ) = @_;
68 print STDOUT "\n", $message->method, " ", $message->uri, "\n";
69 print_headers( $message, @clt_hdr );
71 if ( $self->{binary} || $$dataref =~ /\0/ ) {
73 print STDOUT " (not printing binary data)\n";
77 # this is from CGI.pm, method parse_params()
78 my (@pairs) = split( /[&;]/, $$dataref );
80 my ( $param, $value ) = split( '=', $_, 2 );
81 $param = unescape($param);
82 $value = unescape($value);
83 printf STDOUT " %-20s => %s\n", $param, $value;
88 my $get_filter = HTTP::Proxy::HeaderFilter::simple->new(
90 my ( $self, $headers, $message ) = @_;
91 my $req = $message->request;
92 if ( $req->method ne 'POST' ) {
93 print STDOUT "\n", $req->method, " ", $req->uri, "\n";
94 print_headers( $req, @clt_hdr );
96 print STDOUT $message->status_line, "\n";
97 print_headers( $message, @srv_hdr );
99 if ( my $cookie = $message->header( 'Set-Cookie' ) ) {
100 my $host = $req->uri->host;
101 warn "COOKIE: $cookie from $host\n";
102 var_save 'cookie' => $host;
110 if ( $message->header($h) ) {
111 print STDOUT " $h: $_\n" for ( $message->header($h) );
116 # create and start the proxy
117 my $proxy = HTTP::Proxy->new(@ARGV);
119 # if we want to look at SOME sites
120 if (@{$args{peek}}) {
121 for (@{$args{peek}}) {
125 request => $post_filter
129 response => $get_filter,
134 # otherwise, peek at all sites
138 request => $post_filter
140 $proxy->push_filter( response => $get_filter, mime => $args{mime} );
149 # you should probably restrict this to certain hosts as well
151 mime => 'application/pdf',
153 response => HTTP::Proxy::BodyFilter::save->new(
158 # send a HTML message instead
159 response => HTTP::Proxy::BodyFilter::simple->new(
161 my ( $self, $message ) = @_; # for information, saorge
165 # my ( $self, $dataref, $message, $protocol, $buffer ) = @_;
166 # $$dataref = $saved++ ? ""
167 # : sprintf '<p>Saving PDF file. Go <a href="%s">back</a></p>',
168 # $message->request->header('referer');
171 # change the response Content-Type
172 response => HTTP::Proxy::HeaderFilter::simple->new(
174 my ( $self, $headers, $response ) = @_;
175 # $headers->content_type('text/html');
185 use HTTP::Proxy qw( :log );
186 use MIME::Base64 qw( encode_base64 );
188 # the encoded user:password pair
191 my $token = "Basic " . encode_base64( "http:proxy", '' );
193 # the authentication filter
195 request => HTTP::Proxy::HeaderFilter::simple->new(
197 my ( $self, $headers, $request ) = @_;
199 # check the token against all credentials
201 $_ eq $token && $ok++
202 for $self->proxy->hop_headers->header('Proxy-Authorization');
204 # no valid credential
206 my $response = HTTP::Response->new(407);
208 Proxy_Authenticate => 'Basic realm="HTTP::Proxy"' );
209 $self->proxy->response($response);
220 sub debug_on { -e 'var/debug' }
221 sub debug_dump { -e 'var/debug' && warn "## ", dump( @_ ) }
223 my $admin_filter = HTTP::Proxy::HeaderFilter::simple->new( sub {
224 my ( $self, $headers, $message ) = @_;
225 warn "\nXXX [", $headers->header('x-forwarded-for'), '] ', $message->uri, "\n";
227 print $message->headers_as_string if debug_on;
229 my $host = $message->uri->host;
230 var_save 'hits' => $host;
231 return unless $host eq $proxy->host && $message->uri->port == $proxy->port;
233 if ( my $q = $message->uri->query ) {
234 if ( $q =~ m{debug} ) {
235 -e 'var/debug' ? unlink 'var/debug' : open(my $touch,'>','var/debug');
238 debug_dump( $headers, $message );
240 my $host_port = $proxy->host . ':' . $proxy->port;
242 my $res = HTTP::Response->new( 200 );
244 if ( $message->uri->path =~ m/(proxy.pac|wpad.dat)/ ) {
245 $res->content_type('application/x-ns-proxy-autoconfig');
248 function FindProxyForURL(url, host) {
249 // if (shExpMatch(url, "*.example.com:*/*")) {return "DIRECT";}
251 if (shExpMatch(url, "*.js")) return "DIRECT";
252 if (shExpMatch(url, "*.css")) return "DIRECT";
253 if (shExpMatch(url, "*.gif")) return "DIRECT";
254 if (shExpMatch(url, "*.png")) return "DIRECT";
255 if (shExpMatch(url, "*.ico")) return "DIRECT";
256 if (shExpMatch(url, "*.jpg")) return "DIRECT";
258 if (isInNet(host, "127.0.0.0", "255.0.0.0"))
261 // if (isInNet(host, "10.0.0.0", "255.255.248.0")) {
262 // return "PROXY fastproxy.example.com:8080";
265 // we don't want to see this traffic!
266 if (shExpMatch(url, "*.google.*")) return "DIRECT";
268 return "PROXY $host_port; DIRECT";
272 $self->proxy->response( $res );
276 $res->content_type('text/html');
279 <h1>HTTP Proxy Archive</h1>
281 <div style="background: #ff0; padding: 1em;">
283 Copy following url into automatic proxy configuration and enable it:
285 <a href="http://$host_port/proxy.pac">http://$host_port/proxy.pac</a>
290 . qq|<a href=/>/</a> <a href="?debug">debug</a>|
293 $self->proxy->response( $res );
295 $proxy->push_filter( request => $admin_filter );
302 warn "user interface at http://", $proxy->host, ":", $proxy->port, "\n";