File Coverage

blib/lib/App/HTTP_Proxy_IMP/IMP/FakeResponse.pm
Criterion Covered Total %
statement 37 114 32.4
branch 4 54 7.4
condition 3 20 15.0
subroutine 11 17 64.7
pod 7 8 87.5
total 62 213 29.1


line stmt bran cond sub pod time code
1              
2 1     1   5 use strict;
  1         2  
  1         44  
3 1     1   5 use warnings;
  1         2  
  1         45  
4             package App::HTTP_Proxy_IMP::IMP::FakeResponse;
5 1     1   5 use base 'Net::IMP::HTTP::Request';
  1         1  
  1         202  
6 1     1   7 use fields qw(root file response);
  1         2  
  1         7  
7              
8 1     1   75 use Net::IMP;
  1         2  
  1         110  
9 1     1   6 use Net::IMP::Debug;
  1         2  
  1         7  
10 1     1   100 use Carp;
  1         1  
  1         74  
11 1     1   6 use Digest::MD5;
  1         10  
  1         1792  
12              
13 1     1 0 15 sub RTYPES { ( IMP_PASS,IMP_REPLACE,IMP_DENY,IMP_ACCTFIELD ) }
14              
15             sub new_factory {
16 1     1 1 4 my ($class,%args) = @_;
17 1 50       5 my $dir = $args{root} or croak("no root directory given");
18 1 50 33     23 -d $dir && -r _ && -x _ or croak("cannot use base dir $dir: $!");
      33        
19 1         9 my $obj = $class->SUPER::new_factory(%args);
20 1         170 $obj->{root} = $dir;
21 1         6 return $obj;
22             }
23              
24             sub validate_cfg {
25 1     1 1 4 my ($class,%args) = @_;
26 1         3 my $dir = delete $args{root};
27 1         3 delete $args{ignore_parameters};
28 1         8 my @err = $class->SUPER::validate_cfg(%args);
29 1 50 33     41 if ( ! $dir ) {
    50          
30 0         0 push @err, "no 'root' given";
31             } elsif ( ! -d $dir || ! -r _ || ! -x _ ) {
32 0         0 push @err, "cannot access root dir $dir";
33             }
34 1         4 return @err;
35             }
36              
37             sub request_hdr {
38 0     0 1   my ($self,$hdr) = @_;
39              
40 0           my ($method,$proto,$host,$path) = $hdr =~m{\A([A-Z]+) +(?:(\w+)://([^/]+))?(\S+)};
41 0 0         $host = $1 if $hdr =~m{\nHost: *(\S+)}i;
42 0 0         if ( ! $host ) {
43 0           $self->run_callback([IMP_DENY,0,'cannot determine host']);
44 0           return;
45             }
46 0   0       $proto ||= 'http';
47 0           $host = lc($host);
48 0 0         my $port =
49 0 0         $host=~s{^(?:\[(\w._\-:)+\]|(\w._\-))(?::(\d+))?$}{ $1 || $2 }e ?
50             $3:80;
51              
52 0           my $dir = $self->{factory_args}{root}."/$host:$port";
53 0 0         goto IGNORE if ! -d $dir;
54              
55 0           my $uri = "$proto://$host:$port$path";
56 0 0         my $qstring = $path =~s{\?(.+)}{} ? $1 : undef;
57             # collect information to determine filename
58 0           my %file = (
59             uri => $uri,
60             dir => $dir,
61             method => $method,
62             md5path => Digest::MD5->new->add($path)->hexdigest,
63             md5data => undef,
64             );
65              
66 0           my $fname = "$dir/".lc($method)."-".$file{md5path};
67 0 0         goto TRY_FNAME if $self->{factory_args}{ignore_parameters};
68              
69 0 0         ( $file{md5data} = Digest::MD5->new )->add("\000$qstring\001")
70             if defined $qstring;
71 0 0         if ( $method ~~ [ 'GET','HEAD' ] ) {
72 0 0         $fname .= "-".$file{md5data}->hexdigest if $file{md5data};
73 0           goto TRY_FNAME;
74             }
75              
76             # ignore if there will not be a matching filename, no matter
77             # what md5data will be
78 0 0 0       goto IGNORE if ! -f $fname and ! glob("$fname-*");
79              
80             # don't pass yet, continue in request body
81 0           $file{rqhdr} = $hdr;
82 0           $self->{file} = \%file;
83 0           return;
84              
85             TRY_FNAME:
86 0 0         if ( $self->{response} = _extract_response($fname)) {
87 0           $hdr =~s{(\A\w+\s+)}{$1internal://};
88 0           debug("hijack http://$host:$port$path");
89 0           $self->run_callback(
90             [ IMP_ACCTFIELD,'orig_uri',$uri ],
91             [ IMP_REPLACE,0,$self->offset(0),$hdr ],
92             [ IMP_PASS,0,IMP_MAXOFFSET ]
93             );
94 0           return;
95             }
96              
97             IGNORE:
98 0           $self->run_callback(
99             [ IMP_PASS,0,IMP_MAXOFFSET ],
100             [ IMP_PASS,1,IMP_MAXOFFSET ],
101             );
102             }
103              
104             sub request_body {
105 0     0 1   my ($self,$data) = @_;
106 0 0         my $f = $self->{file} or return;
107 0           my $md = $f->{md5data};
108 0 0         if ( $data ne '' ) {
109 0   0       $md ||= $f->{md5data} = Digest::MD5->new;
110 0           $md->add($data);
111 0           return;
112             }
113              
114             # eof of request body - determine final filename
115 0           $self->{file} = undef;
116             my $fname = $f->{dir}.'/'.join('-',
117             lc($f->{method}),
118             $f->{md5path},
119 0 0         $f->{md5data} ? ($f->{md5data}->hexdigest):()
120             );
121              
122             # setup response if file is found
123 0 0         if ( $self->{response} = _extract_response($fname)) {
124 0           ( my $hdr = $f->{rqhdr})=~s{(\A\w+\s+)}{$1internal://};
125 0           debug("hijack $f->{uri}");
126             $self->run_callback(
127             [ IMP_ACCTFIELD,'orig_uri',$f->{uri} ],
128 0           [ IMP_REPLACE,0,length($f->{rqhdr}),$hdr ],
129             [ IMP_PASS,0,IMP_MAXOFFSET ]
130             );
131 0           return;
132             }
133              
134             # otherwise pass everything through
135             $self->run_callback(
136 0           [ IMP_PASS,0,IMP_MAXOFFSET ],
137             [ IMP_PASS,1,IMP_MAXOFFSET ],
138             );
139             }
140              
141              
142             sub response_hdr {
143 0     0 1   my ($self,$hdr) = @_;
144 0 0 0       my $rphdr = $self->{response} && $self->{response}[0] or return;
145 0           $rphdr =~s{\r?\n}{\r\n}g;
146 0           my $clen = length($self->{response}[1]);
147 0 0         $rphdr =~s{(\nContent-length:[ \t]*)\d+}{$1$clen} or
148             $rphdr =~s{\n}{\nContent-length: $clen\r\n};
149 0           warn "XXXX offset=".$self->offset(1)." len=".length($hdr);
150 0           $self->run_callback([ IMP_REPLACE,1,$self->offset(1),$rphdr ]);
151             }
152              
153             sub response_body {
154 0     0 1   my ($self,$data) = @_;
155 0 0         my $rp = $self->{response} or return;
156 0           $self->{response} = undef;
157 0           warn "XXXX offset=".$self->offset(1)." len=".length($data);
158 0           $self->run_callback(
159             [ IMP_REPLACE,1,$self->offset(1),$rp->[1] ],
160             [ IMP_PASS,1,IMP_MAXOFFSET ],
161             );
162             }
163              
164             sub any_data {
165 0     0 1   my $self = shift;
166             # ignore
167 0 0         $self->{file} or return;
168 0           $self->{file} = undef;
169 0           $self->run_callback(
170             [ IMP_PASS,0,IMP_MAXOFFSET ],
171             [ IMP_PASS,1,IMP_MAXOFFSET ],
172             );
173             }
174              
175             sub _extract_response {
176 0     0     my $fname = shift;
177 0 0         open( my $fh,'<',$fname) or return;
178 0           my $data = do { local $/; <$fh> };
  0            
  0            
179 0 0         if ( $data =~s{\A(HTTP/1\.[01] .*?(\r?\n)\2)}{}s ) {
180             # only response header + body
181 0           return [ $1,$data ];
182             } else {
183 0           my @size = unpack("NNNN",substr($data,-16));
184 0 0         if ( $size[0]+$size[1]+$size[2]+$size[3] + 16 == length($data)) {
185             # format used by Net::IMP::HTTP::SaveResponse
186 0           my $rq = $size[0]+$size[1]; # skip request
187             return [
188 0           substr($data,$rq,$size[2]), # response header
189             substr($data,$rq+$size[2],$size[3]) # response body
190             ],
191             }
192             }
193 0           debug("unknown format in $fname");
194 0           return;
195             }
196              
197             1;
198              
199             __END__