File Coverage

blib/lib/LWP/Protocol/AnyEvent/http.pm
Criterion Covered Total %
statement 80 107 74.7
branch 16 46 34.7
condition 2 14 14.2
subroutine 16 17 94.1
pod 1 1 100.0
total 115 185 62.1


line stmt bran cond sub pod time code
1              
2             package LWP::Protocol::AnyEvent::http;
3              
4 9     9   663310 use strict;
  9         79  
  9         284  
5 9     9   55 use warnings;
  9         18  
  9         293  
6              
7 9     9   3952 use version; our $VERSION = qv('v1.14.0');
  9         17098  
  9         53  
8              
9 9     9   2989 use AnyEvent qw( );
  9         14861  
  9         192  
10 9     9   5132 use AnyEvent::HTTP qw( );
  9         268019  
  9         335  
11 9     9   4206 use HTTP::Response qw( );
  9         214302  
  9         223  
12 9     9   3537 use LWP::Protocol qw( );
  9         33557  
  9         198  
13 9     9   3641 use LWP::Protocol::http qw( );
  9         302419  
  9         385  
14              
15             BEGIN {
16 9     9   50 local $^W = 0; # AnyEvent::HTTP::Socks warns when used with -w
17 9         3983 require AnyEvent::HTTP::Socks;
18             }
19              
20              
21             our @ISA = 'LWP::Protocol';
22              
23             LWP::Protocol::implementor($_, __PACKAGE__) for qw( http https socks socks5 socks4a socks4 );
24              
25              
26             # This code was based on _extra_sock_opts in LWP::Protocol::https
27             sub _get_tls_ctx {
28 0     0   0 my ($self) = @_;
29 0 0       0 my %ssl_opts = %{ $self->{ua}{ssl_opts} || {} };
  0         0  
30 0         0 my %tls_ctx;
31              
32             # Convert various ssl_opts values to corresponding AnyEvent::TLS tls_ctx values.
33 0         0 $tls_ctx{ verify } = $ssl_opts{SSL_verify_mode};
34 0 0 0     0 $tls_ctx{ verify_peername } = 'http' if defined($ssl_opts{SSL_verifycn_scheme}) && $ssl_opts{SSL_verifycn_scheme} eq 'www';
35 0 0       0 $tls_ctx{ ca_file } = $ssl_opts{SSL_ca_file} if exists($ssl_opts{SSL_ca_file});
36 0 0       0 $tls_ctx{ ca_path } = $ssl_opts{SSL_ca_path} if exists($ssl_opts{SSL_ca_path});
37 0 0       0 $tls_ctx{ cert_file } = $ssl_opts{SSL_cert_file} if exists($ssl_opts{SSL_cert_file});
38 0 0       0 $tls_ctx{ cert } = $ssl_opts{SSL_cert} if exists($ssl_opts{SSL_cert});
39 0 0       0 $tls_ctx{ key_file } = $ssl_opts{SSL_key_file} if exists($ssl_opts{SSL_key_file});
40 0 0       0 $tls_ctx{ key } = $ssl_opts{SSL_key} if exists($ssl_opts{SSL_key});
41              
42 0 0       0 if ($ssl_opts{verify_hostname}) {
43 0   0     0 $tls_ctx{verify} ||= 1;
44 0         0 $tls_ctx{verify_peername} = 'http';
45             }
46              
47             # We are verifying certificates, but don't have any CA specified, so we try using Mozilla::CA.
48 0 0 0     0 if ($tls_ctx{verify} && !( exists($tls_ctx{ca_file}) || exists($tls_ctx{ca_path}) )) {
      0        
49 0 0       0 if (!eval { require Mozilla::CA }) {
  0         0  
50 0 0       0 if ($@ !~ /^Can\'t locate Mozilla\/CA\.pm/) {
51 0         0 die 'Unable to find a list of Certificate Authorities to trust. '
52             . 'To fix this error, either install Mozilla::CA or configure '
53             . 'the ssl_opts as documented in LWP::UserAgent';
54             } else {
55 0         0 die $@;
56             }
57             }
58              
59 0         0 $tls_ctx{ca_file} = Mozilla::CA::SSL_ca_file();
60             }
61              
62 0         0 return \%tls_ctx;
63             }
64              
65              
66             sub _set_response_headers {
67 31     31   126 my ($response, $headers) = @_;
68              
69 31         283 my %headers = %$headers;
70              
71 31 100       477 $response->protocol( "HTTP/".delete($headers{ HTTPVersion }) ) if $headers{ HTTPVersion };
72 31         659 $response->code( delete($headers{ Status }) );
73 31         553 $response->message( delete($headers{ Reason }) );
74              
75             # Uppercase headers are pseudo headers added by AnyEvent::HTTP.
76 31         778 $headers{"X-AE-$_"} = delete($headers{$_}) for grep /^(?!X-)[A-Z]/, keys(%headers);
77              
78 31 100       169 if (exists($headers->{'set-cookie'})) {
79             # Set-Cookie headers are very non-standard.
80             # They cannot be safely joined.
81             # Try to undo their joining for HTTP::Cookies.
82             $headers{'set-cookie'} = [
83 2         14 split(/,(?=\s*\w+\s*(?:[=,;]|\z))/, $headers{'set-cookie'})
84             ];
85             }
86              
87             # Imitate Net::HTTP's removal of newlines.
88             s/\s*\n\s+/ /g
89 31         290 for values %headers;
90              
91 31         601 $response->header(%headers);
92             }
93              
94              
95             sub request {
96 16     16 1 49641 my ($self, $request, $proxy, $arg, $size, $timeout) = @_;
97              
98 16         86 my $method = $request->method();
99 16         244 my $url = $request->uri();
100              
101 16         150 my %headers;
102             {
103 16         66 my $headers_obj = $request->headers->clone();
  16         80  
104              
105             # Convert user:pass in url into an Authorization header.
106 16         1798 LWP::Protocol::http->_fixup_header($headers_obj, $url, $proxy);
107              
108             $headers_obj->scan(sub {
109 33     33   771 my ($k, $v) = @_;
110             # Imitate LWP::Protocol::http's removal of newlines.
111 33         157 $v =~ s/\n/ /g;
112 33         158 $headers{ lc($k) } = $v;
113 16         1704 });
114             }
115              
116 16         338 my $body = $request->content_ref();
117              
118             # Fix AnyEvent::HTTP setting Referer to the request URL
119 16 100       1466 $headers{referer} = undef unless exists $headers{referer};
120              
121             # The status code will be replaced.
122 16         157 my $response = HTTP::Response->new(599, 'Internal Server Error');
123 16         1318 $response->request($request);
124              
125 16         657 my $headers_avail = AnyEvent->condvar();
126 16         7164 my $data_avail = AnyEvent->condvar();
127 16         110 my @data_queue;
128              
129             my %handle_opts;
130 16 50       75 $handle_opts{read_size} = $size if defined($size);
131 16 50       62 $handle_opts{max_read_size} = $size if defined($size);
132              
133 16         130 my %opts = ( handle_params => \%handle_opts );
134 16 50       111 $opts{body} = $$body if defined($body);
135 16 50       106 $opts{timeout} = $timeout if defined($timeout);
136              
137 16 50 66     170 if (( $proxy || $url )->scheme eq 'https') {
138 0         0 $opts{tls_ctx} = $self->_get_tls_ctx();
139             }
140              
141 16 100       530 if (!$proxy) {
    50          
142 15         80 $opts{proxy} = undef;
143             }
144             elsif ($proxy =~ /^socks/) {
145 0         0 $proxy =~ s{socks://}{socks5://}gi;
146 0         0 $opts{proxy} = undef;
147 0         0 $opts{socks} = $proxy;
148             }
149             else {
150 1         19 $opts{proxy} = [ $proxy->host, $proxy->port, $proxy->scheme ];
151             }
152              
153             # Let LWP handle redirects and cookies.
154             AnyEvent::HTTP::Socks::http_request(
155             $method => $url,
156             headers => \%headers,
157             %opts,
158             recurse => 0,
159             on_header => sub {
160             #my ($headers) = @_;
161 15     15   10085255 _set_response_headers($response, $_[0]);
162 15         4446 $headers_avail->send();
163 15         240 return 1;
164             },
165             on_body => sub {
166             #my ($chunk, $headers) = @_;
167 16     16   6989474 push @data_queue, \$_[0];
168 16         125 $data_avail->send();
169 16         272 return 1;
170             },
171             sub { # On completion
172             # On successful completion: @_ = ( '', $headers )
173             # On error: @_ = ( undef, $headers )
174              
175             # It is possible for the request to complete without
176             # calling the header callback in the event of error.
177             # It is also possible for the Status to change as the
178             # result of an error. This handles these events.
179 16     16   4961257 _set_response_headers($response, $_[1]);
180 16         3638 $headers_avail->send();
181              
182 16         234 push @data_queue, \''; # '
183 16         67 $data_avail->send();
184             },
185 16         528 );
186              
187             # We need to wait for the headers so the response code
188             # is set up properly. LWP::Protocol decides on ->is_success
189             # whether to call the :content_cb or not.
190 16         21542 $headers_avail->recv();
191              
192             return $self->collect($arg, $response, sub {
193 32 100   32   29100 if (!@data_queue) {
194             # Re-prime our channel, in case there is more.
195 15         703 $data_avail = AnyEvent->condvar();
196              
197             # Wait for more data to arrive
198 15         246 $data_avail->recv();
199             };
200              
201 32         2663 return shift(@data_queue);
202 16         2558 });
203             }
204              
205              
206             1;
207              
208              
209             __END__