File Coverage

blib/lib/LWP/Protocol/Coro/http.pm
Criterion Covered Total %
statement 75 102 73.5
branch 14 44 31.8
condition 2 14 14.2
subroutine 16 17 94.1
pod 1 1 100.0
total 108 178 60.6


line stmt bran cond sub pod time code
1              
2             package LWP::Protocol::Coro::http;
3              
4 9     9   636536 use strict;
  9         79  
  9         256  
5 9     9   48 use warnings;
  9         19  
  9         236  
6              
7 9     9   3453 use version; our $VERSION = qv('v1.14.0');
  9         15072  
  9         44  
8              
9 9     9   5655 use AnyEvent::HTTP qw( );
  9         261508  
  9         285  
10 9     9   4149 use Coro::Channel qw( );
  9         78893  
  9         221  
11 9     9   3941 use HTTP::Response qw( );
  9         241696  
  9         229  
12 9     9   4096 use LWP::Protocol qw( );
  9         37883  
  9         235  
13 9     9   3967 use LWP::Protocol::http qw( );
  9         344545  
  9         391  
14              
15             BEGIN {
16 9     9   63 local $^W = 0; # AnyEvent::HTTP::Socks warns when used with -w
17 9         3852 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 32     32   123 my ($response, $headers) = @_;
68              
69 32         275 my %headers = %$headers;
70              
71 32 100       455 $response->protocol( "HTTP/".delete($headers{ HTTPVersion }) ) if $headers{ HTTPVersion };
72 32         658 $response->code( delete($headers{ Status }) );
73 32         508 $response->message( delete($headers{ Reason }) );
74              
75             # Uppercase headers are pseudo headers added by AnyEvent::HTTP.
76 32         737 $headers{"X-AE-$_"} = delete($headers{$_}) for grep /^(?!X-)[A-Z]/, keys(%headers);
77              
78 32 100       153 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         33 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 32         289 for values %headers;
90              
91 32         588 $response->header(%headers);
92             }
93              
94              
95             sub request {
96 17     17 1 64454 my ($self, $request, $proxy, $arg, $size, $timeout) = @_;
97              
98 17         87 my $method = $request->method();
99 17         288 my $url = $request->uri();
100              
101 17         167 my %headers;
102             {
103 17         55 my $headers_obj = $request->headers->clone();
  17         91  
104              
105             # Convert user:pass in url into an Authorization header.
106 17         1958 LWP::Protocol::http->_fixup_header($headers_obj, $url, $proxy);
107              
108             $headers_obj->scan(sub {
109 35     35   902 my ($k, $v) = @_;
110             # Imitate LWP::Protocol::http's removal of newlines.
111 35         138 $v =~ s/\n/ /g;
112 35         166 $headers{ lc($k) } = $v;
113 17         1587 });
114             }
115              
116 17         374 my $body = $request->content_ref();
117              
118             # Fix AnyEvent::HTTP setting Referer to the request URL
119 17 100       445 $headers{referer} = undef unless exists $headers{referer};
120              
121             # The status code will be replaced.
122 17         154 my $response = HTTP::Response->new(599, 'Internal Server Error');
123 17         1441 $response->request($request);
124              
125 17         879 my $headers_avail = AnyEvent->condvar();
126 17         8088 my $data_channel = Coro::Channel->new(1);
127              
128 17         222 my %handle_opts;
129 17 50       74 $handle_opts{read_size} = $size if defined($size);
130 17 50       64 $handle_opts{max_read_size} = $size if defined($size);
131              
132 17         101 my %opts = ( handle_params => \%handle_opts );
133 17 50       114 $opts{body} = $$body if defined($body);
134 17 50       103 $opts{timeout} = $timeout if defined($timeout);
135              
136 17 50 66     162 if (( $proxy || $url )->scheme eq 'https') {
137 0         0 $opts{tls_ctx} = $self->_get_tls_ctx();
138             }
139              
140 17 100       565 if (!$proxy) {
    50          
141 16         77 $opts{proxy} = undef;
142             }
143             elsif ($proxy =~ /^socks/) {
144 0         0 $proxy =~ s{socks://}{socks5://}gi;
145 0         0 $opts{proxy} = undef;
146 0         0 $opts{socks} = $proxy;
147             }
148             else {
149 1         20 $opts{proxy} = [ $proxy->host, $proxy->port, $proxy->scheme ];
150             }
151              
152             # Let LWP handle redirects and cookies.
153             AnyEvent::HTTP::Socks::http_request(
154             $method => $url,
155             headers => \%headers,
156             %opts,
157             recurse => 0,
158             on_header => sub {
159             #my ($headers) = @_;
160 15     15   10090685 _set_response_headers($response, $_[0]);
161 15         4052 $headers_avail->send();
162 15         354 return 1;
163             },
164             on_body => sub {
165             #my ($chunk, $headers) = @_;
166 16     16   6997031 $data_channel->put(\$_[0]);
167 16         274 return 1;
168             },
169             sub { # On completion
170             # On successful completion: @_ = ( '', $headers )
171             # On error: @_ = ( undef, $headers )
172              
173             # It is possible for the request to complete without
174             # calling the header callback in the event of error.
175             # It is also possible for the Status to change as the
176             # result of an error. This handles these events.
177 17     17   4979785 _set_response_headers($response, $_[1]);
178 17         3759 $headers_avail->send();
179 17         396 $data_channel->put(\''); # '
180             },
181 17         546 );
182              
183             # We need to wait for the headers so the response code
184             # is set up properly. LWP::Protocol decides on ->is_success
185             # whether to call the :content_cb or not.
186 17         29162 $headers_avail->recv();
187              
188             return $self->collect($arg, $response, sub {
189 33     33   29640 return $data_channel->get();
190 17         1766 });
191             }
192              
193              
194             1;
195              
196              
197             __END__