File Coverage

blib/lib/LWP/Protocol/Coro/http.pm
Criterion Covered Total %
statement 73 97 75.2
branch 13 42 30.9
condition 0 11 0.0
subroutine 15 16 93.7
pod 1 1 100.0
total 102 167 61.0


line stmt bran cond sub pod time code
1              
2             package LWP::Protocol::Coro::http;
3              
4 9     9   513642 use strict;
  9         69  
  9         205  
5 9     9   37 use warnings;
  9         15  
  9         192  
6              
7 9     9   3316 use version; our $VERSION = qv('v1.10.0');
  9         13593  
  9         39  
8              
9 9     9   5050 use AnyEvent::HTTP qw( http_request );
  9         221449  
  9         561  
10 9     9   3837 use Coro::Channel qw( );
  9         67346  
  9         177  
11 9     9   3511 use HTTP::Response qw( );
  9         207601  
  9         214  
12 9     9   3538 use LWP::Protocol qw( );
  9         33794  
  9         182  
13 9     9   3684 use LWP::Protocol::http qw( );
  9         297415  
  9         7874  
14              
15             our @ISA = 'LWP::Protocol';
16              
17             LWP::Protocol::implementor($_, __PACKAGE__) for qw( http https );
18              
19              
20             # This code was based on _extra_sock_opts in LWP::Protocol::https
21             sub _get_tls_ctx {
22 0     0   0 my ($self) = @_;
23 0 0       0 my %ssl_opts = %{ $self->{ua}{ssl_opts} || {} };
  0         0  
24 0         0 my %tls_ctx;
25              
26             # Convert various ssl_opts values to corresponding AnyEvent::TLS tls_ctx values.
27 0         0 $tls_ctx{ verify } = $ssl_opts{SSL_verify_mode};
28 0 0 0     0 $tls_ctx{ verify_peername } = 'http' if defined($ssl_opts{SSL_verifycn_scheme}) && $ssl_opts{SSL_verifycn_scheme} eq 'www';
29 0 0       0 $tls_ctx{ ca_file } = $ssl_opts{SSL_ca_file} if exists($ssl_opts{SSL_ca_file});
30 0 0       0 $tls_ctx{ ca_path } = $ssl_opts{SSL_ca_path} if exists($ssl_opts{SSL_ca_path});
31 0 0       0 $tls_ctx{ cert_file } = $ssl_opts{SSL_cert_file} if exists($ssl_opts{SSL_cert_file});
32 0 0       0 $tls_ctx{ cert } = $ssl_opts{SSL_cert} if exists($ssl_opts{SSL_cert});
33 0 0       0 $tls_ctx{ key_file } = $ssl_opts{SSL_key_file} if exists($ssl_opts{SSL_key_file});
34 0 0       0 $tls_ctx{ key } = $ssl_opts{SSL_key} if exists($ssl_opts{SSL_key});
35              
36 0 0       0 if ($ssl_opts{verify_hostname}) {
37 0   0     0 $tls_ctx{verify} ||= 1;
38 0         0 $tls_ctx{verify_peername} = 'http';
39             }
40              
41             # We are verifying certificates, but don't have any CA specified, so we try using Mozilla::CA.
42 0 0 0     0 if ($tls_ctx{verify} && !( exists($tls_ctx{ca_file}) || exists($tls_ctx{ca_path}) )) {
      0        
43 0 0       0 if (!eval { require Mozilla::CA }) {
  0         0  
44 0 0       0 if ($@ !~ /^Can\'t locate Mozilla\/CA\.pm/) {
45 0         0 die 'Unable to find a list of Certificate Authorities to trust. '
46             . 'To fix this error, either install Mozilla::CA or configure '
47             . 'the ssl_opts as documented in LWP::UserAgent';
48             } else {
49 0         0 die $@;
50             }
51             }
52              
53 0         0 $tls_ctx{ca_file} = Mozilla::CA::SSL_ca_file();
54             }
55              
56 0         0 return \%tls_ctx;
57             }
58              
59              
60             sub _set_response_headers {
61 32     32   90 my ($response, $headers) = @_;
62              
63 32         208 my %headers = %$headers;
64              
65             $response->protocol( "HTTP/".delete($headers{ HTTPVersion }) )
66 32 100       399 if $headers{ HTTPVersion };
67 32         526 $response->code( delete($headers{ Status }) );
68 32         416 $response->message( delete($headers{ Reason }) );
69              
70             # Uppercase headers are pseudo headers added by AnyEvent::HTTP.
71 32         583 $headers{"X-AE-$_"} = delete($headers{$_}) for grep /^(?!X-)[A-Z]/, keys(%headers);
72              
73 32 100       124 if (exists($headers->{'set-cookie'})) {
74             # Set-Cookie headers are very non-standard.
75             # They cannot be safely joined.
76             # Try to undo their joining for HTTP::Cookies.
77             $headers{'set-cookie'} = [
78 2         9 split(/,(?=\s*\w+\s*(?:[=,;]|\z))/, $headers{'set-cookie'})
79             ];
80             }
81              
82             # Imitate Net::HTTP's removal of newlines.
83             s/\s*\n\s+/ /g
84 32         235 for values %headers;
85              
86 32         502 $response->header(%headers);
87             }
88              
89              
90             sub request {
91 17     17 1 53636 my ($self, $request, $proxy, $arg, $size, $timeout) = @_;
92              
93 17         78 my $method = $request->method();
94 17         240 my $url = $request->uri();
95              
96 17         119 my %headers;
97             {
98 17         37 my $headers_obj = $request->headers->clone();
  17         66  
99              
100             # Convert user:pass in url into an Authorization header.
101 17         1811 LWP::Protocol::http->_fixup_header($headers_obj, $url, $proxy);
102              
103             $headers_obj->scan(sub {
104 35     35   689 my ($k, $v) = @_;
105             # Imitate LWP::Protocol::http's removal of newlines.
106 35         125 $v =~ s/\n/ /g;
107 35         124 $headers{ lc($k) } = $v;
108 17         1278 });
109             }
110              
111 17         272 my $body = $request->content_ref();
112              
113             # Fix AnyEvent::HTTP setting Referer to the request URL
114 17 100       344 $headers{referer} = undef unless exists $headers{referer};
115              
116             # The status code will be replaced.
117 17         133 my $response = HTTP::Response->new(599, 'Internal Server Error');
118 17         1057 $response->request($request);
119              
120 17         703 my $headers_avail = AnyEvent->condvar();
121 17         7753 my $data_channel = Coro::Channel->new(1);
122              
123 17         165 my %handle_opts;
124 17 50       56 $handle_opts{read_size} = $size if defined($size);
125 17 50       68 $handle_opts{max_read_size} = $size if defined($size);
126              
127 17         81 my %opts = ( handle_params => \%handle_opts );
128 17 50       102 $opts{body} = $$body if defined($body);
129 17 50       102 $opts{timeout} = $timeout if defined($timeout);
130              
131 17 50       66 if ($url->scheme eq 'https') {
132 0         0 $opts{tls_ctx} = $self->_get_tls_ctx();
133             }
134              
135 17 100       315 if ($proxy) {
136 1         8 my $proxy_uri = URI->new($proxy);
137 1         63 $opts{proxy} = [ $proxy_uri->host, $proxy_uri->port, $proxy_uri->scheme ];
138             }
139              
140             # Let LWP handle redirects and cookies.
141             http_request(
142             $method => $url,
143             headers => \%headers,
144             %opts,
145             recurse => 0,
146             on_header => sub {
147             #my ($headers) = @_;
148 15     15   10093795 _set_response_headers($response, $_[0]);
149 15         3144 $headers_avail->send();
150 15         308 return 1;
151             },
152             on_body => sub {
153             #my ($chunk, $headers) = @_;
154 16     16   6996560 $data_channel->put(\$_[0]);
155 16         246 return 1;
156             },
157             sub { # On completion
158             # On successful completion: @_ = ( '', $headers )
159             # On error: @_ = ( undef, $headers )
160              
161             # It is possible for the request to complete without
162             # calling the header callback in the event of error.
163             # It is also possible for the Status to change as the
164             # result of an error. This handles these events.
165 17     17   4993749 _set_response_headers($response, $_[1]);
166 17         2574 $headers_avail->send();
167 17         339 $data_channel->put(\''); # '
168             },
169 17         438 );
170              
171             # We need to wait for the headers so the response code
172             # is set up properly. LWP::Protocol decides on ->is_success
173             # whether to call the :content_cb or not.
174 17         125880 $headers_avail->recv();
175              
176             return $self->collect($arg, $response, sub {
177 33     33   21005 return $data_channel->get();
178 17         1421 });
179             }
180              
181              
182             1;
183              
184              
185             __END__