File Coverage

blib/lib/LWP/Protocol/Coro/http.pm
Criterion Covered Total %
statement 73 93 78.4
branch 13 34 38.2
condition 0 11 0.0
subroutine 15 16 93.7
pod 1 1 100.0
total 102 155 65.8


line stmt bran cond sub pod time code
1              
2             package LWP::Protocol::Coro::http;
3              
4 9     9   1718696 use strict;
  9         22  
  9         391  
5 9     9   65 use warnings;
  9         17  
  9         814  
6              
7 9     9   7673 use version; our $VERSION = qv('v1.8.0');
  9         20535  
  9         54  
8              
9 9     9   10656 use AnyEvent::HTTP qw( http_request );
  9         6444222  
  9         1060  
10 9     9   9018 use Coro::Channel qw( );
  9         154094  
  9         248  
11 9     9   9378 use HTTP::Response qw( );
  9         1202205  
  9         446  
12 9     9   9908 use LWP::Protocol qw( );
  9         45134  
  9         235  
13 9     9   7891 use LWP::Protocol::http qw( );
  9         3495362  
  9         9553  
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              
32 0 0       0 if ($ssl_opts{verify_hostname}) {
33 0   0     0 $tls_ctx{verify} ||= 1;
34 0         0 $tls_ctx{verify_peername} = 'http';
35             }
36              
37             # We are verifying certificates, but don't have any CA specified, so we try using Mozilla::CA.
38 0 0 0     0 if ($tls_ctx{verify} && !( exists($tls_ctx{ca_file}) || exists($tls_ctx{ca_path}) )) {
      0        
39 0 0       0 if (!eval { require Mozilla::CA }) {
  0         0  
40 0 0       0 if ($@ !~ /^Can\'t locate Mozilla\/CA\.pm/) {
41 0         0 die 'Unable to find a list of Certificate Authorities to trust. '
42             . 'To fix this error, either install Mozilla::CA or configure '
43             . 'the ssl_opts as documented in LWP::UserAgent';
44             } else {
45 0         0 die $@;
46             }
47             }
48              
49 0         0 $tls_ctx{ca_file} = Mozilla::CA::SSL_ca_file();
50             }
51              
52 0         0 return \%tls_ctx;
53             }
54              
55              
56             sub _set_response_headers {
57 32     32   100 my ($response, $headers) = @_;
58              
59 32         535 my %headers = %$headers;
60              
61             $response->protocol( "HTTP/".delete($headers{ HTTPVersion }) )
62 32 100       562 if $headers{ HTTPVersion };
63 32         774 $response->code( delete($headers{ Status }) );
64 32         498 $response->message( delete($headers{ Reason }) );
65              
66             # Uppercase headers are pseudo headers added by AnyEvent::HTTP.
67 32         871 $headers{"X-AE-$_"} = delete($headers{$_}) for grep /^(?!X-)[A-Z]/, keys(%headers);
68              
69 32 100       190 if (exists($headers->{'set-cookie'})) {
70             # Set-Cookie headers are very non-standard.
71             # They cannot be safely joined.
72             # Try to undo their joining for HTTP::Cookies.
73 2         15 $headers{'set-cookie'} = [
74             split(/,(?=\s*\w+\s*(?:[=,;]|\z))/, $headers{'set-cookie'})
75             ];
76             }
77              
78             # Imitate Net::HTTP's removal of newlines.
79             s/\s*\n\s+/ /g
80 32         363 for values %headers;
81              
82 32         540 $response->header(%headers);
83             }
84              
85              
86             sub request {
87 17     17 1 67994 my ($self, $request, $proxy, $arg, $size, $timeout) = @_;
88              
89 17         71 my $method = $request->method();
90 17         191 my $url = $request->uri();
91              
92 17         111 my %headers;
93             {
94 17         33 my $headers_obj = $request->headers->clone();
  17         65  
95              
96             # Convert user:pass in url into an Authorization header.
97 17         2639 LWP::Protocol::http->_fixup_header($headers_obj, $url, $proxy);
98              
99             $headers_obj->scan(sub {
100 35     35   597 my ($k, $v) = @_;
101             # Imitate LWP::Protocol::http's removal of newlines.
102 35         93 $v =~ s/\n/ /g;
103 35         131 $headers{ lc($k) } = $v;
104 17         1314 });
105             }
106              
107 17         343 my $body = $request->content_ref();
108              
109             # Fix AnyEvent::HTTP setting Referer to the request URL
110 17 100       274 $headers{referer} = undef unless exists $headers{referer};
111              
112             # The status code will be replaced.
113 17         188 my $response = HTTP::Response->new(599, 'Internal Server Error');
114 17         1391 $response->request($request);
115              
116 17         750 my $headers_avail = AnyEvent->condvar();
117 17         12085 my $data_channel = Coro::Channel->new(1);
118              
119 17         240 my %handle_opts;
120 17 50       80 $handle_opts{read_size} = $size if defined($size);
121 17 50       68 $handle_opts{max_read_size} = $size if defined($size);
122              
123 17         92 my %opts = ( handle_params => \%handle_opts );
124 17 50       102 $opts{body} = $$body if defined($body);
125 17 50       101 $opts{timeout} = $timeout if defined($timeout);
126              
127 17 50       81 if ($url->scheme eq 'https') {
128 0         0 $opts{tls_ctx} = $self->_get_tls_ctx();
129             }
130              
131 17 100       376 if ($proxy) {
132 1         11 my $proxy_uri = URI->new($proxy);
133 1         58 $opts{proxy} = [ $proxy_uri->host, $proxy_uri->port, $proxy_uri->scheme ];
134             }
135              
136             # Let LWP handle redirects and cookies.
137             my $guard = http_request(
138             $method => $url,
139             headers => \%headers,
140             %opts,
141             recurse => 0,
142             on_header => sub {
143             #my ($headers) = @_;
144 15     15   11339018 _set_response_headers($response, $_[0]);
145 15         3764 $headers_avail->send();
146 15         391 return 1;
147             },
148             on_body => sub {
149             #my ($chunk, $headers) = @_;
150 16     16   6999921 $data_channel->put(\$_[0]);
151 16         303 return 1;
152             },
153             sub { # On completion
154             # On successful completion: @_ = ( '', $headers )
155             # On error: @_ = ( undef, $headers )
156              
157             # It is possible for the request to complete without
158             # calling the header callback in the event of error.
159             # It is also possible for the Status to change as the
160             # result of an error. This handles these events.
161 17     17   4988523 _set_response_headers($response, $_[1]);
162 17         4329 $headers_avail->send();
163 17         1027 $data_channel->put(\''); # '
164             },
165 17         604 );
166              
167             # We need to wait for the headers so the response code
168             # is set up properly. LWP::Protocol decides on ->is_success
169             # whether to call the :content_cb or not.
170 17         188484 $headers_avail->recv();
171              
172             return $self->collect($arg, $response, sub {
173 33     33   30465 return $data_channel->get();
174 17         1690 });
175             }
176              
177              
178             1;
179              
180              
181             __END__