line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::HTTP2::Client; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
378
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
4
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
22
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 NAME |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
Net::HTTP2::Client - Full-featured HTTP/2 client base class |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=cut |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# perl -I ../p5-X-Tiny/lib -MData::Dumper -MAnyEvent -I ../p5-IO-SigGuard/lib -I ../p5-Promise-ES6/lib -Ilib -MNet::HTTP2::Client -e'my $h2 = Net::HTTP2::Client->new(); my $cv = AnyEvent->condvar(); $h2->request("GET", "https://google.com")->then( sub { print Dumper shift } )->finally($cv); $cv->recv();' |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
15
|
|
|
|
|
|
|
|
16
|
1
|
|
|
1
|
|
3
|
use Carp (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
17
|
1
|
|
|
1
|
|
407
|
use URI::Split (); |
|
1
|
|
|
|
|
2821
|
|
|
1
|
|
|
|
|
19
|
|
18
|
|
|
|
|
|
|
|
19
|
1
|
|
|
1
|
|
333
|
use Net::HTTP2::Constants (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
20
|
|
|
|
|
|
|
|
21
|
1
|
|
|
|
|
760
|
use constant _SIMPLE_REDIRECTS => ( |
22
|
|
|
|
|
|
|
301, 308, |
23
|
|
|
|
|
|
|
302, 307, |
24
|
1
|
|
|
1
|
|
5
|
); |
|
1
|
|
|
|
|
2
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
#---------------------------------------------------------------------- |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub new { |
29
|
1
|
|
|
1
|
0
|
13
|
return bless { |
30
|
|
|
|
|
|
|
host_port_client => { }, |
31
|
|
|
|
|
|
|
}, shift; |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub _split_uri_auth { |
35
|
2
|
|
|
2
|
|
5
|
my $auth = shift; |
36
|
|
|
|
|
|
|
|
37
|
2
|
50
|
|
|
|
9
|
if ( $auth =~ m<\A([^:]+):(.+)> ) { |
38
|
0
|
|
|
|
|
0
|
return ($1, $2); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
2
|
|
|
|
|
7
|
return ($auth, Net::HTTP2::Constants::HTTPS_PORT); |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub request { |
45
|
1
|
|
|
1
|
0
|
5590
|
my ($self, $method, $url, @opts_kv) = @_; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Omit the fragment: |
48
|
1
|
|
|
|
|
5
|
my ($scheme, $auth, $path, $query) = URI::Split::uri_split($url); |
49
|
|
|
|
|
|
|
|
50
|
1
|
50
|
|
|
|
10
|
if (!$scheme) { |
51
|
0
|
|
|
|
|
0
|
Carp::croak "Need absolute URL, not “$url”"; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
1
|
50
|
|
|
|
14
|
if ($scheme ne 'https') { |
55
|
0
|
|
|
|
|
0
|
Carp::croak "https only, not $scheme!"; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
1
|
|
|
|
|
3
|
my ($host, $port) = _split_uri_auth($auth); |
59
|
|
|
|
|
|
|
|
60
|
1
|
|
|
|
|
4
|
my $host_port_conn_hr = $self->{'host_port_client'}; |
61
|
|
|
|
|
|
|
|
62
|
1
|
|
|
|
|
4
|
my $conn_ns = $self->_get_conn_namespace(); |
63
|
|
|
|
|
|
|
|
64
|
1
|
|
|
|
|
3
|
my $path_and_query = $path; |
65
|
1
|
50
|
33
|
|
|
4
|
if (defined $query && length $query) { |
66
|
0
|
|
|
|
|
0
|
$path_and_query .= "?$query"; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
1
|
|
|
|
|
5
|
return _request_recurse( |
70
|
|
|
|
|
|
|
$conn_ns, |
71
|
|
|
|
|
|
|
$host_port_conn_hr, |
72
|
|
|
|
|
|
|
$method, |
73
|
|
|
|
|
|
|
$host, |
74
|
|
|
|
|
|
|
$port, |
75
|
|
|
|
|
|
|
$path_and_query, |
76
|
|
|
|
|
|
|
@opts_kv, |
77
|
|
|
|
|
|
|
); |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub _request_recurse { |
81
|
2
|
|
|
2
|
|
11
|
my ($conn_ns, $host_port_conn_hr, $method, $host, $port, $path_and_query, @opts_kv) = @_; |
82
|
|
|
|
|
|
|
|
83
|
2
|
|
|
|
|
6
|
my $conn = _get_conn( $conn_ns, $host_port_conn_hr, $host, $port, @opts_kv ); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
return _request_once( $conn, $method, $path_and_query )->then( |
86
|
|
|
|
|
|
|
sub { |
87
|
2
|
|
|
2
|
|
112
|
my $resp = shift; |
88
|
|
|
|
|
|
|
|
89
|
2
|
|
|
|
|
9
|
my $status = $resp->status(); |
90
|
2
|
|
|
|
|
9
|
my $redirect_yn = grep { $_ == $status } _SIMPLE_REDIRECTS; |
|
8
|
|
|
|
|
18
|
|
91
|
|
|
|
|
|
|
|
92
|
2
|
50
|
|
|
|
12
|
if ($status == 303) { |
93
|
0
|
|
|
|
|
0
|
$redirect_yn = 1; |
94
|
|
|
|
|
|
|
|
95
|
0
|
|
|
|
|
0
|
$method = 'GET'; |
96
|
0
|
|
|
|
|
0
|
push @opts_kv, body => q<>; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
2
|
100
|
|
|
|
6
|
if ($redirect_yn) { |
100
|
|
|
|
|
|
|
my ($new_host, $new_port, $path_and_query) = _consume_location( |
101
|
1
|
|
|
|
|
5
|
$resp->headers()->{'location'}, |
102
|
|
|
|
|
|
|
$host, $port, $path_and_query, |
103
|
|
|
|
|
|
|
); |
104
|
|
|
|
|
|
|
|
105
|
1
|
|
|
|
|
2
|
$host = $new_host; |
106
|
1
|
|
|
|
|
2
|
$port = $new_port; |
107
|
|
|
|
|
|
|
|
108
|
1
|
|
|
|
|
5
|
return _request_recurse( $conn_ns, $host_port_conn_hr, $method, $host, $port, $path_and_query, @opts_kv ); |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
|
111
|
1
|
|
|
|
|
3
|
return $resp; |
112
|
|
|
|
|
|
|
} |
113
|
2
|
|
|
|
|
14
|
); |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub _consume_location { |
117
|
1
|
|
|
1
|
|
5
|
my ($location, $host, $port, $old_path) = @_; |
118
|
|
|
|
|
|
|
|
119
|
1
|
|
|
|
|
6
|
my ($scheme, $auth, $path, $query) = URI::Split::uri_split($location); |
120
|
|
|
|
|
|
|
|
121
|
1
|
|
|
|
|
19
|
my $path_and_query = $path; |
122
|
1
|
50
|
33
|
|
|
4
|
if (defined $query && length $query) { |
123
|
0
|
|
|
|
|
0
|
$path_and_query .= "?$query"; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
1
|
50
|
|
|
|
3
|
if ($scheme) { |
127
|
1
|
50
|
|
|
|
5
|
if ($scheme ne 'https') { |
128
|
0
|
|
|
|
|
0
|
Carp::croak "Invalid scheme in redirect: $location"; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
1
|
|
|
|
|
4
|
($host, $port) = _split_uri_auth($auth); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
1
|
50
|
|
|
|
5
|
if (rindex($path, '/', 0) != 0) { |
135
|
0
|
|
|
|
|
0
|
$old_path =~ s<(.*)/><$1>; |
136
|
0
|
|
|
|
|
0
|
substr( $path_and_query, 0, 0, "$old_path/" ); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
1
|
|
|
|
|
4
|
return ($host, $port, $path_and_query); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub _get_conn { |
143
|
2
|
|
|
2
|
|
27
|
my ($conn_ns, $host_port_conn_hr, $host, $port) = @_; |
144
|
|
|
|
|
|
|
|
145
|
2
|
50
|
50
|
|
|
42
|
return $host_port_conn_hr->{$host}{$port || q<>} ||= $conn_ns->new( |
|
|
|
33
|
|
|
|
|
146
|
|
|
|
|
|
|
$host, |
147
|
|
|
|
|
|
|
($port == Net::HTTP2::Constants::HTTPS_PORT ? () : (port => $port)), |
148
|
|
|
|
|
|
|
); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub _request_once { |
152
|
2
|
|
|
2
|
|
6
|
my ($conn, $method, $path_and_query, @opts_kv) = @_; |
153
|
|
|
|
|
|
|
|
154
|
2
|
|
|
|
|
12
|
return $conn->request($method, $path_and_query); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub _get_conn_namespace { |
158
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
159
|
|
|
|
|
|
|
|
160
|
1
|
|
33
|
|
|
4
|
return $self->{'_conn_ns'} ||= do { |
161
|
1
|
|
|
|
|
6
|
my $ns = "Net::HTTP2::Client::Connection::" . $self->_CLIENT_IO(); |
162
|
|
|
|
|
|
|
|
163
|
1
|
|
|
|
|
2
|
local $@; |
164
|
1
|
50
|
|
|
|
43
|
Carp::croak $@ if !eval "require $ns"; |
165
|
|
|
|
|
|
|
|
166
|
1
|
|
|
|
|
12
|
$ns; |
167
|
|
|
|
|
|
|
}; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
1; |