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