File: Handler.pm

package info (click to toggle)
libfuture-http-perl 0.17-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 276 kB
  • sloc: perl: 1,320; makefile: 2
file content (140 lines) | stat: -rw-r--r-- 5,219 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package Future::HTTP::Handler;
use Moo::Role;
use experimental 'signatures';

our $VERSION = '0.17';

=head1 NAME

Future::HTTP::Handler - common role for handling HTTP responses

=cut

has 'on_http_response' => (
    is => 'rw',
);

sub http_response_received( $self, $res, $body, $headers ) {
    $self->on_http_response( $res, $body, $headers )
        if $self->on_http_response;
    if( $headers->{Status} =~ /^[23]../ ) {
        $body = $self->decode_content( $body, $headers );
        $res->done($body, $headers);
    } else {
        $res->fail('error when connecting', $headers);
    }
}

no warnings 'once';
sub decode_content {
    my($self, $body, $headers) = @_;
    my $content_ref = \$body;
    my $content_ref_iscopy = 1;

    if (my $h = $headers->{'content-encoding'}) {
        $h =~ s/^\s+//;
        $h =~ s/\s+$//;
        for my $ce (reverse split(/\s*,\s*/, lc($h))) {
            next unless $ce;
            next if $ce eq "identity" || $ce eq "none";
            if ($ce eq "gzip" || $ce eq "x-gzip") {
                require IO::Uncompress::Gunzip;
                my $output;
                IO::Uncompress::Gunzip::gunzip($content_ref, \$output, Transparent => 0)
                    or die "Can't gunzip content: $IO::Uncompress::Gunzip::GunzipError";
                $content_ref = \$output;
                $content_ref_iscopy++;
            }
            elsif ($ce eq "x-bzip2" or $ce eq "bzip2") {
                require IO::Uncompress::Bunzip2;
                my $output;
                IO::Uncompress::Bunzip2::bunzip2($content_ref, \$output, Transparent => 0)
                or die "Can't bunzip content: $IO::Uncompress::Bunzip2::Bunzip2Error";
                $content_ref = \$output;
                $content_ref_iscopy++;
            }
            elsif ($ce eq "deflate") {
                require IO::Uncompress::Inflate;
                my $output;
                my $status = IO::Uncompress::Inflate::inflate($content_ref, \$output, Transparent => 0);
                my $error = $IO::Uncompress::Inflate::InflateError;
                unless ($status) {
                # "Content-Encoding: deflate" is supposed to mean the
                # "zlib" format of RFC 1950, but Microsoft got that
                # wrong, so some servers sends the raw compressed
                # "deflate" data.  This tries to inflate this format.
                $output = undef;
                require IO::Uncompress::RawInflate;
                unless (IO::Uncompress::RawInflate::rawinflate($content_ref, \$output)) {
                    #$self->push_header("Client-Warning" =>
                    #"Could not raw inflate content: $IO::Uncompress::RawInflate::RawInflateError");
                    $output = undef;
                }
                }
                die "Can't inflate content: $error" unless defined $output;
                $content_ref = \$output;
                $content_ref_iscopy++;
            }
            elsif ($ce eq "compress" || $ce eq "x-compress") {
                die "Can't uncompress content";
            }
            elsif ($ce eq "base64") {  # not really C-T-E, but should be harmless
                require MIME::Base64;
                $content_ref = \MIME::Base64::decode($$content_ref);
                $content_ref_iscopy++;
            }
            elsif ($ce eq "quoted-printable") { # not really C-T-E, but should be harmless
                require MIME::QuotedPrint;
                $content_ref = \MIME::QuotedPrint::decode($$content_ref);
                $content_ref_iscopy++;
            }
            else {
                die "Don't know how to decode Content-Encoding '$ce'";
            }
        }
    }

    return $$content_ref
}

sub mirror( $self, $url, $outfile, $args ) {
    if ( exists $args->{headers} ) {
        my $headers = {};
        while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
            $headers->{lc $key} = $value;
        }
        $args->{headers} = $headers;
    }

    if ( -e $outfile and my $mtime = (stat($outfile))[9] ) {
        $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
    }
    my $tempfile = $outfile . int(rand(2**31));

    require Fcntl;
    sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY()
         or croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/);
    binmode $fh;
    $args->{on_body} = sub { print {$fh} $_[0] };
    my $response_f = $self->request('GET', $url, $args)->on_done(sub( $response_f ) {
        close $fh
            or croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/);

        if ( $response_f->is_success ) {
            my $response = $response_f->get;
            rename $tempfile, $outfile
                or _croak(qq/Error replacing $outfile with $tempfile: $!\n/);
            my $lm = $response->{headers}{'last-modified'};
            if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
                utime $mtime, $mtime, $outfile;
            }
        }
        $response_f->{success} ||= $response_f->{status} eq '304';
        unlink $tempfile;

        $response_f
    });
    return $response_f;
}

1;