File Coverage

blib/lib/Future/HTTP/Handler.pm
Criterion Covered Total %
statement 24 97 24.7
branch 3 48 6.2
condition 0 24 0.0
subroutine 5 8 62.5
pod 0 3 0.0
total 32 180 17.7


line stmt bran cond sub pod time code
1             package Future::HTTP::Handler;
2 6     6   74110 use Moo::Role;
  6         17  
  6         45  
3 6     6   3414 use experimental 'signatures';
  6         23  
  6         67  
4              
5             our $VERSION = '0.17';
6              
7             =head1 NAME
8              
9             Future::HTTP::Handler - common role for handling HTTP responses
10              
11             =cut
12              
13             has 'on_http_response' => (
14             is => 'rw',
15             );
16              
17 3     3 0 6 sub http_response_received( $self, $res, $body, $headers ) {
  3         7  
  3         24  
  3         6  
  3         7  
  3         6  
18 3 50       38 $self->on_http_response( $res, $body, $headers )
19             if $self->on_http_response;
20 3 50       24 if( $headers->{Status} =~ /^[23]../ ) {
21 3         15 $body = $self->decode_content( $body, $headers );
22 3         44 $res->done($body, $headers);
23             } else {
24 0         0 $res->fail('error when connecting', $headers);
25             }
26             }
27              
28 6     6   2707 no warnings 'once';
  6         96  
  6         10553  
29             sub decode_content {
30 3     3 0 9 my($self, $body, $headers) = @_;
31 3         8 my $content_ref = \$body;
32 3         7 my $content_ref_iscopy = 1;
33              
34 3 50       12 if (my $h = $headers->{'content-encoding'}) {
35 0         0 $h =~ s/^\s+//;
36 0         0 $h =~ s/\s+$//;
37 0         0 for my $ce (reverse split(/\s*,\s*/, lc($h))) {
38 0 0       0 next unless $ce;
39 0 0 0     0 next if $ce eq "identity" || $ce eq "none";
40 0 0 0     0 if ($ce eq "gzip" || $ce eq "x-gzip") {
    0 0        
    0 0        
    0          
    0          
    0          
41 0         0 require IO::Uncompress::Gunzip;
42 0         0 my $output;
43 0 0       0 IO::Uncompress::Gunzip::gunzip($content_ref, \$output, Transparent => 0)
44             or die "Can't gunzip content: $IO::Uncompress::Gunzip::GunzipError";
45 0         0 $content_ref = \$output;
46 0         0 $content_ref_iscopy++;
47             }
48             elsif ($ce eq "x-bzip2" or $ce eq "bzip2") {
49 0         0 require IO::Uncompress::Bunzip2;
50 0         0 my $output;
51 0 0       0 IO::Uncompress::Bunzip2::bunzip2($content_ref, \$output, Transparent => 0)
52             or die "Can't bunzip content: $IO::Uncompress::Bunzip2::Bunzip2Error";
53 0         0 $content_ref = \$output;
54 0         0 $content_ref_iscopy++;
55             }
56             elsif ($ce eq "deflate") {
57 0         0 require IO::Uncompress::Inflate;
58 0         0 my $output;
59 0         0 my $status = IO::Uncompress::Inflate::inflate($content_ref, \$output, Transparent => 0);
60 0         0 my $error = $IO::Uncompress::Inflate::InflateError;
61 0 0       0 unless ($status) {
62             # "Content-Encoding: deflate" is supposed to mean the
63             # "zlib" format of RFC 1950, but Microsoft got that
64             # wrong, so some servers sends the raw compressed
65             # "deflate" data. This tries to inflate this format.
66 0         0 $output = undef;
67 0         0 require IO::Uncompress::RawInflate;
68 0 0       0 unless (IO::Uncompress::RawInflate::rawinflate($content_ref, \$output)) {
69             #$self->push_header("Client-Warning" =>
70             #"Could not raw inflate content: $IO::Uncompress::RawInflate::RawInflateError");
71 0         0 $output = undef;
72             }
73             }
74 0 0       0 die "Can't inflate content: $error" unless defined $output;
75 0         0 $content_ref = \$output;
76 0         0 $content_ref_iscopy++;
77             }
78             elsif ($ce eq "compress" || $ce eq "x-compress") {
79 0         0 die "Can't uncompress content";
80             }
81             elsif ($ce eq "base64") { # not really C-T-E, but should be harmless
82 0         0 require MIME::Base64;
83 0         0 $content_ref = \MIME::Base64::decode($$content_ref);
84 0         0 $content_ref_iscopy++;
85             }
86             elsif ($ce eq "quoted-printable") { # not really C-T-E, but should be harmless
87 0         0 require MIME::QuotedPrint;
88 0         0 $content_ref = \MIME::QuotedPrint::decode($$content_ref);
89 0         0 $content_ref_iscopy++;
90             }
91             else {
92 0         0 die "Don't know how to decode Content-Encoding '$ce'";
93             }
94             }
95             }
96              
97 3         9 return $$content_ref
98             }
99              
100 0     0 0   sub mirror( $self, $url, $outfile, $args ) {
  0            
  0            
  0            
  0            
  0            
101 0 0         if ( exists $args->{headers} ) {
102 0           my $headers = {};
103 0 0         while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
  0            
104 0           $headers->{lc $key} = $value;
105             }
106 0           $args->{headers} = $headers;
107             }
108              
109 0 0 0       if ( -e $outfile and my $mtime = (stat($outfile))[9] ) {
110 0   0       $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
111             }
112 0           my $tempfile = $outfile . int(rand(2**31));
113              
114 0           require Fcntl;
115 0 0         sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY()
116             or croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/);
117 0           binmode $fh;
118 0     0     $args->{on_body} = sub { print {$fh} $_[0] };
  0            
  0            
119 0     0     my $response_f = $self->request('GET', $url, $args)->on_done(sub( $response_f ) {
  0            
  0            
120 0 0         close $fh
121             or croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/);
122              
123 0 0         if ( $response_f->is_success ) {
124 0           my $response = $response_f->get;
125 0 0         rename $tempfile, $outfile
126             or _croak(qq/Error replacing $outfile with $tempfile: $!\n/);
127 0           my $lm = $response->{headers}{'last-modified'};
128 0 0 0       if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
129 0           utime $mtime, $mtime, $outfile;
130             }
131             }
132 0   0       $response_f->{success} ||= $response_f->{status} eq '304';
133 0           unlink $tempfile;
134              
135 0           $response_f
136 0           });
137 0           return $response_f;
138             }
139              
140             1;