File Coverage

inc/HTTP/Response.pm
Criterion Covered Total %
statement 21 164 12.8
branch 1 78 1.2
condition 0 51 0.0
subroutine 9 24 37.5
pod 22 22 100.0
total 53 339 15.6


line stmt bran cond sub pod time code
1             #line 1
2             package HTTP::Response;
3              
4             require HTTP::Message;
5             @ISA = qw(HTTP::Message);
6             $VERSION = "6.04";
7 6     6   195991  
  6         17  
  6         208  
8 6     6   10907 use strict;
  6         23168  
  6         10987  
9             use HTTP::Status ();
10              
11              
12              
13             sub new
14 18     18 1 17108 {
15 18         158 my($class, $rc, $msg, $header, $content) = @_;
16 18         451 my $self = $class->SUPER::new($header, $content);
17 18         202 $self->code($rc);
18 18         138 $self->message($msg);
19             $self;
20             }
21              
22              
23             sub parse
24 0     0 1 0 {
25 0         0 my($class, $str) = @_;
26 0 0       0 my $status_line;
27 0         0 if ($str =~ s/^(.*)\n//) {
28             $status_line = $1;
29             }
30 0         0 else {
31 0         0 $status_line = $str;
32             $str = "";
33             }
34 0         0  
35 0         0 my $self = $class->SUPER::parse($str);
36 0 0       0 my($protocol, $code, $message);
37             if ($status_line =~ /^\d{3} /) {
38 0         0 # Looks like a response created by HTTP::Response->new
39             ($code, $message) = split(' ', $status_line, 2);
40 0         0 } else {
41             ($protocol, $code, $message) = split(' ', $status_line, 3);
42 0 0       0 }
43 0 0       0 $self->protocol($protocol) if $protocol;
44 0 0       0 $self->code($code) if defined($code);
45 0         0 $self->message($message) if defined($message);
46             $self;
47             }
48              
49              
50             sub clone
51 0     0 1 0 {
52 0         0 my $self = shift;
53 0         0 my $clone = bless $self->SUPER::clone, ref($self);
54 0         0 $clone->code($self->code);
55 0 0       0 $clone->message($self->message);
56             $clone->request($self->request->clone) if $self->request;
57 0         0 # we don't clone previous
58             $clone;
59             }
60              
61 43     43 1 601  
62 20     20 1 276 sub code { shift->_elem('_rc', @_); }
63 23     23 1 102 sub message { shift->_elem('_msg', @_); }
64 2     2 1 57 sub previous { shift->_elem('_previous',@_); }
65             sub request { shift->_elem('_request', @_); }
66              
67              
68             sub status_line
69 0     0 1 0 {
70 0   0     0 my $self = shift;
71 0   0     0 my $code = $self->{'_rc'} || "000";
72 0         0 my $mess = $self->{'_msg'} || HTTP::Status::status_message($code) || "Unknown code";
73             return "$code $mess";
74             }
75              
76              
77             sub base
78 0     0 1 0 {
79 0         0 my $self = shift;
80             my $base = (
81             $self->header('Content-Base'), # used to be HTTP/1.1
82             $self->header('Content-Location'), # HTTP/1.1
83             $self->header('Base'), # HTTP/1.0
84 0 0 0     0 )[0];
85             if ($base && $base =~ /^$URI::scheme_re:/o) {
86 0         0 # already absolute
87             return $HTTP::URI_CLASS->new($base);
88             }
89 0         0  
90 0 0       0 my $req = $self->request;
91             if ($req) {
92             # if $base is undef here, the return value is effectively
93 0         0 # just a copy of $self->request->uri.
94             return $HTTP::URI_CLASS->new_abs($base, $req->uri);
95             }
96              
97 0         0 # can't find an absolute base
98             return undef;
99             }
100              
101              
102 23     23 1 588 sub redirects {
103 23         37 my $self = shift;
104 23         30 my @r;
105 23         84 my $r = $self;
106 0         0 while (my $p = $r->previous) {
107 0         0 push(@r, $p);
108             $r = $p;
109 23 50       264 }
110 0         0 return @r unless wantarray;
111             return reverse @r;
112             }
113              
114              
115             sub filename
116 0     0 1 0 {
117 0         0 my $self = shift;
118             my $file;
119 0         0  
120 0 0       0 my $cd = $self->header('Content-Disposition');
121 0         0 if ($cd) {
122 0 0       0 require HTTP::Headers::Util;
123 0         0 if (my @cd = HTTP::Headers::Util::split_header_words($cd)) {
  0         0  
124 0         0 my ($disposition, undef, %cd_param) = @{$cd[-1]};
125             $file = $cd_param{filename};
126              
127 0 0 0     0 # RFC 2047 encoded?
128 0         0 if ($file && $file =~ /^=\?(.+?)\?(.+?)\?(.+)\?=$/) {
129 0         0 my $charset = $1;
130 0         0 my $encoding = uc($2);
131             my $encfile = $3;
132 0 0 0     0  
133 0         0 if ($encoding eq 'Q' || $encoding eq 'B') {
134 0         0 local($SIG{__DIE__});
135 0 0       0 eval {
136 0         0 if ($encoding eq 'Q') {
137 0         0 $encfile =~ s/_/ /g;
138 0         0 require MIME::QuotedPrint;
139             $encfile = MIME::QuotedPrint::decode($encfile);
140             }
141 0         0 else { # $encoding eq 'B'
142 0         0 require MIME::Base64;
143             $encfile = MIME::Base64::decode($encfile);
144             }
145 0         0  
146 0         0 require Encode;
147 0         0 require Encode::Locale;
148             Encode::from_to($encfile, $charset, "locale_fs");
149             };
150 0 0       0  
151             $file = $encfile unless $@;
152             }
153             }
154             }
155             }
156 0 0 0     0  
157 0         0 unless (defined($file) && length($file)) {
158 0 0       0 my $uri;
    0          
159 0         0 if (my $cl = $self->header('Content-Location')) {
160             $uri = URI->new($cl);
161             }
162 0         0 elsif (my $request = $self->request) {
163             $uri = $request->uri;
164             }
165 0 0       0  
166 0         0 if ($uri) {
167             $file = ($uri->path_segments)[-1];
168             }
169             }
170 0 0       0  
171 0         0 if ($file) {
172             $file =~ s,.*[\\/],,; # basename
173             }
174 0 0 0     0  
175 0         0 if ($file && !length($file)) {
176             $file = undef;
177             }
178 0         0  
179             $file;
180             }
181              
182              
183             sub as_string
184 0     0 1 0 {
185 0         0 my $self = shift;
186 0 0       0 my($eol) = @_;
187             $eol = "\n" unless defined $eol;
188 0         0  
189 0         0 my $status_line = $self->status_line;
190 0 0       0 my $proto = $self->protocol;
191             $status_line = "$proto $status_line" if $proto;
192 0         0  
193             return join($eol, $status_line, $self->SUPER::as_string(@_));
194             }
195              
196              
197             sub dump
198 0     0 1 0 {
199             my $self = shift;
200 0         0  
201 0         0 my $status_line = $self->status_line;
202 0 0       0 my $proto = $self->protocol;
203             $status_line = "$proto $status_line" if $proto;
204 0         0  
205             return $self->SUPER::dump(
206             preheader => $status_line,
207             @_,
208             );
209             }
210              
211 0     0 1 0  
212 0     0 1 0 sub is_info { HTTP::Status::is_info (shift->{'_rc'}); }
213 0     0 1 0 sub is_success { HTTP::Status::is_success (shift->{'_rc'}); }
214 23     23 1 117 sub is_redirect { HTTP::Status::is_redirect (shift->{'_rc'}); }
215             sub is_error { HTTP::Status::is_error (shift->{'_rc'}); }
216              
217              
218             sub error_as_HTML
219 0     0 1   {
220 0           my $self = shift;
221 0           my $title = 'An Error Occurred';
222 0           my $body = $self->status_line;
223 0           $body =~ s/&/&/g;
224 0           $body =~ s/
225             return <
226            
227             $title
228            
229            

$title

230            

$body

231            
232            
233             EOM
234             }
235              
236              
237             sub current_age
238 0     0 1   {
239 0           my $self = shift;
240             my $time = shift;
241              
242             # Implementation of RFC 2616 section 13.2.3
243 0           # (age calculations)
244 0           my $response_time = $self->client_date;
245             my $date = $self->date;
246 0            
247 0 0 0       my $age = 0;
248 0           if ($response_time && $date) {
249 0 0         $age = $response_time - $date; # apparent_age
250             $age = 0 if $age < 0;
251             }
252 0            
253 0 0 0       my $age_v = $self->header('Age');
254 0           if ($age_v && $age_v > $age) {
255             $age = $age_v; # corrected_received_age
256             }
257 0 0          
258 0           if ($response_time) {
259 0 0         my $request = $self->request;
260 0           if ($request) {
261 0 0 0       my $request_time = $request->date;
262             if ($request_time && $request_time < $response_time) {
263 0           # Add response_delay to age to get 'corrected_initial_age'
264             $age += $response_time - $request_time;
265             }
266 0   0       }
267             $age += ($time || time) - $response_time;
268 0           }
269             return $age;
270             }
271              
272              
273             sub freshness_lifetime
274 0     0 1   {
275             my($self, %opt) = @_;
276              
277 0           # First look for the Cache-Control: max-age=n header
278 0           for my $cc ($self->header('Cache-Control')) {
279 0 0         for my $cc_dir (split(/\s*,\s*/, $cc)) {
280             return $1 if $cc_dir =~ /^max-age\s*=\s*(\d+)/i;
281             }
282             }
283              
284 0   0       # Next possibility is to look at the "Expires" header
285 0 0         my $date = $self->date || $self->client_date || $opt{time} || time;
286 0           if (my $expires = $self->expires) {
287             return $expires - $date;
288             }
289              
290 0 0 0       # Must apply heuristic expiration
291             return undef if exists $opt{heuristic_expiry} && !$opt{heuristic_expiry};
292              
293 0   0       # Default heuristic expiration parameters
294 0   0       $opt{h_min} ||= 60;
295 0   0       $opt{h_max} ||= 24 * 3600;
296 0   0       $opt{h_lastmod_fraction} ||= 0.10; # 10% since last-mod suggested by RFC2616
297             $opt{h_default} ||= 3600;
298              
299             # Should give a warning if more than 24 hours according to
300             # RFC 2616 section 13.2.4. Here we just make this the default
301             # maximum value.
302 0 0          
303 0           if (my $last_modified = $self->last_modified) {
304 0 0         my $h_exp = ($date - $last_modified) * $opt{h_lastmod_fraction};
305 0 0         return $opt{h_min} if $h_exp < $opt{h_min};
306 0           return $opt{h_max} if $h_exp > $opt{h_max};
307             return $h_exp;
308             }
309              
310 0 0         # default when all else fails
311 0           return $opt{h_min} if $opt{h_min} > $opt{h_default};
312             return $opt{h_default};
313             }
314              
315              
316             sub is_fresh
317 0     0 1   {
318 0   0       my($self, %opt) = @_;
319 0           $opt{time} ||= time;
320 0 0         my $f = $self->freshness_lifetime(%opt);
321 0           return undef unless defined($f);
322             return $f > $self->current_age($opt{time});
323             }
324              
325              
326             sub fresh_until
327 0     0 1   {
328 0   0       my($self, %opt) = @_;
329 0           $opt{time} ||= time;
330 0 0         my $f = $self->freshness_lifetime(%opt);
331 0           return undef unless defined($f);
332             return $f - $self->current_age($opt{time}) + $opt{time};
333             }
334              
335             1;
336              
337              
338             __END__