| Filename | /usr/share/perl5/HTTP/Response.pm | 
| Statements | Executed 10 statements in 1.83ms | 
| Calls | P | F | Exclusive Time | Inclusive Time | Subroutine | 
|---|---|---|---|---|---|
| 1 | 1 | 1 | 24µs | 30µs | HTTP::Response::BEGIN@7 | 
| 1 | 1 | 1 | 7µs | 7µs | HTTP::Response::BEGIN@8 | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::as_string | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::base | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::clone | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::code | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::current_age | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::dump | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::error_as_HTML | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::filename | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::fresh_until | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::freshness_lifetime | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::is_error | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::is_fresh | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::is_info | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::is_redirect | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::is_success | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::message | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::new | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::parse | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::previous | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::redirects | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::request | 
| 0 | 0 | 0 | 0s | 0s | HTTP::Response::status_line | 
| Line | State ments | Time on line | Calls | Time in subs | Code | 
|---|---|---|---|---|---|
| 1 | package HTTP::Response; | ||||
| 2 | |||||
| 3 | 1 | 1µs | require HTTP::Message; | ||
| 4 | 1 | 10µs | @ISA = qw(HTTP::Message); | ||
| 5 | 1 | 600ns | $VERSION = "5.836"; | ||
| 6 | |||||
| 7 | 3 | 35µs | 2 | 36µs | # spent 30µs (24+6) within HTTP::Response::BEGIN@7 which was called:
#    once (24µs+6µs) by LWP::UserAgent::BEGIN@11 at line 7 # spent    30µs making 1 call to HTTP::Response::BEGIN@7
# spent     6µs making 1 call to strict::import | 
| 8 | 3 | 1.78ms | 1 | 7µs | # spent 7µs within HTTP::Response::BEGIN@8 which was called:
#    once (7µs+0s) by LWP::UserAgent::BEGIN@11 at line 8 # spent     7µs making 1 call to HTTP::Response::BEGIN@8 | 
| 9 | |||||
| - - | |||||
| 12 | sub new | ||||
| 13 | { | ||||
| 14 | my($class, $rc, $msg, $header, $content) = @_; | ||||
| 15 | my $self = $class->SUPER::new($header, $content); | ||||
| 16 | $self->code($rc); | ||||
| 17 | $self->message($msg); | ||||
| 18 | $self; | ||||
| 19 | } | ||||
| 20 | |||||
| 21 | |||||
| 22 | sub parse | ||||
| 23 | { | ||||
| 24 | my($class, $str) = @_; | ||||
| 25 | my $status_line; | ||||
| 26 | if ($str =~ s/^(.*)\n//) { | ||||
| 27 | $status_line = $1; | ||||
| 28 | } | ||||
| 29 | else { | ||||
| 30 | $status_line = $str; | ||||
| 31 | $str = ""; | ||||
| 32 | } | ||||
| 33 | |||||
| 34 | my $self = $class->SUPER::parse($str); | ||||
| 35 | my($protocol, $code, $message); | ||||
| 36 | if ($status_line =~ /^\d{3} /) { | ||||
| 37 | # Looks like a response created by HTTP::Response->new | ||||
| 38 | ($code, $message) = split(' ', $status_line, 2); | ||||
| 39 | } else { | ||||
| 40 | ($protocol, $code, $message) = split(' ', $status_line, 3); | ||||
| 41 | } | ||||
| 42 | $self->protocol($protocol) if $protocol; | ||||
| 43 | $self->code($code) if defined($code); | ||||
| 44 | $self->message($message) if defined($message); | ||||
| 45 | $self; | ||||
| 46 | } | ||||
| 47 | |||||
| 48 | |||||
| 49 | sub clone | ||||
| 50 | { | ||||
| 51 | my $self = shift; | ||||
| 52 | my $clone = bless $self->SUPER::clone, ref($self); | ||||
| 53 | $clone->code($self->code); | ||||
| 54 | $clone->message($self->message); | ||||
| 55 | $clone->request($self->request->clone) if $self->request; | ||||
| 56 | # we don't clone previous | ||||
| 57 | $clone; | ||||
| 58 | } | ||||
| 59 | |||||
| 60 | |||||
| 61 | sub code { shift->_elem('_rc', @_); } | ||||
| 62 | sub message { shift->_elem('_msg', @_); } | ||||
| 63 | sub previous { shift->_elem('_previous',@_); } | ||||
| 64 | sub request { shift->_elem('_request', @_); } | ||||
| 65 | |||||
| 66 | |||||
| 67 | sub status_line | ||||
| 68 | { | ||||
| 69 | my $self = shift; | ||||
| 70 | my $code = $self->{'_rc'} || "000"; | ||||
| 71 | my $mess = $self->{'_msg'} || HTTP::Status::status_message($code) || "Unknown code"; | ||||
| 72 | return "$code $mess"; | ||||
| 73 | } | ||||
| 74 | |||||
| 75 | |||||
| 76 | sub base | ||||
| 77 | { | ||||
| 78 | my $self = shift; | ||||
| 79 | my $base = ( | ||||
| 80 | $self->header('Content-Base'), # used to be HTTP/1.1 | ||||
| 81 | $self->header('Content-Location'), # HTTP/1.1 | ||||
| 82 | $self->header('Base'), # HTTP/1.0 | ||||
| 83 | )[0]; | ||||
| 84 | if ($base && $base =~ /^$URI::scheme_re:/o) { | ||||
| 85 | # already absolute | ||||
| 86 | return $HTTP::URI_CLASS->new($base); | ||||
| 87 | } | ||||
| 88 | |||||
| 89 | my $req = $self->request; | ||||
| 90 | if ($req) { | ||||
| 91 | # if $base is undef here, the return value is effectively | ||||
| 92 | # just a copy of $self->request->uri. | ||||
| 93 | return $HTTP::URI_CLASS->new_abs($base, $req->uri); | ||||
| 94 | } | ||||
| 95 | |||||
| 96 | # can't find an absolute base | ||||
| 97 | return undef; | ||||
| 98 | } | ||||
| 99 | |||||
| 100 | |||||
| 101 | sub redirects { | ||||
| 102 | my $self = shift; | ||||
| 103 | my @r; | ||||
| 104 | my $r = $self; | ||||
| 105 | while (my $p = $r->previous) { | ||||
| 106 | push(@r, $p); | ||||
| 107 | $r = $p; | ||||
| 108 | } | ||||
| 109 | return @r unless wantarray; | ||||
| 110 | return reverse @r; | ||||
| 111 | } | ||||
| 112 | |||||
| 113 | |||||
| 114 | sub filename | ||||
| 115 | { | ||||
| 116 | my $self = shift; | ||||
| 117 | my $file; | ||||
| 118 | |||||
| 119 | my $cd = $self->header('Content-Disposition'); | ||||
| 120 | if ($cd) { | ||||
| 121 | require HTTP::Headers::Util; | ||||
| 122 | if (my @cd = HTTP::Headers::Util::split_header_words($cd)) { | ||||
| 123 | my ($disposition, undef, %cd_param) = @{$cd[-1]}; | ||||
| 124 | $file = $cd_param{filename}; | ||||
| 125 | |||||
| 126 | # RFC 2047 encoded? | ||||
| 127 | if ($file && $file =~ /^=\?(.+?)\?(.+?)\?(.+)\?=$/) { | ||||
| 128 | my $charset = $1; | ||||
| 129 | my $encoding = uc($2); | ||||
| 130 | my $encfile = $3; | ||||
| 131 | |||||
| 132 | if ($encoding eq 'Q' || $encoding eq 'B') { | ||||
| 133 | local($SIG{__DIE__}); | ||||
| 134 | eval { | ||||
| 135 | if ($encoding eq 'Q') { | ||||
| 136 | $encfile =~ s/_/ /g; | ||||
| 137 | require MIME::QuotedPrint; | ||||
| 138 | $encfile = MIME::QuotedPrint::decode($encfile); | ||||
| 139 | } | ||||
| 140 | else { # $encoding eq 'B' | ||||
| 141 | require MIME::Base64; | ||||
| 142 | $encfile = MIME::Base64::decode($encfile); | ||||
| 143 | } | ||||
| 144 | |||||
| 145 | require Encode; | ||||
| 146 | require encoding; | ||||
| 147 | # This is ugly use of non-public API, but is there | ||||
| 148 | # a better way to accomplish what we want (locally | ||||
| 149 | # as-is usable filename string)? | ||||
| 150 | my $locale_charset = encoding::_get_locale_encoding(); | ||||
| 151 | Encode::from_to($encfile, $charset, $locale_charset); | ||||
| 152 | }; | ||||
| 153 | |||||
| 154 | $file = $encfile unless $@; | ||||
| 155 | } | ||||
| 156 | } | ||||
| 157 | } | ||||
| 158 | } | ||||
| 159 | |||||
| 160 | unless (defined($file) && length($file)) { | ||||
| 161 | my $uri; | ||||
| 162 | if (my $cl = $self->header('Content-Location')) { | ||||
| 163 | $uri = URI->new($cl); | ||||
| 164 | } | ||||
| 165 | elsif (my $request = $self->request) { | ||||
| 166 | $uri = $request->uri; | ||||
| 167 | } | ||||
| 168 | |||||
| 169 | if ($uri) { | ||||
| 170 | $file = ($uri->path_segments)[-1]; | ||||
| 171 | } | ||||
| 172 | } | ||||
| 173 | |||||
| 174 | if ($file) { | ||||
| 175 | $file =~ s,.*[\\/],,; # basename | ||||
| 176 | } | ||||
| 177 | |||||
| 178 | if ($file && !length($file)) { | ||||
| 179 | $file = undef; | ||||
| 180 | } | ||||
| 181 | |||||
| 182 | $file; | ||||
| 183 | } | ||||
| 184 | |||||
| 185 | |||||
| 186 | sub as_string | ||||
| 187 | { | ||||
| 188 | require HTTP::Status; | ||||
| 189 | my $self = shift; | ||||
| 190 | my($eol) = @_; | ||||
| 191 | $eol = "\n" unless defined $eol; | ||||
| 192 | |||||
| 193 | my $status_line = $self->status_line; | ||||
| 194 | my $proto = $self->protocol; | ||||
| 195 | $status_line = "$proto $status_line" if $proto; | ||||
| 196 | |||||
| 197 | return join($eol, $status_line, $self->SUPER::as_string(@_)); | ||||
| 198 | } | ||||
| 199 | |||||
| 200 | |||||
| 201 | sub dump | ||||
| 202 | { | ||||
| 203 | my $self = shift; | ||||
| 204 | |||||
| 205 | my $status_line = $self->status_line; | ||||
| 206 | my $proto = $self->protocol; | ||||
| 207 | $status_line = "$proto $status_line" if $proto; | ||||
| 208 | |||||
| 209 | return $self->SUPER::dump( | ||||
| 210 | preheader => $status_line, | ||||
| 211 | @_, | ||||
| 212 | ); | ||||
| 213 | } | ||||
| 214 | |||||
| 215 | |||||
| 216 | sub is_info { HTTP::Status::is_info (shift->{'_rc'}); } | ||||
| 217 | sub is_success { HTTP::Status::is_success (shift->{'_rc'}); } | ||||
| 218 | sub is_redirect { HTTP::Status::is_redirect (shift->{'_rc'}); } | ||||
| 219 | sub is_error { HTTP::Status::is_error (shift->{'_rc'}); } | ||||
| 220 | |||||
| 221 | |||||
| 222 | sub error_as_HTML | ||||
| 223 | { | ||||
| 224 | require HTML::Entities; | ||||
| 225 | my $self = shift; | ||||
| 226 | my $title = 'An Error Occurred'; | ||||
| 227 | my $body = HTML::Entities::encode($self->status_line); | ||||
| 228 | return <<EOM; | ||||
| 229 | <html> | ||||
| 230 | <head><title>$title</title></head> | ||||
| 231 | <body> | ||||
| 232 | <h1>$title</h1> | ||||
| 233 | <p>$body</p> | ||||
| 234 | </body> | ||||
| 235 | </html> | ||||
| 236 | EOM | ||||
| 237 | } | ||||
| 238 | |||||
| 239 | |||||
| 240 | sub current_age | ||||
| 241 | { | ||||
| 242 | my $self = shift; | ||||
| 243 | my $time = shift; | ||||
| 244 | |||||
| 245 | # Implementation of RFC 2616 section 13.2.3 | ||||
| 246 | # (age calculations) | ||||
| 247 | my $response_time = $self->client_date; | ||||
| 248 | my $date = $self->date; | ||||
| 249 | |||||
| 250 | my $age = 0; | ||||
| 251 | if ($response_time && $date) { | ||||
| 252 | $age = $response_time - $date; # apparent_age | ||||
| 253 | $age = 0 if $age < 0; | ||||
| 254 | } | ||||
| 255 | |||||
| 256 | my $age_v = $self->header('Age'); | ||||
| 257 | if ($age_v && $age_v > $age) { | ||||
| 258 | $age = $age_v; # corrected_received_age | ||||
| 259 | } | ||||
| 260 | |||||
| 261 | if ($response_time) { | ||||
| 262 | my $request = $self->request; | ||||
| 263 | if ($request) { | ||||
| 264 | my $request_time = $request->date; | ||||
| 265 | if ($request_time && $request_time < $response_time) { | ||||
| 266 | # Add response_delay to age to get 'corrected_initial_age' | ||||
| 267 | $age += $response_time - $request_time; | ||||
| 268 | } | ||||
| 269 | } | ||||
| 270 | $age += ($time || time) - $response_time; | ||||
| 271 | } | ||||
| 272 | return $age; | ||||
| 273 | } | ||||
| 274 | |||||
| 275 | |||||
| 276 | sub freshness_lifetime | ||||
| 277 | { | ||||
| 278 | my($self, %opt) = @_; | ||||
| 279 | |||||
| 280 | # First look for the Cache-Control: max-age=n header | ||||
| 281 | for my $cc ($self->header('Cache-Control')) { | ||||
| 282 | for my $cc_dir (split(/\s*,\s*/, $cc)) { | ||||
| 283 | return $1 if $cc_dir =~ /^max-age\s*=\s*(\d+)/i; | ||||
| 284 | } | ||||
| 285 | } | ||||
| 286 | |||||
| 287 | # Next possibility is to look at the "Expires" header | ||||
| 288 | my $date = $self->date || $self->client_date || $opt{time} || time; | ||||
| 289 | if (my $expires = $self->expires) { | ||||
| 290 | return $expires - $date; | ||||
| 291 | } | ||||
| 292 | |||||
| 293 | # Must apply heuristic expiration | ||||
| 294 | return undef if exists $opt{heuristic_expiry} && !$opt{heuristic_expiry}; | ||||
| 295 | |||||
| 296 | # Default heuristic expiration parameters | ||||
| 297 | $opt{h_min} ||= 60; | ||||
| 298 | $opt{h_max} ||= 24 * 3600; | ||||
| 299 | $opt{h_lastmod_fraction} ||= 0.10; # 10% since last-mod suggested by RFC2616 | ||||
| 300 | $opt{h_default} ||= 3600; | ||||
| 301 | |||||
| 302 | # Should give a warning if more than 24 hours according to | ||||
| 303 | # RFC 2616 section 13.2.4. Here we just make this the default | ||||
| 304 | # maximum value. | ||||
| 305 | |||||
| 306 | if (my $last_modified = $self->last_modified) { | ||||
| 307 | my $h_exp = ($date - $last_modified) * $opt{h_lastmod_fraction}; | ||||
| 308 | return $opt{h_min} if $h_exp < $opt{h_min}; | ||||
| 309 | return $opt{h_max} if $h_exp > $opt{h_max}; | ||||
| 310 | return $h_exp; | ||||
| 311 | } | ||||
| 312 | |||||
| 313 | # default when all else fails | ||||
| 314 | return $opt{h_min} if $opt{h_min} > $opt{h_default}; | ||||
| 315 | return $opt{h_default}; | ||||
| 316 | } | ||||
| 317 | |||||
| 318 | |||||
| 319 | sub is_fresh | ||||
| 320 | { | ||||
| 321 | my($self, %opt) = @_; | ||||
| 322 | $opt{time} ||= time; | ||||
| 323 | my $f = $self->freshness_lifetime(%opt); | ||||
| 324 | return undef unless defined($f); | ||||
| 325 | return $f > $self->current_age($opt{time}); | ||||
| 326 | } | ||||
| 327 | |||||
| 328 | |||||
| 329 | sub fresh_until | ||||
| 330 | { | ||||
| 331 | my($self, %opt) = @_; | ||||
| 332 | $opt{time} ||= time; | ||||
| 333 | my $f = $self->freshness_lifetime(%opt); | ||||
| 334 | return undef unless defined($f); | ||||
| 335 | return $f - $self->current_age($opt{time}) + $opt{time}; | ||||
| 336 | } | ||||
| 337 | |||||
| 338 | 1 | 4µs | 1; | ||
| 339 | |||||
| 340 | |||||
| 341 | __END__ |