line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTTP::Request::FromCurl; |
2
|
13
|
|
|
13
|
|
208679
|
use strict; |
|
13
|
|
|
|
|
112
|
|
|
13
|
|
|
|
|
367
|
|
3
|
13
|
|
|
13
|
|
70
|
use warnings; |
|
13
|
|
|
|
|
42
|
|
|
13
|
|
|
|
|
454
|
|
4
|
13
|
|
|
13
|
|
84
|
use File::Basename 'basename'; |
|
13
|
|
|
|
|
47
|
|
|
13
|
|
|
|
|
1433
|
|
5
|
13
|
|
|
13
|
|
5624
|
use HTTP::Request; |
|
13
|
|
|
|
|
298405
|
|
|
13
|
|
|
|
|
417
|
|
6
|
13
|
|
|
13
|
|
6445
|
use HTTP::Request::Common; |
|
13
|
|
|
|
|
30421
|
|
|
13
|
|
|
|
|
1009
|
|
7
|
13
|
|
|
13
|
|
127
|
use URI; |
|
13
|
|
|
|
|
33
|
|
|
13
|
|
|
|
|
384
|
|
8
|
13
|
|
|
13
|
|
76
|
use URI::Escape; |
|
13
|
|
|
|
|
31
|
|
|
13
|
|
|
|
|
649
|
|
9
|
13
|
|
|
13
|
|
10285
|
use Getopt::Long; |
|
13
|
|
|
|
|
138615
|
|
|
13
|
|
|
|
|
50
|
|
10
|
13
|
|
|
13
|
|
1903
|
use File::Spec::Unix; |
|
13
|
|
|
|
|
57
|
|
|
13
|
|
|
|
|
562
|
|
11
|
13
|
|
|
13
|
|
6732
|
use HTTP::Request::CurlParameters; |
|
13
|
|
|
|
|
56
|
|
|
13
|
|
|
|
|
573
|
|
12
|
13
|
|
|
13
|
|
7361
|
use HTTP::Request::Generator 'generate_requests'; |
|
13
|
|
|
|
|
293164
|
|
|
13
|
|
|
|
|
915
|
|
13
|
13
|
|
|
13
|
|
115
|
use PerlX::Maybe; |
|
13
|
|
|
|
|
29
|
|
|
13
|
|
|
|
|
117
|
|
14
|
13
|
|
|
13
|
|
6720
|
use MIME::Base64 'encode_base64'; |
|
13
|
|
|
|
|
8280
|
|
|
13
|
|
|
|
|
802
|
|
15
|
13
|
|
|
13
|
|
131
|
use File::Basename 'basename'; |
|
13
|
|
|
|
|
39
|
|
|
13
|
|
|
|
|
655
|
|
16
|
|
|
|
|
|
|
|
17
|
13
|
|
|
13
|
|
87
|
use Filter::signatures; |
|
13
|
|
|
|
|
25
|
|
|
13
|
|
|
|
|
107
|
|
18
|
13
|
|
|
13
|
|
343
|
use feature 'signatures'; |
|
13
|
|
|
|
|
29
|
|
|
13
|
|
|
|
|
908
|
|
19
|
13
|
|
|
13
|
|
77
|
no warnings 'experimental::signatures'; |
|
13
|
|
|
|
|
30
|
|
|
13
|
|
|
|
|
39286
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $VERSION = '0.50'; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 NAME |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
HTTP::Request::FromCurl - create a HTTP::Request from a curl command line |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 SYNOPSIS |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $req = HTTP::Request::FromCurl->new( |
30
|
|
|
|
|
|
|
# Note - curl itself may not appear |
31
|
|
|
|
|
|
|
argv => ['https://example.com'], |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $req = HTTP::Request::FromCurl->new( |
35
|
|
|
|
|
|
|
command => 'https://example.com', |
36
|
|
|
|
|
|
|
); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my $req = HTTP::Request::FromCurl->new( |
39
|
|
|
|
|
|
|
command_curl => 'curl -A mycurl/1.0 https://example.com', |
40
|
|
|
|
|
|
|
); |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
my @requests = HTTP::Request::FromCurl->new( |
43
|
|
|
|
|
|
|
command_curl => 'curl -A mycurl/1.0 https://example.com https://www.example.com', |
44
|
|
|
|
|
|
|
); |
45
|
|
|
|
|
|
|
# Send the requests |
46
|
|
|
|
|
|
|
for my $r (@requests) { |
47
|
|
|
|
|
|
|
$ua->request( $r->as_request ) |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 RATIONALE |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
C command lines are found everywhere in documentation. The Firefox |
53
|
|
|
|
|
|
|
developer tools can also copy network requests as C command lines from |
54
|
|
|
|
|
|
|
the network panel. This module enables converting these to Perl code. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 METHODS |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head2 C<< ->new >> |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $req = HTTP::Request::FromCurl->new( |
61
|
|
|
|
|
|
|
# Note - curl itself may not appear |
62
|
|
|
|
|
|
|
argv => ['--user-agent', 'myscript/1.0', 'https://example.com'], |
63
|
|
|
|
|
|
|
); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my $req = HTTP::Request::FromCurl->new( |
66
|
|
|
|
|
|
|
# Note - curl itself may not appear |
67
|
|
|
|
|
|
|
command => '--user-agent myscript/1.0 https://example.com', |
68
|
|
|
|
|
|
|
); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
The constructor returns one or more L objects |
71
|
|
|
|
|
|
|
that encapsulate the parameters. If the command generates multiple requests, |
72
|
|
|
|
|
|
|
they will be returned in list context. In scalar context, only the first request |
73
|
|
|
|
|
|
|
will be returned. Note that the order of URLs between C<--url> and unadorned URLs will be changed in the sense that all unadorned URLs will be handled first. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my $req = HTTP::Request::FromCurl->new( |
76
|
|
|
|
|
|
|
command => '--data-binary @/etc/passwd https://example.com', |
77
|
|
|
|
|
|
|
read_files => 1, |
78
|
|
|
|
|
|
|
); |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=head3 Options |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=over 4 |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=item B |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
An arrayref of commands as could be given in C< @ARGV >. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item B |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
A scalar in a command line, excluding the C command |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item B |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
A scalar in a command line, including the C command |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item B |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Do read in the content of files specified with (for example) |
99
|
|
|
|
|
|
|
C<< --data=@/etc/passwd >>. The default is to not read the contents of files |
100
|
|
|
|
|
|
|
specified this way. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=back |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head1 GLOBAL VARIABLES |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 C<< %default_headers >> |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Contains the default headers added to every request |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=cut |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
our %default_headers = ( |
113
|
|
|
|
|
|
|
'Accept' => '*/*', |
114
|
|
|
|
|
|
|
'User-Agent' => 'curl/7.55.1', |
115
|
|
|
|
|
|
|
); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head2 C<< @option_spec >> |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Contains the L specification of the recognized command line |
120
|
|
|
|
|
|
|
parameters. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
The following C options are recognized but largely ignored: |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=over 4 |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item C< --dump-header > |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item C< --include > |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=item C< --location > |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item C< --progress-bar > |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=item C< --show-error > |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item C< --fail > |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=item C< --silent > |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=item C< --verbose > |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item C< --junk-session-cookies > |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
If you want to keep session cookies between subsequent requests, you need to |
145
|
|
|
|
|
|
|
provide a cookie jar in your user agent. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item C<--next> |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Resetting the UA between requests is something you need to handle yourself |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item C<--parallel> |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=item C<--parallel-immediate> |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item C<--parallel-max> |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Parallel requests is something you need to handle in the UA |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=back |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=cut |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
our @option_spec = ( |
164
|
|
|
|
|
|
|
'user-agent|A=s', |
165
|
|
|
|
|
|
|
'verbose|v', # ignored |
166
|
|
|
|
|
|
|
'show-error|S', # ignored |
167
|
|
|
|
|
|
|
'fail|f', # ignored |
168
|
|
|
|
|
|
|
'silent|s', # ignored |
169
|
|
|
|
|
|
|
'anyauth', # ignored |
170
|
|
|
|
|
|
|
'basic', |
171
|
|
|
|
|
|
|
'buffer!', |
172
|
|
|
|
|
|
|
'capath=s', |
173
|
|
|
|
|
|
|
'cert|E=s', |
174
|
|
|
|
|
|
|
'compressed', |
175
|
|
|
|
|
|
|
'cookie|b=s', |
176
|
|
|
|
|
|
|
'cookie-jar|c=s', |
177
|
|
|
|
|
|
|
'data|d=s@', |
178
|
|
|
|
|
|
|
'data-ascii=s@', |
179
|
|
|
|
|
|
|
'data-binary=s@', |
180
|
|
|
|
|
|
|
'data-raw=s@', |
181
|
|
|
|
|
|
|
'data-urlencode=s@', |
182
|
|
|
|
|
|
|
'digest', |
183
|
|
|
|
|
|
|
'dump-header|D=s', # ignored |
184
|
|
|
|
|
|
|
'referrer|e=s', |
185
|
|
|
|
|
|
|
'form|F=s@', |
186
|
|
|
|
|
|
|
'form-string=s@', |
187
|
|
|
|
|
|
|
'get|G', |
188
|
|
|
|
|
|
|
'globoff|g', |
189
|
|
|
|
|
|
|
'head|I', |
190
|
|
|
|
|
|
|
'header|H=s@', |
191
|
|
|
|
|
|
|
'include|i', # ignored |
192
|
|
|
|
|
|
|
'insecure|k', |
193
|
|
|
|
|
|
|
'location|L', # ignored, we always follow redirects |
194
|
|
|
|
|
|
|
'max-time|m=s', |
195
|
|
|
|
|
|
|
'ntlm', |
196
|
|
|
|
|
|
|
'keepalive!', |
197
|
|
|
|
|
|
|
'range=s', |
198
|
|
|
|
|
|
|
'request|X=s', |
199
|
|
|
|
|
|
|
'oauth2-bearer=s', |
200
|
|
|
|
|
|
|
'output|o=s', |
201
|
|
|
|
|
|
|
'progress-bar|#', # ignored |
202
|
|
|
|
|
|
|
'user|u=s', |
203
|
|
|
|
|
|
|
'next', # ignored |
204
|
|
|
|
|
|
|
'parallel|Z', # ignored |
205
|
|
|
|
|
|
|
'parallel-immediate', # ignored |
206
|
|
|
|
|
|
|
'parallel-max', # ignored |
207
|
|
|
|
|
|
|
'junk-session-cookies|j', # ignored, must be set in code using the HTTP request |
208
|
|
|
|
|
|
|
'unix-socket=s', |
209
|
|
|
|
|
|
|
'url=s@', |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
|
212
|
5
|
|
|
5
|
1
|
4237
|
sub new( $class, %options ) { |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
8
|
|
213
|
5
|
|
|
|
|
11
|
my $cmd = $options{ argv }; |
214
|
|
|
|
|
|
|
|
215
|
5
|
100
|
|
|
|
19
|
if( $options{ command }) { |
|
|
50
|
|
|
|
|
|
216
|
1
|
|
|
|
|
467
|
require Text::ParseWords; |
217
|
1
|
|
|
|
|
1357
|
$cmd = [ Text::ParseWords::shellwords($options{ command }) ]; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
} elsif( $options{ command_curl }) { |
220
|
0
|
|
|
|
|
0
|
require Text::ParseWords; |
221
|
0
|
|
|
|
|
0
|
$cmd = [ Text::ParseWords::shellwords($options{ command_curl }) ]; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# remove the implicit curl command: |
224
|
0
|
|
|
|
|
0
|
shift @$cmd; |
225
|
|
|
|
|
|
|
}; |
226
|
|
|
|
|
|
|
|
227
|
5
|
|
|
|
|
455
|
for (@$cmd) { |
228
|
28
|
50
|
|
|
|
64
|
$_ = '--next' |
229
|
|
|
|
|
|
|
if $_ eq '-:'; # GetOptions does not like "next|:" as specification |
230
|
|
|
|
|
|
|
}; |
231
|
|
|
|
|
|
|
|
232
|
5
|
|
|
|
|
33
|
my $p = Getopt::Long::Parser->new( |
233
|
|
|
|
|
|
|
config => [ 'bundling', 'no_auto_abbrev', 'no_ignore_case_always' ], |
234
|
|
|
|
|
|
|
); |
235
|
5
|
50
|
|
|
|
542
|
$p->getoptionsfromarray( $cmd, |
236
|
|
|
|
|
|
|
\my %curl_options, |
237
|
|
|
|
|
|
|
@option_spec, |
238
|
|
|
|
|
|
|
) or return; |
239
|
5
|
50
|
|
|
|
14943
|
my @urls = (@$cmd, @{ $curl_options{ url } || [] }); |
|
5
|
|
|
|
|
35
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
return |
242
|
5
|
100
|
|
|
|
38
|
wantarray ? map { $class->_build_request( $_, \%curl_options, %options ) } @urls |
|
7
|
|
|
|
|
29
|
|
243
|
|
|
|
|
|
|
: ($class->_build_request( $urls[0], \%curl_options, %options ))[0] |
244
|
|
|
|
|
|
|
; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head1 METHODS |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=head2 C<< ->squash_uri( $uri ) >> |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
my $uri = HTTP::Request::FromCurl->squash_uri( |
252
|
|
|
|
|
|
|
URI->new( 'https://example.com/foo/bar/..' ) |
253
|
|
|
|
|
|
|
); |
254
|
|
|
|
|
|
|
# https://example.com/foo/ |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Helper method to clean up relative path elements from the URI the same way |
257
|
|
|
|
|
|
|
that curl does. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=cut |
260
|
|
|
|
|
|
|
|
261
|
19
|
|
|
19
|
1
|
16939
|
sub squash_uri( $class, $uri ) { |
|
19
|
|
|
|
|
35
|
|
|
19
|
|
|
|
|
32
|
|
|
19
|
|
|
|
|
35
|
|
262
|
19
|
|
|
|
|
79
|
my $u = $uri->clone; |
263
|
19
|
|
|
|
|
247
|
my @segments = $u->path_segments; |
264
|
|
|
|
|
|
|
|
265
|
19
|
100
|
100
|
|
|
814
|
if( $segments[-1] and ($segments[-1] eq '..' or $segments[-1] eq '.' ) ) { |
|
|
|
100
|
|
|
|
|
266
|
6
|
|
|
|
|
14
|
push @segments, ''; |
267
|
|
|
|
|
|
|
}; |
268
|
|
|
|
|
|
|
|
269
|
19
|
|
|
|
|
40
|
@segments = grep { $_ ne '.' } @segments; |
|
57
|
|
|
|
|
117
|
|
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# While we find a pair ( "foo", ".." ) remove that pair |
272
|
19
|
|
|
|
|
47
|
while( grep { $_ eq '..' } @segments ) { |
|
75
|
|
|
|
|
150
|
|
273
|
10
|
|
|
|
|
15
|
my $i = 0; |
274
|
10
|
|
|
|
|
28
|
while( $i < $#segments ) { |
275
|
28
|
100
|
100
|
|
|
126
|
if( $segments[$i] ne '..' and $segments[$i+1] eq '..') { |
276
|
12
|
|
|
|
|
41
|
splice @segments, $i, 2; |
277
|
|
|
|
|
|
|
} else { |
278
|
16
|
|
|
|
|
30
|
$i++ |
279
|
|
|
|
|
|
|
}; |
280
|
|
|
|
|
|
|
}; |
281
|
|
|
|
|
|
|
}; |
282
|
|
|
|
|
|
|
|
283
|
19
|
100
|
|
|
|
48
|
if( @segments < 2 ) { |
284
|
11
|
|
|
|
|
29
|
@segments = ('',''); |
285
|
|
|
|
|
|
|
}; |
286
|
|
|
|
|
|
|
|
287
|
19
|
|
|
|
|
61
|
$u->path_segments( @segments ); |
288
|
19
|
|
|
|
|
1258
|
return $u |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
30
|
|
|
30
|
|
40
|
sub _add_header( $self, $headers, $h, $value ) { |
|
30
|
|
|
|
|
43
|
|
|
30
|
|
|
|
|
42
|
|
|
30
|
|
|
|
|
53
|
|
|
30
|
|
|
|
|
43
|
|
|
30
|
|
|
|
|
36
|
|
292
|
30
|
50
|
|
|
|
58
|
if( exists $headers->{ $h }) { |
293
|
0
|
0
|
|
|
|
0
|
if (!ref( $headers->{ $h })) { |
294
|
0
|
|
|
|
|
0
|
$headers->{ $h } = [ $headers->{ $h }]; |
295
|
|
|
|
|
|
|
} |
296
|
0
|
|
|
|
|
0
|
push @{ $headers->{ $h } }, $value; |
|
0
|
|
|
|
|
0
|
|
297
|
|
|
|
|
|
|
} else { |
298
|
30
|
|
|
|
|
66
|
$headers->{ $h } = $value; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
1
|
|
|
1
|
|
3
|
sub _maybe_read_data_file( $self, $read_files, $data ) { |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
3
|
|
303
|
1
|
|
|
|
|
2
|
my $res; |
304
|
1
|
50
|
|
|
|
4
|
if( $read_files ) { |
305
|
0
|
0
|
|
|
|
0
|
if( $data =~ /^\@(.*)/ ) { |
306
|
0
|
0
|
|
|
|
0
|
open my $fh, '<', $1 |
307
|
|
|
|
|
|
|
or die "$1: $!"; |
308
|
0
|
|
|
|
|
0
|
local $/; # / for Filter::Simple |
309
|
0
|
|
|
|
|
0
|
binmode $fh; |
310
|
0
|
|
|
|
|
0
|
$res = <$fh> |
311
|
|
|
|
|
|
|
} else { |
312
|
0
|
|
|
|
|
0
|
$res = $data |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} else { |
315
|
1
|
50
|
|
|
|
8
|
$res = ($data =~ /^\@(.*)/) |
316
|
|
|
|
|
|
|
? "... contents of $1 ..." |
317
|
|
|
|
|
|
|
: $data |
318
|
|
|
|
|
|
|
} |
319
|
1
|
|
|
|
|
3
|
return $res |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
0
|
|
|
0
|
|
0
|
sub _maybe_read_upload_file( $self, $read_files, $data ) { |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
323
|
0
|
|
|
|
|
0
|
my $res; |
324
|
0
|
0
|
|
|
|
0
|
if( $read_files ) { |
325
|
0
|
0
|
|
|
|
0
|
if( $data =~ /^<(.*)/ ) { |
|
|
0
|
|
|
|
|
|
326
|
0
|
0
|
|
|
|
0
|
open my $fh, '<', $1 |
327
|
|
|
|
|
|
|
or die "$1: $!"; |
328
|
0
|
|
|
|
|
0
|
local $/; # / for Filter::Simple |
329
|
0
|
|
|
|
|
0
|
binmode $fh; |
330
|
0
|
|
|
|
|
0
|
$res = <$fh> |
331
|
|
|
|
|
|
|
} elsif( $data =~ /^\@(.*)/ ) { |
332
|
|
|
|
|
|
|
# Upload the file |
333
|
0
|
|
|
|
|
0
|
$res = [ $1 => basename($1), Content_Type => 'application/octet-stream' ]; |
334
|
|
|
|
|
|
|
} else { |
335
|
0
|
|
|
|
|
0
|
$res = $data |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
} else { |
338
|
0
|
0
|
|
|
|
0
|
if( $data =~ /^[<@](.*)/ ) { |
339
|
0
|
|
|
|
|
0
|
$res = [ undef, basename($1), Content_Type => 'application/octet-stream', Content => "... contents of $1 ..." ], |
340
|
|
|
|
|
|
|
} else { |
341
|
0
|
|
|
|
|
0
|
$res = $data |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
} |
344
|
0
|
|
|
|
|
0
|
return $res |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
8
|
|
|
8
|
|
13
|
sub _build_request( $self, $uri, $options, %build_options ) { |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
21
|
|
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
14
|
|
348
|
8
|
|
|
|
|
12
|
my $body; |
349
|
|
|
|
|
|
|
|
350
|
8
|
100
|
|
|
|
13
|
my @headers = @{ $options->{header} || []}; |
|
8
|
|
|
|
|
36
|
|
351
|
8
|
|
|
|
|
19
|
my $method = $options->{request}; |
352
|
|
|
|
|
|
|
# Ideally, we shouldn't sort the data but process it in-order |
353
|
8
|
100
|
|
|
|
28
|
my @post_read_data = (@{ $options->{'data'} || []}, |
354
|
8
|
50
|
|
|
|
14
|
@{ $options->{'data-ascii'} || [] } |
|
8
|
|
|
|
|
31
|
|
355
|
|
|
|
|
|
|
); |
356
|
|
|
|
|
|
|
; |
357
|
8
|
50
|
|
|
|
15
|
my @post_raw_data = @{ $options->{'data-raw'} || [] }, |
|
8
|
|
|
|
|
32
|
|
358
|
|
|
|
|
|
|
; |
359
|
8
|
50
|
|
|
|
13
|
my @post_urlencode_data = @{ $options->{'data-urlencode'} || [] }; |
|
8
|
|
|
|
|
27
|
|
360
|
8
|
50
|
|
|
|
15
|
my @post_binary_data = @{ $options->{'data-binary'} || [] }; |
|
8
|
|
|
|
|
29
|
|
361
|
|
|
|
|
|
|
|
362
|
8
|
|
|
|
|
13
|
my @form_args; |
363
|
8
|
50
|
|
|
|
19
|
if( $options->{form}) { |
364
|
|
|
|
|
|
|
# support --form uploaded_file=@myfile |
365
|
|
|
|
|
|
|
# and --form "uploaded_text=<~/texts/content.txt" |
366
|
|
|
|
|
|
|
push @form_args, map { /^([^=]+)=(.*)$/ |
367
|
0
|
0
|
|
|
|
0
|
? ($1 => $self->_maybe_read_upload_file( $build_options{ read_files }, $2 )) |
368
|
0
|
|
|
|
|
0
|
: () } @{$options->{form} |
369
|
0
|
|
|
|
|
0
|
}; |
370
|
|
|
|
|
|
|
}; |
371
|
8
|
50
|
|
|
|
19
|
if( $options->{'form-string'}) { |
372
|
0
|
0
|
|
|
|
0
|
push @form_args, map {; /^([^=]+)=(.*)$/ ? ($1 => $2) : (); } @{ $options->{'form-string'}}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
373
|
|
|
|
|
|
|
}; |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# expand the URI here if wanted |
376
|
8
|
|
|
|
|
18
|
my @uris = ($uri); |
377
|
8
|
100
|
|
|
|
20
|
if( ! $options->{ globoff }) { |
378
|
4
|
|
|
|
|
32
|
@uris = map { $_->{url} } generate_requests( pattern => shift @uris, limit => $build_options{ limit } ); |
|
5
|
|
|
|
|
19880
|
|
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
8
|
|
|
|
|
24
|
my @res; |
382
|
8
|
|
|
|
|
40
|
for my $uri (@uris) { |
383
|
9
|
|
|
|
|
54
|
$uri = URI->new( $uri ); |
384
|
9
|
|
|
|
|
615
|
$uri = $self->squash_uri( $uri ); |
385
|
|
|
|
|
|
|
|
386
|
9
|
100
|
|
|
|
76
|
my $host = $uri->can( 'host_port' ) ? $uri->host_port : "$uri"; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# Stuff we use unless nothing else hits |
389
|
9
|
|
|
|
|
285
|
my %request_default_headers = %default_headers; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Sluuuurp |
392
|
|
|
|
|
|
|
# Thous should be hoisted out of the loop |
393
|
|
|
|
|
|
|
@post_binary_data = map { |
394
|
9
|
|
|
|
|
20
|
$self->_maybe_read_data_file( $build_options{ read_files }, $_ ); |
|
0
|
|
|
|
|
0
|
|
395
|
|
|
|
|
|
|
} @post_binary_data; |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
@post_read_data = map { |
398
|
9
|
|
|
|
|
18
|
my $v = $self->_maybe_read_data_file( $build_options{ read_files }, $_ ); |
|
1
|
|
|
|
|
6
|
|
399
|
1
|
|
|
|
|
4
|
$v =~ s![\r\n]!!g; |
400
|
1
|
|
|
|
|
4
|
$v |
401
|
|
|
|
|
|
|
} @post_read_data; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
@post_urlencode_data = map { |
404
|
9
|
0
|
|
|
|
12
|
m/\A([^@=]*)([=@])?(.*)\z/sm |
|
0
|
|
|
|
|
0
|
|
405
|
|
|
|
|
|
|
or die "This should never happen"; |
406
|
0
|
|
|
|
|
0
|
my ($name, $op, $content) = ($1,$2,$3); |
407
|
0
|
0
|
|
|
|
0
|
if(! $op) { |
|
|
0
|
|
|
|
|
|
408
|
0
|
|
|
|
|
0
|
$content = $name; |
409
|
|
|
|
|
|
|
} elsif( $op eq '@' ) { |
410
|
0
|
|
|
|
|
0
|
$content = "$op$content"; |
411
|
|
|
|
|
|
|
}; |
412
|
0
|
0
|
0
|
|
|
0
|
if( defined $name and length $name ) { |
413
|
0
|
|
|
|
|
0
|
$name .= '='; |
414
|
|
|
|
|
|
|
} else { |
415
|
0
|
|
|
|
|
0
|
$name = ''; |
416
|
|
|
|
|
|
|
}; |
417
|
0
|
|
|
|
|
0
|
my $v = $self->_maybe_read_data_file( $build_options{ read_files }, $content ); |
418
|
0
|
|
|
|
|
0
|
$name . uri_escape( $v ) |
419
|
|
|
|
|
|
|
} @post_urlencode_data; |
420
|
|
|
|
|
|
|
|
421
|
9
|
|
|
|
|
17
|
my $data; |
422
|
9
|
50
|
66
|
|
|
57
|
if( @post_read_data |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
423
|
|
|
|
|
|
|
or @post_binary_data |
424
|
|
|
|
|
|
|
or @post_raw_data |
425
|
|
|
|
|
|
|
or @post_urlencode_data |
426
|
|
|
|
|
|
|
) { |
427
|
1
|
|
|
|
|
4
|
$data = join "&", |
428
|
|
|
|
|
|
|
@post_read_data, |
429
|
|
|
|
|
|
|
@post_binary_data, |
430
|
|
|
|
|
|
|
@post_raw_data, |
431
|
|
|
|
|
|
|
@post_urlencode_data |
432
|
|
|
|
|
|
|
; |
433
|
|
|
|
|
|
|
}; |
434
|
|
|
|
|
|
|
|
435
|
9
|
50
|
|
|
|
50
|
if( @form_args) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
436
|
0
|
|
0
|
|
|
0
|
$method //= 'POST'; |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
#my $req = HTTP::Request::Common::POST( |
439
|
|
|
|
|
|
|
# 'https://example.com', |
440
|
|
|
|
|
|
|
# Content_Type => 'form-data', |
441
|
|
|
|
|
|
|
# Content => \@form_args, |
442
|
|
|
|
|
|
|
#); |
443
|
|
|
|
|
|
|
#$body = $req->content; |
444
|
|
|
|
|
|
|
#$request_default_headers{ 'Content-Type' } = join "; ", $req->headers->content_type; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
} elsif( $options->{ get }) { |
447
|
0
|
|
|
|
|
0
|
$method = 'GET'; |
448
|
|
|
|
|
|
|
# Also, append the POST data to the URL |
449
|
0
|
0
|
|
|
|
0
|
if( $data ) { |
450
|
0
|
|
|
|
|
0
|
my $q = $uri->query; |
451
|
0
|
0
|
0
|
|
|
0
|
if( defined $q and length $q ) { |
452
|
0
|
|
|
|
|
0
|
$q .= "&"; |
453
|
|
|
|
|
|
|
} else { |
454
|
0
|
|
|
|
|
0
|
$q = ""; |
455
|
|
|
|
|
|
|
}; |
456
|
0
|
|
|
|
|
0
|
$q .= $data; |
457
|
0
|
|
|
|
|
0
|
$uri->query( $q ); |
458
|
|
|
|
|
|
|
}; |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
} elsif( $options->{ head }) { |
461
|
0
|
|
|
|
|
0
|
$method = 'HEAD'; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
} elsif( defined $data ) { |
464
|
1
|
|
50
|
|
|
3
|
$method //= 'POST'; |
465
|
1
|
|
|
|
|
2
|
$body = $data; |
466
|
1
|
|
|
|
|
4
|
$request_default_headers{ 'Content-Type' } = 'application/x-www-form-urlencoded'; |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
} else { |
469
|
8
|
|
100
|
|
|
26
|
$method ||= 'GET'; |
470
|
|
|
|
|
|
|
}; |
471
|
|
|
|
|
|
|
|
472
|
9
|
100
|
|
|
|
21
|
if( defined $body ) { |
473
|
1
|
|
|
|
|
4
|
$request_default_headers{ 'Content-Length' } = length $body; |
474
|
|
|
|
|
|
|
}; |
475
|
|
|
|
|
|
|
|
476
|
9
|
50
|
|
|
|
22
|
if( $options->{ 'oauth2-bearer' } ) { |
477
|
0
|
|
|
|
|
0
|
push @headers, sprintf 'Authorization: Bearer %s', $options->{'oauth2-bearer'}; |
478
|
|
|
|
|
|
|
}; |
479
|
|
|
|
|
|
|
|
480
|
9
|
100
|
|
|
|
21
|
if( $options->{ 'user' } ) { |
481
|
1
|
50
|
33
|
|
|
27
|
if( $options->{anyauth} |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
482
|
|
|
|
|
|
|
|| $options->{digest} |
483
|
|
|
|
|
|
|
|| $options->{ntlm} |
484
|
|
|
|
|
|
|
|| $options->{negotiate} |
485
|
|
|
|
|
|
|
) { |
486
|
|
|
|
|
|
|
# Nothing to do here, just let LWP::UserAgent do its thing |
487
|
|
|
|
|
|
|
# This means one additional request to fetch the appropriate |
488
|
|
|
|
|
|
|
# 401 response asking for credentials, but ... |
489
|
|
|
|
|
|
|
} else { |
490
|
|
|
|
|
|
|
# $options->{basic} or none at all |
491
|
1
|
|
|
|
|
4
|
my $info = delete $options->{'user'}; |
492
|
|
|
|
|
|
|
# We need to bake this into the header here?! |
493
|
1
|
|
|
|
|
11
|
push @headers, sprintf 'Authorization: Basic %s', encode_base64( $info ); |
494
|
|
|
|
|
|
|
} |
495
|
|
|
|
|
|
|
}; |
496
|
|
|
|
|
|
|
|
497
|
9
|
|
|
|
|
14
|
my %headers; |
498
|
9
|
|
|
|
|
21
|
for my $kv ( |
499
|
2
|
50
|
|
|
|
17
|
(map { /^\s*([^:\s]+)\s*:\s*(.*)$/ ? [$1 => $2] : () } @headers),) { |
500
|
2
|
|
|
|
|
8
|
$self->_add_header( \%headers, @$kv ); |
501
|
|
|
|
|
|
|
}; |
502
|
|
|
|
|
|
|
|
503
|
9
|
50
|
|
|
|
24
|
if( defined $options->{ 'user-agent' }) { |
504
|
0
|
|
|
|
|
0
|
$self->_add_header( \%headers, "User-Agent", $options->{ 'user-agent' } ); |
505
|
|
|
|
|
|
|
}; |
506
|
|
|
|
|
|
|
|
507
|
9
|
50
|
|
|
|
19
|
if( defined $options->{ referrer }) { |
508
|
0
|
|
|
|
|
0
|
$self->_add_header( \%headers, "Referer" => $options->{ 'referrer' } ); |
509
|
|
|
|
|
|
|
}; |
510
|
|
|
|
|
|
|
|
511
|
9
|
50
|
|
|
|
21
|
if( defined $options->{ range }) { |
512
|
0
|
|
|
|
|
0
|
$self->_add_header( \%headers, "Range" => $options->{ 'range' } ); |
513
|
|
|
|
|
|
|
}; |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
# We want to compare the headers case-insensitively |
516
|
9
|
|
|
|
|
25
|
my %headers_lc = map { lc $_ => 1 } keys %headers; |
|
2
|
|
|
|
|
10
|
|
517
|
|
|
|
|
|
|
|
518
|
9
|
|
|
|
|
21
|
for my $k (keys %request_default_headers) { |
519
|
20
|
100
|
|
|
|
58
|
if( ! $headers_lc{ lc $k }) { |
520
|
19
|
|
|
|
|
47
|
$self->_add_header( \%headers, $k, $request_default_headers{ $k }); |
521
|
|
|
|
|
|
|
}; |
522
|
|
|
|
|
|
|
}; |
523
|
9
|
50
|
|
|
|
22
|
if( ! $headers{ 'Host' }) { |
524
|
9
|
|
|
|
|
26
|
$self->_add_header( \%headers, 'Host' => $host ); |
525
|
|
|
|
|
|
|
}; |
526
|
|
|
|
|
|
|
|
527
|
9
|
50
|
|
|
|
23
|
if( defined $options->{ 'cookie-jar' }) { |
528
|
0
|
|
|
|
|
0
|
$options->{'cookie-jar-options'}->{ 'write' } = 1; |
529
|
|
|
|
|
|
|
}; |
530
|
|
|
|
|
|
|
|
531
|
9
|
50
|
|
|
|
23
|
if( defined( my $c = $options->{ cookie })) { |
532
|
0
|
0
|
|
|
|
0
|
if( $c =~ /=/ ) { |
533
|
0
|
|
|
|
|
0
|
$headers{ Cookie } = $options->{ 'cookie' }; |
534
|
|
|
|
|
|
|
} else { |
535
|
0
|
|
|
|
|
0
|
$options->{'cookie-jar'} = $c; |
536
|
0
|
|
|
|
|
0
|
$options->{'cookie-jar-options'}->{ 'read' } = 1; |
537
|
|
|
|
|
|
|
}; |
538
|
|
|
|
|
|
|
}; |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
# Curl 7.61.0 ignores these: |
541
|
|
|
|
|
|
|
#if( $options->{ keepalive }) { |
542
|
|
|
|
|
|
|
# $headers{ 'Keep-Alive' } = 1; |
543
|
|
|
|
|
|
|
#} elsif( exists $options->{ keepalive }) { |
544
|
|
|
|
|
|
|
# $headers{ 'Keep-Alive' } = 0; |
545
|
|
|
|
|
|
|
#}; |
546
|
|
|
|
|
|
|
|
547
|
9
|
50
|
|
|
|
21
|
if( $options->{ compressed }) { |
548
|
0
|
|
|
|
|
0
|
my $compressions = HTTP::Message::decodable(); |
549
|
0
|
|
|
|
|
0
|
$self->_add_header( \%headers, 'Accept-Encoding' => $compressions ); |
550
|
|
|
|
|
|
|
}; |
551
|
|
|
|
|
|
|
|
552
|
9
|
|
|
|
|
14
|
my $auth; |
553
|
9
|
|
|
|
|
23
|
for my $kind (qw(basic ntlm negotiate)) { |
554
|
27
|
50
|
|
|
|
56
|
if( $options->{$kind}) { |
555
|
0
|
|
|
|
|
0
|
$auth = $kind; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
}; |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
push @res, HTTP::Request::CurlParameters->new({ |
560
|
|
|
|
|
|
|
method => $method, |
561
|
|
|
|
|
|
|
uri => $uri, |
562
|
|
|
|
|
|
|
headers => \%headers, |
563
|
|
|
|
|
|
|
body => $body, |
564
|
|
|
|
|
|
|
maybe auth => $auth, |
565
|
|
|
|
|
|
|
maybe cert => $options->{cert}, |
566
|
|
|
|
|
|
|
maybe capath => $options->{capath}, |
567
|
|
|
|
|
|
|
maybe credentials => $options->{ user }, |
568
|
|
|
|
|
|
|
maybe output => $options->{ output }, |
569
|
|
|
|
|
|
|
maybe timeout => $options->{ 'max-time' }, |
570
|
|
|
|
|
|
|
maybe cookie_jar => $options->{'cookie-jar'}, |
571
|
|
|
|
|
|
|
maybe cookie_jar_options => $options->{'cookie-jar-options'}, |
572
|
|
|
|
|
|
|
maybe insecure => $options->{'insecure'}, |
573
|
|
|
|
|
|
|
maybe show_error => $options->{'show-error'}, |
574
|
|
|
|
|
|
|
maybe fail => $options->{'fail'}, |
575
|
9
|
50
|
|
|
|
327
|
maybe unix_socket => $options->{'unix-socket'}, |
576
|
|
|
|
|
|
|
maybe form_args => scalar @form_args ? \@form_args : undef, |
577
|
|
|
|
|
|
|
}); |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
return @res |
581
|
8
|
|
|
|
|
143
|
}; |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
1; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
=head1 LIVE DEMO |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
L |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
=head1 KNOWN DIFFERENCES |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
=head2 Incompatible cookie jar formats |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
Until somebody writes a robust Netscape cookie file parser and proper loading |
594
|
|
|
|
|
|
|
and storage for L, this module will not be able to load and |
595
|
|
|
|
|
|
|
save files in the format that Curl uses. |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=head2 Loading/saving cookie jars is the job of the UA |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
You're expected to instruct your UA to load/save cookie jars: |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
use Path::Tiny; |
602
|
|
|
|
|
|
|
use HTTP::CookieJar::LWP; |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
if( my $cookies = $r->cookie_jar ) { |
605
|
|
|
|
|
|
|
$ua->cookie_jar( HTTP::CookieJar::LWP->new()->load_cookies( |
606
|
|
|
|
|
|
|
path($cookies)->lines |
607
|
|
|
|
|
|
|
)); |
608
|
|
|
|
|
|
|
}; |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=head2 Different Content-Length for POST requests |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=head2 Different delimiter for form data |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
The delimiter is built by L, and C uses a different |
615
|
|
|
|
|
|
|
mechanism to come up with a unique data delimiter. This results in differences |
616
|
|
|
|
|
|
|
in the raw body content and the C header. |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=head1 MISSING FUNCTIONALITY |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=over 4 |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=item * |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
File uploads / content from files |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
While file uploads and reading POST data from files are supported, the content |
627
|
|
|
|
|
|
|
is slurped into memory completely. This can be problematic for large files |
628
|
|
|
|
|
|
|
and little available memory. |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=item * |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
Mixed data instances |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
Multiple mixed instances of C<--data>, C<--data-ascii>, C<--data-raw>, |
635
|
|
|
|
|
|
|
C<--data-binary> or C<--data-raw> are sorted by type first instead of getting |
636
|
|
|
|
|
|
|
concatenated in the order they appear on the command line. |
637
|
|
|
|
|
|
|
If the order is important to you, use one type only. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=item * |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
Multiple sets of parameters from the command line |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
Curl supports the C<< --next >> command line switch which resets |
644
|
|
|
|
|
|
|
parameters for the next URL. |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
This is not (yet) supported. |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
=back |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
=head1 SEE ALSO |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
L |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
L |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
L |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
L - for the inverse function |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
The module HTTP::Request::AsCurl likely also implements a much better version |
661
|
|
|
|
|
|
|
of C<< ->as_curl >> than this module. |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
L - a converter for multiple |
664
|
|
|
|
|
|
|
target languages |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=head1 REPOSITORY |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
The public repository of this module is |
669
|
|
|
|
|
|
|
L. |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=head1 SUPPORT |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
The public support forum of this module is |
674
|
|
|
|
|
|
|
L. |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=head1 BUG TRACKER |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
Please report bugs in this module via the Github bug queue at |
679
|
|
|
|
|
|
|
L |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=head1 AUTHOR |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
Max Maischein C |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=head1 COPYRIGHT (c) |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Copyright 2018-2023 by Max Maischein C. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=head1 LICENSE |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
This module is released under the same terms as Perl itself. |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
=cut |