File Coverage

blib/lib/WWW/Mechanize/Cached.pm
Criterion Covered Total %
statement 64 93 68.8
branch 25 56 44.6
condition 7 15 46.6
subroutine 14 16 87.5
pod 1 2 50.0
total 111 182 60.9


line stmt bran cond sub pod time code
1             package WWW::Mechanize::Cached;
2              
3 11     11   1603312 use Moo 1.004005;
  11         99494  
  11         83  
4 11     11   12895 use warnings FATAL => 'all';
  11         21  
  11         592  
5              
6             our $VERSION = '1.56';
7              
8 11     11   59 use Carp qw( carp croak );
  11         19  
  11         533  
9 11     11   58 use Module::Runtime qw( use_module );
  11         23  
  11         66  
10 11     11   5560 use MooX::Types::MooseLike::Base qw( AnyOf Bool Enum Maybe );
  11         62967  
  11         1078  
11 11     11   6369 use Storable qw( nfreeze thaw );
  11         29538  
  11         766  
12              
13 11     11   5132 use namespace::clean;
  11         107020  
  11         63  
14              
15             extends 'WWW::Mechanize';
16              
17             has is_cached => ( is => 'rw', isa => Maybe [Bool], default => undef );
18             has positive_cache => ( is => 'rw', isa => Bool, default => 1 );
19             has ref_in_cache_key => ( is => 'rw', isa => Bool, default => 0 );
20             has _verbose_dwarn => ( is => 'rw', isa => Bool, default => 0 );
21             has _last_request => ( is => 'rw' );
22              
23             has [qw/ cache_undef_content_length cache_zero_content_length /] =>
24             ( is => 'rw', isa => AnyOf [ Bool, Enum ['warn'] ], default => 0 );
25              
26             has cache_mismatch_content_length => (
27             is => 'rw',
28             isa => AnyOf [ Bool, Enum ['warn'] ],
29             default => 'warn',
30             );
31              
32             has cache => ( is => 'lazy', isa => \&_isa_warn_cache );
33              
34             sub FOREIGNBUILDARGS {
35 21     21 0 158782 my ( $class, %args ) = @_;
36              
37             # WWW::Mechanize/LWP::UserAgent would complain about these
38 21         230 for my $attribute (
39             qw(
40             is_cached
41             positive_cache
42             ref_in_cach_key
43             _verbose_dwarn
44             cache_undef_content_length
45             cache_zero_content_length
46             cache_mismatch_content_length
47             cache
48             )
49             ) {
50 168         236 delete $args{$attribute};
51             }
52              
53 21         171 return %args;
54             }
55              
56             sub _isa_warn_cache {
57             return
58 10 50 66 10   127166 if 'HASH' ne ref $_[0]
      66        
59             and $_[0]->can('get')
60             and $_[0]->can('set');
61 1         21 carp 'The cache param must be an initialized cache object';
62 1         669 $_[0] = undef;
63             }
64              
65             sub _build_cache {
66 2     2   15163 my $self = shift;
67              
68             return Cache::FileCache->new(
69             {
70             default_expires_in => '1d',
71             namespace => 'www-mechanize-cached',
72             }
73 2 50       5 ) if eval { use_module('Cache::FileCache') };
  2         13  
74              
75             return CHI->new(
76             driver => 'File',
77             expires_in => '1d',
78             namespace => 'www-mechanize-cached',
79 0 0       0 ) if eval { use_module('CHI') };
  0         0  
80              
81 0         0 croak( 'Could not create a default cache.'
82             . 'Please make sure either CHI or Cache::FileCache are installed or configure manually as appropriate'
83             );
84             }
85              
86             around _make_request => sub {
87             my ( $orig, $self, $request ) = splice @_, 0, 3;
88             my $req = $request;
89              
90             $self->is_cached(0);
91              
92             # An odd line to need.
93             # No idea what purpose this serves? OALDERS
94             $self->{proxy} = {} unless defined $self->{proxy}; ## no critic
95              
96             # RT #56757
97             if ( !$self->ref_in_cache_key ) {
98             my $clone = $request->clone;
99             $clone->header( Referer => undef );
100             $req = $clone->as_string;
101             }
102              
103             my $response = $self->cache->get($req);
104              
105             if ($response) {
106             $response = thaw($response);
107             }
108             if ( $self->_cache_ok($response) ) {
109             $self->is_cached(1);
110             $self->_last_request($req);
111             return $response;
112             }
113              
114             $response = $self->$orig( $request, @_ );
115              
116             # decode strips some important headers.
117             my $headers = $response->headers->clone;
118              
119             my $should_cache = $self->_response_cache_ok( $response, $headers );
120              
121             # http://rt.cpan.org/Public/Bug/Display.html?id=42693
122             $response->decode();
123             delete $response->{handlers}; ## no critic
124              
125             if ($should_cache) {
126             $self->_last_request($req);
127             $self->cache->set( $req, nfreeze($response) );
128             }
129              
130             return $response;
131             };
132              
133             sub invalidate_last_request {
134 1     1 1 1265 my $self = shift;
135 1 50       26 return unless $self->is_cached;
136              
137 0         0 my $request = $self->_last_request;
138 0 0       0 return unless $request;
139              
140 0         0 $self->cache->remove($request);
141 0 0       0 return $self->is_cached( $self->cache->get($request) ? 1 : 0 );
142             }
143              
144             sub _dwarn_filter {
145             return {
146 0     0   0 hide_keys => [
147             qw( _content cookie content set-cookie handlers cookie_jar cache req res page_stack )
148             ]
149             };
150              
151             }
152              
153             sub _dwarn {
154 0     0   0 my $self = shift;
155 0         0 my $message = shift;
156              
157 0 0       0 return unless my $handler = $self->{onwarn}; ## no critic
158              
159 0 0       0 return if $self->quiet;
160              
161 0 0       0 if ( $self->_verbose_dwarn ) {
162 0         0 my $payload = {
163             self => $self,
164             message => $message,
165             debug => \@_,
166             };
167 0         0 require Data::Dump;
168 0         0 return $handler->( Data::Dump::dumpf( $payload, \&_dwarn_filter ) );
169             }
170             else {
171 0         0 return $handler->($message);
172             }
173             }
174              
175             sub _response_cache_ok {
176 22     22   13764 my $self = shift;
177 22         33 my $response = shift;
178 22         30 my $headers = shift;
179              
180 22 50       59 return 0 if !$response;
181 22 50       496 return 1 if !$self->positive_cache;
182              
183 22 50       204 return 0 if $response->code < 200;
184 22 100       233 return 0 if $response->code > 301;
185              
186 13         102 my $size;
187             {
188 13 100       14 if ( $headers->header('Client-Transfer-Encoding') ) {
  13         29  
189 3         93 my @cte = $headers->header('Client-Transfer-Encoding');
190 3         76 for my $cte (@cte) {
191              
192             # Transfer-Encoding = chunked means document consistency
193             # is independent of Content-Length value,
194             # and that Content-Length can be safely ignored.
195             # Its not obvious how the lower levels represent a
196             # failed chunked-transfer yet.
197             # But its safe to say relying on content-length proves pointless.
198 3 100       13 return 1 if $cte eq 'chunked';
199             }
200             }
201              
202 11         374 $size = $headers->{'content-length'};
203             }
204              
205 11 100       27 if ( not defined $size ) {
206 1 50       17 if ( $self->cache_undef_content_length . q{} eq q{warn} ) {
207 0         0 $self->_dwarn(
208             q[Content-Length header was undefined, not caching]
209             . q[ (E=WWW_MECH_CACHED_CONTENTLENGTH_MISSING)],
210             $headers
211             );
212 0         0 return 0;
213             }
214 1 50       20 if ( $self->cache_undef_content_length == 0 ) {
215 1         8 return 0;
216             }
217             }
218              
219 10 50 33     41 if ( defined $size and $size == 0 ) {
220 0 0       0 if ( $self->cache_zero_content_length . q{} eq q{warn} ) {
221 0         0 $self->_dwarn(
222             q{Content-Length header was 0, not caching}
223             . q{ (E=WWW_MECH_CACHED_CONTENTLENGTH_ZERO)},
224             $headers
225             );
226 0         0 return 0;
227             }
228 0 0       0 if ( $self->cache_zero_content_length == 0 ) {
229 0         0 return 0;
230             }
231             }
232              
233 10 50 33     43 if ( defined $size
      33        
234             and $size != 0
235             and $size != length( $response->content ) ) {
236 0 0       0 if ( $self->cache_mismatch_content_length . "" eq "warn" ) {
237 0         0 $self->_dwarn(
238             q{Content-Length header did not match contents actual length, not caching}
239             . q{ (E=WWW_MECH_CACHED_CONTENTLENGTH_MISSMATCH)} );
240 0         0 return 0;
241             }
242 0 0       0 if ( $self->cache_mismatch_content_length == 0 ) {
243 0         0 return 0;
244             }
245             }
246              
247 10         120 return 1;
248             }
249              
250             sub _cache_ok {
251              
252 43     43   14279 my $self = shift;
253 43         59 my $response = shift;
254              
255 43 100       116 return 0 if !$response;
256 24 100       388 return 1 if !$self->positive_cache;
257              
258 22 50       160 return 0 if $response->code < 200;
259 22 100       209 return 0 if $response->code > 301;
260              
261 21         182 return 1;
262             }
263              
264 11     11   14937 no warnings;
  11         23  
  11         554  
265             "We miss you, Spoon"; ## no critic
266              
267             # ABSTRACT: Cache response to be polite
268              
269             __END__