File Coverage

blib/lib/AnyEvent/UserAgent.pm
Criterion Covered Total %
statement 98 111 88.2
branch 30 38 78.9
condition 18 36 50.0
subroutine 17 23 73.9
pod 8 8 100.0
total 171 216 79.1


line stmt bran cond sub pod time code
1             package AnyEvent::UserAgent;
2              
3             # This module based on original AnyEvent::HTTP::Simple module by punytan
4             # (punytan@gmail.com): http://github.com/punytan/AnyEvent-HTTP-Simple
5              
6 8     8   1051137 use Moo;
  8         75816  
  8         55  
7              
8 8     8   20921 use AnyEvent::HTTP ();
  8         348802  
  8         411  
9 8     8   8037 use HTTP::Cookies ();
  8         130976  
  8         269  
10 8     8   4068 use HTTP::Request ();
  8         224519  
  8         296  
11 8     8   4678 use HTTP::Request::Common ();
  8         25817  
  8         256  
12 8     8   4630 use HTTP::Response ();
  8         74220  
  8         371  
13              
14 8     8   5081 use namespace::clean;
  8         170995  
  8         68  
15              
16             our $VERSION = '0.09';
17              
18              
19             has agent => (is => 'rw', default => sub { $AnyEvent::HTTP::USERAGENT . ' AnyEvent-UserAgent/' . $VERSION });
20              
21             has cookie_jar => (is => 'rw', default => sub { HTTP::Cookies->new });
22              
23             has max_redirects => (is => 'rw', default => sub { 5 });
24              
25             has inactivity_timeout => (is => 'rw', default => sub { 20 });
26              
27             has request_timeout => (is => 'rw', default => sub { 0 });
28              
29             my @OPTIONS = qw(
30             proxy tls_ctx session timeout on_prepare tcp_connect on_header on_body
31             want_body_handle persistent keepalive handle_params
32             );
33              
34             for my $o (@OPTIONS) {
35             has $o => (is => 'rw', default => undef);
36             }
37              
38             sub request {
39 12     12 1 81953 my $cb = pop();
40 12         70 my ($self, $req, %opts) = @_;
41              
42             $self->_request($req, \%opts, sub {
43 12     12   70 $self->_response($req, @_, $cb);
44 12         152 });
45             }
46              
47 10     10 1 37551 sub get { _do_request(\&HTTP::Request::Common::GET => @_) }
48 0     0 1 0 sub head { _do_request(\&HTTP::Request::Common::HEAD => @_) }
49 0     0 1 0 sub put { _do_request(\&HTTP::Request::Common::PUT => @_) }
50 0     0 1 0 sub delete { _do_request(\&HTTP::Request::Common::DELETE => @_) }
51 0     0 1 0 sub post { _do_request(\&HTTP::Request::Common::POST => @_) }
52 0     0 1 0 sub patch { _do_request(\&HTTP::Request::Common::PATCH => @_) }
53 0     0 1 0 sub options { _do_request(\&HTTP::Request::Common::OPTIONS => @_) }
54              
55             sub _do_request {
56 10     10   28 my $cb = pop();
57 10         23 my $meth = shift();
58 10         20 my $self = shift();
59              
60 10         59 $self->request($meth->(@_), $cb);
61             }
62              
63             sub _request {
64 13     13   77 my ($self, $req, $opts, $cb) = @_;
65              
66 13         80 my $uri = $req->uri;
67 13         213 my $hdrs = $req->headers;
68              
69 13 100       167 unless ($hdrs->user_agent) {
70 12         611 $hdrs->user_agent($self->agent);
71             }
72              
73 13 50 66     627 if ($uri->can('userinfo') && $uri->userinfo && !$hdrs->authorization) {
      33        
74 0         0 $hdrs->authorization_basic(split(':', $uri->userinfo, 2));
75             }
76 13 100       1192 if ($uri->scheme) {
77 12         568 $self->cookie_jar->add_cookie_header($req);
78             }
79              
80 13         3968 for (qw(max_redirects inactivity_timeout request_timeout), @OPTIONS) {
81 195 100       824 $opts->{$_} = $self->$_() unless exists($opts->{$_});
82             }
83              
84 13         53 my ($grd, $tmr);
85              
86 13 100       52 if ($opts->{request_timeout}) {
87             $tmr = AE::timer $opts->{request_timeout}, 0, sub {
88 1     1   489139 undef($grd);
89 1         14 $cb->($opts, undef, {Status => 597, Reason => 'Request timeout'});
90 1         22 };
91             }
92             $grd = AnyEvent::HTTP::http_request(
93             $req->method,
94             $req->uri,
95 19         895 headers => {map { $_ => scalar($hdrs->header($_)) } $hdrs->header_field_names},
96             body => $req->content,
97             recurse => 0,
98             timeout => $opts->{inactivity_timeout},
99 2         44 (map { $_ => $opts->{$_} } grep { defined($opts->{$_}) } @OPTIONS),
  156         1250  
100             sub {
101 12     12   194739 undef($grd);
102 12         27 undef($tmr);
103 12         50 $cb->($opts, @_);
104             }
105 13         69 );
106             }
107              
108             sub _response {
109 13     13   33 my $cb = pop();
110 13         56 my ($self, $req, $opts, $body, $hdrs, $prev, $count) = @_;
111              
112 13         132 my $res = HTTP::Response->new(delete($hdrs->{Status}), delete($hdrs->{Reason}));
113              
114 13         1079 $res->request($req);
115 13 100       202 $res->previous($prev) if $prev;
116              
117 13         39 delete($hdrs->{URL});
118 13 50       77 if (defined($hdrs->{HTTPVersion})) {
119 0         0 $res->protocol('HTTP/' . delete($hdrs->{HTTPVersion}));
120             }
121 13 100       60 if (my $hdr = $hdrs->{'set-cookie'}) {
122             # Split comma-concatenated "Set-Cookie" values.
123             # Based on RFC 6265, section 4.1.1.
124 3         33 local @_ = split(/,([\w.!"'%\$&*+-^`]+=)/, ',' . $hdr);
125 3         6 shift();
126 3         5 my @val;
127 3         18 push(@val, join('', shift(), shift())) while @_;
128 3         8 $hdrs->{'set-cookie'} = \@val;
129             }
130 13 100       59 if (keys(%$hdrs)) {
131 5         27 $res->header(%$hdrs);
132             }
133 13 50 66     555 if ($res->code >= 590 && $res->code <= 599 && $res->message) {
      66        
134 3 50       133 if ($res->message eq 'Connection timed out') {
135 0         0 $res->message('Inactivity timeout');
136             }
137 3 50       58 unless ($res->header('client-warning')) {
138 3         372 $res->header('client-warning' => $res->message);
139             }
140             }
141 13 100       381 if (defined($body)) {
142 10         89 $res->content_ref(\$body);
143             }
144 13         472 $self->cookie_jar->extract_cookies($res);
145              
146 13         4468 my $code = $res->code;
147              
148 13 100 66     354 if ($code == 301 || $code == 302 || $code == 303 || $code == 307 || $code == 308) {
      66        
      66        
      33        
149 2         12 $self->_redirect($req, $opts, $code, $res, $count, $cb);
150             }
151             else {
152 11         80 $cb->($res);
153             }
154             }
155              
156             sub _redirect {
157 2     2   8 my ($self, $req, $opts, $code, $prev, $count, $cb) = @_;
158              
159 2 100       11 unless (defined($count) ? $count : ($count = $opts->{max_redirects})) {
    100          
160 1         10 $prev->header('client-warning' => 'Redirect loop detected (max_redirects = ' . $opts->{max_redirects} . ')');
161 1         82 $cb->($prev);
162 1         784 return;
163             }
164              
165 1         9 my $meth = $req->method;
166 1         21 my $proto = $req->uri->scheme;
167 1         45 my $uri = $prev->header('location');
168              
169 1         87 $req = $req->clone();
170 1         269 $req->remove_header('cookie');
171 1 50 33     158 if (($code == 302 || $code == 303) && !($meth eq 'GET' || $meth eq 'HEAD')) {
      33        
      33        
172 0         0 $req->method('GET');
173 0         0 $req->content('');
174 0         0 $req->remove_content_headers();
175             }
176             {
177             # Support for relative URL for redirect.
178             # Not correspond to RFC.
179 1         6 local $URI::ABS_ALLOW_RELATIVE_SCHEME = 1;
  1         3  
180 1         9 my $base = $prev->base;
181 1 50       1365 $uri = $HTTP::URI_CLASS->new(defined($uri) ? $uri : '', $base)->abs($base);
182             }
183 1         362 $req->uri($uri);
184 1 50 33     41 if ($proto eq 'https' && $uri->scheme eq 'http') {
185             # Suppress 'Referer' header for HTTPS to HTTP redirect.
186             # RFC 2616, section 15.1.3.
187 0         0 $req->remove_header('referer');
188             }
189              
190             $self->_request($req, $opts, sub {
191 1     1   20 $self->_response($req, @_, $prev, $count - 1, sub { return $cb->(@_); });
  1         8  
192 1         13 });
193             }
194              
195              
196             1;
197              
198              
199             __END__