File Coverage

blib/lib/AnyEvent/UserAgent.pm
Criterion Covered Total %
statement 102 113 90.2
branch 30 38 78.9
condition 18 36 50.0
subroutine 18 22 81.8
pod 6 6 100.0
total 174 215 80.9


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