| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Webqq::UserAgent; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 575 | use AnyEvent::HTTP (); | 
|  | 1 |  |  |  |  | 24465 |  | 
|  | 1 |  |  |  |  | 34 |  | 
| 4 | 1 |  |  | 1 |  | 1409 | use HTTP::Cookies (); | 
|  | 1 |  |  |  |  | 5426 |  | 
|  | 1 |  |  |  |  | 44 |  | 
| 5 | 1 |  |  | 1 |  | 6 | use HTTP::Request (); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 11 |  | 
| 6 | 1 |  |  | 1 |  | 456 | use HTTP::Request::Common (); | 
|  | 1 |  |  |  |  | 1640 |  | 
|  | 1 |  |  |  |  | 18 |  | 
| 7 | 1 |  |  | 1 |  | 5 | use HTTP::Response (); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 213 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | sub new { | 
| 10 | 0 |  |  | 0 | 0 |  | my $class = shift; | 
| 11 | 0 |  |  |  |  |  | my %p = @_; | 
| 12 | 0 |  | 0 |  |  |  | return bless { | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 13 |  |  |  |  |  |  | agent               =>  $p{agent} || $AnyEvent::HTTP::USERAGENT . ' AnyEvent-UserAgent/' . $VERSION , | 
| 14 |  |  |  |  |  |  | cookie_jar          =>  $p{cookie_jar} || HTTP::Cookies->new, | 
| 15 |  |  |  |  |  |  | max_redirects       =>  $p{max_redirects} || 5, | 
| 16 |  |  |  |  |  |  | inactivity_timeout  =>  $p{inactivity_timeout} || 20, | 
| 17 |  |  |  |  |  |  | request_timeout     =>  $p{request_timeout} || 0 | 
| 18 |  |  |  |  |  |  | },$class; | 
| 19 |  |  |  |  |  |  | } | 
| 20 |  |  |  |  |  |  | sub request { | 
| 21 | 0 |  |  | 0 | 0 |  | my $cb = pop(); | 
| 22 | 0 |  |  |  |  |  | my ($self, $req, %opts) = @_; | 
| 23 |  |  |  |  |  |  | $self->_request($req, \%opts, sub { | 
| 24 | 0 |  |  | 0 |  |  | $self->_response($req, @_, $cb); | 
| 25 | 0 |  |  |  |  |  | }); | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 0 |  |  | 0 | 0 |  | sub get    { _make_request(GET    => @_) } | 
| 29 | 0 |  |  | 0 | 0 |  | sub head   { _make_request(HEAD   => @_) } | 
| 30 | 0 |  |  | 0 | 0 |  | sub put    { _make_request(PUT    => @_) } | 
| 31 | 0 |  |  | 0 | 0 |  | sub delete { _make_request(DELETE => @_) } | 
| 32 | 0 |  |  | 0 | 0 |  | sub post   { _make_request(POST   => @_) } | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | sub _make_request { | 
| 35 | 0 |  |  | 0 |  |  | my $cb   = pop(); | 
| 36 | 0 |  |  |  |  |  | my $meth = shift(); | 
| 37 | 0 |  |  |  |  |  | my $self = shift(); | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 1 |  |  | 1 |  | 5 | no strict 'refs'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 858 |  | 
| 40 | 0 |  |  |  |  |  | $self->request(&{'HTTP::Request::Common::' . $meth}(@_), $cb); | 
|  | 0 |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | sub _request { | 
| 44 | 0 |  |  | 0 |  |  | my ($self, $req, $opts, $cb) = @_; | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 0 |  |  |  |  |  | my $uri  = $req->uri; | 
| 47 | 0 |  |  |  |  |  | my $hdrs = $req->headers; | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 0 | 0 |  |  |  |  | unless ($hdrs->user_agent) { | 
| 50 | 0 |  |  |  |  |  | $hdrs->user_agent($self->{agent}); | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 0 | 0 | 0 |  |  |  | if ($uri->can('userinfo') && $uri->userinfo && !$hdrs->authorization) { | 
|  |  |  | 0 |  |  |  |  | 
| 54 | 0 |  |  |  |  |  | $hdrs->authorization_basic(split(':', $uri->userinfo, 2)); | 
| 55 |  |  |  |  |  |  | } | 
| 56 | 0 | 0 |  |  |  |  | if ($uri->scheme) { | 
| 57 | 0 |  |  |  |  |  | $self->{cookie_jar}->add_cookie_header($req); | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 |  |  |  |  |  | for (qw(max_redirects inactivity_timeout request_timeout)) { | 
| 61 | 0 | 0 |  |  |  |  | $opts->{$_} = $self->{$_} unless exists($opts->{$_}); | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 0 |  |  |  |  |  | my ($grd, $tmr); | 
| 65 |  |  |  |  |  |  |  | 
| 66 | 0 | 0 |  |  |  |  | if ($opts->{request_timeout}) { | 
| 67 |  |  |  |  |  |  | $tmr = AE::timer $opts->{request_timeout}, 0, sub { | 
| 68 | 0 |  |  | 0 |  |  | undef($grd); | 
| 69 | 0 |  |  |  |  |  | $cb->($opts, undef, {Status => 597, Reason => 'Request timeout'}); | 
| 70 | 0 |  |  |  |  |  | }; | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 0 |  |  |  |  |  | $grd = AnyEvent::HTTP::http_request( | 
| 73 |  |  |  |  |  |  | $req->method, | 
| 74 |  |  |  |  |  |  | $req->uri, | 
| 75 | 0 |  |  |  |  |  | headers => {map { $_ => $hdrs->header($_) } $hdrs->header_field_names}, | 
| 76 |  |  |  |  |  |  | body    => $req->content, | 
| 77 |  |  |  |  |  |  | recurse => 0, | 
| 78 |  |  |  |  |  |  | timeout => $opts->{inactivity_timeout}, | 
| 79 | 0 |  |  |  |  |  | (map { $_ => $opts->{$_} } grep { exists($opts->{$_}) } | 
| 80 |  |  |  |  |  |  | qw(proxy tls_ctx session timeout on_prepare tcp_connect on_header | 
| 81 |  |  |  |  |  |  | on_body want_body_handle persistent keepalive handle_params)), | 
| 82 |  |  |  |  |  |  | sub { | 
| 83 | 0 |  |  | 0 |  |  | undef($grd); | 
| 84 | 0 |  |  |  |  |  | undef($tmr); | 
| 85 | 0 |  |  |  |  |  | $cb->($opts, @_); | 
| 86 |  |  |  |  |  |  | } | 
| 87 | 0 |  |  |  |  |  | ); | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | sub _response { | 
| 91 | 0 |  |  | 0 |  |  | my $cb = pop(); | 
| 92 | 0 |  |  |  |  |  | my ($self, $req, $opts, $body, $hdrs, $prev, $count) = @_; | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 0 |  |  |  |  |  | my $res = HTTP::Response->new(delete($hdrs->{Status}), delete($hdrs->{Reason})); | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 0 |  |  |  |  |  | $res->request($req); | 
| 97 | 0 | 0 |  |  |  |  | $res->previous($prev) if $prev; | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 0 |  |  |  |  |  | delete($hdrs->{URL}); | 
| 100 | 0 | 0 |  |  |  |  | if (defined($hdrs->{HTTPVersion})) { | 
| 101 | 0 |  |  |  |  |  | $res->protocol('HTTP/' . delete($hdrs->{HTTPVersion})); | 
| 102 |  |  |  |  |  |  | } | 
| 103 | 0 | 0 |  |  |  |  | if (my $hdr = $hdrs->{'set-cookie'}) { | 
| 104 |  |  |  |  |  |  | # Split comma-concatenated "Set-Cookie" values. | 
| 105 |  |  |  |  |  |  | # Based on RFC 6265, section 4.1.1. | 
| 106 | 0 |  |  |  |  |  | local @_ = split(/,([\w.!"'%\$&*+-^`]+=)/, ',' . $hdr); | 
| 107 | 0 |  |  |  |  |  | shift(); | 
| 108 | 0 |  |  |  |  |  | my @val; | 
| 109 | 0 |  |  |  |  |  | push(@val, join('', shift(), shift())) while @_; | 
| 110 | 0 |  |  |  |  |  | $hdrs->{'set-cookie'} = \@val; | 
| 111 |  |  |  |  |  |  | } | 
| 112 | 0 | 0 |  |  |  |  | if (keys(%$hdrs)) { | 
| 113 | 0 |  |  |  |  |  | $res->header(%$hdrs); | 
| 114 |  |  |  |  |  |  | } | 
| 115 | 0 | 0 | 0 |  |  |  | if ($res->code >= 590 && $res->code <= 599 && $res->message) { | 
|  |  |  | 0 |  |  |  |  | 
| 116 | 0 | 0 |  |  |  |  | if ($res->message eq 'Connection timed out') { | 
| 117 | 0 |  |  |  |  |  | $res->message('Inactivity timeout'); | 
| 118 |  |  |  |  |  |  | } | 
| 119 | 0 | 0 |  |  |  |  | unless ($res->header('client-warning')) { | 
| 120 | 0 |  |  |  |  |  | $res->header('client-warning' => $res->message); | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  | } | 
| 123 | 0 | 0 |  |  |  |  | if (defined($body)) { | 
| 124 | 0 |  |  |  |  |  | $res->content_ref(\$body); | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 0 |  |  |  |  |  | $self->{cookie_jar}->extract_cookies($res); | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 0 |  |  |  |  |  | my $code = $res->code; | 
| 129 |  |  |  |  |  |  |  | 
| 130 | 0 | 0 | 0 |  |  |  | if ($code == 301 || $code == 302 || $code == 303 || $code == 307 || $code == 308) { | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 131 | 0 |  |  |  |  |  | $self->_redirect($req, $opts, $code, $res, $count, $cb); | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | else { | 
| 134 | 0 |  |  |  |  |  | $cb->($res); | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub _redirect { | 
| 139 | 0 |  |  | 0 |  |  | my ($self, $req, $opts, $code, $prev, $count, $cb) = @_; | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 0 | 0 |  |  |  |  | unless (defined($count) ? $count : ($count = $opts->{max_redirects})) { | 
|  |  | 0 |  |  |  |  |  | 
| 142 | 0 |  |  |  |  |  | $prev->header('client-warning' => 'Redirect loop detected (max_redirects = ' . $opts->{max_redirects} . ')'); | 
| 143 | 0 |  |  |  |  |  | $cb->($prev); | 
| 144 | 0 |  |  |  |  |  | return; | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 0 |  |  |  |  |  | my $meth  = $req->method; | 
| 148 | 0 |  |  |  |  |  | my $proto = $req->uri->scheme; | 
| 149 | 0 |  |  |  |  |  | my $uri   = $prev->header('location'); | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 0 |  |  |  |  |  | $req = $req->clone(); | 
| 152 | 0 |  |  |  |  |  | $req->remove_header('cookie'); | 
| 153 | 0 | 0 | 0 |  |  |  | if (($code == 302 || $code == 303) && !($meth eq 'GET' || $meth eq 'HEAD')) { | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 154 | 0 |  |  |  |  |  | $req->method('GET'); | 
| 155 | 0 |  |  |  |  |  | $req->content(''); | 
| 156 | 0 |  |  |  |  |  | $req->remove_content_headers(); | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | { | 
| 159 |  |  |  |  |  |  | # Support for relative URL for redirect. | 
| 160 |  |  |  |  |  |  | # Not correspond to RFC. | 
| 161 | 0 |  |  |  |  |  | local $URI::ABS_ALLOW_RELATIVE_SCHEME = 1; | 
|  | 0 |  |  |  |  |  |  | 
| 162 | 0 |  |  |  |  |  | my $base = $prev->base; | 
| 163 | 0 | 0 |  |  |  |  | $uri = $HTTP::URI_CLASS->new(defined($uri) ? $uri : '', $base)->abs($base); | 
| 164 |  |  |  |  |  |  | } | 
| 165 | 0 |  |  |  |  |  | $req->uri($uri); | 
| 166 | 0 | 0 | 0 |  |  |  | if ($proto eq 'https' && $uri->scheme eq 'http') { | 
| 167 |  |  |  |  |  |  | # Suppress 'Referer' header for HTTPS to HTTP redirect. | 
| 168 |  |  |  |  |  |  | # RFC 2616, section 15.1.3. | 
| 169 | 0 |  |  |  |  |  | $req->remove_header('referer'); | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | $self->_request($req, $opts, sub { | 
| 173 | 0 |  |  | 0 |  |  | $self->_response($req, @_, $prev, $count - 1, sub { return $cb->(@_); }); | 
|  | 0 |  |  |  |  |  |  | 
| 174 | 0 |  |  |  |  |  | }); | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | 1; | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | __END__ |