line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package HTTP::Request::FromWget; |
2
|
3
|
|
|
3
|
|
1472
|
use strict; |
|
3
|
|
|
|
|
25
|
|
|
3
|
|
|
|
|
88
|
|
3
|
3
|
|
|
3
|
|
19
|
use warnings; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
77
|
|
4
|
3
|
|
|
3
|
|
1449
|
use HTTP::Request; |
|
3
|
|
|
|
|
79674
|
|
|
3
|
|
|
|
|
92
|
|
5
|
3
|
|
|
3
|
|
1566
|
use HTTP::Request::Common; |
|
3
|
|
|
|
|
7339
|
|
|
3
|
|
|
|
|
240
|
|
6
|
3
|
|
|
3
|
|
24
|
use URI; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
66
|
|
7
|
3
|
|
|
3
|
|
2419
|
use Getopt::Long; |
|
3
|
|
|
|
|
32921
|
|
|
3
|
|
|
|
|
12
|
|
8
|
3
|
|
|
3
|
|
470
|
use File::Spec::Unix; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
135
|
|
9
|
3
|
|
|
3
|
|
1696
|
use HTTP::Request::CurlParameters; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
146
|
|
10
|
3
|
|
|
3
|
|
1725
|
use HTTP::Request::Generator 'generate_requests'; |
|
3
|
|
|
|
|
69660
|
|
|
3
|
|
|
|
|
189
|
|
11
|
3
|
|
|
3
|
|
23
|
use PerlX::Maybe; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
28
|
|
12
|
3
|
|
|
3
|
|
1533
|
use MIME::Base64 'encode_base64'; |
|
3
|
|
|
|
|
1935
|
|
|
3
|
|
|
|
|
206
|
|
13
|
|
|
|
|
|
|
|
14
|
3
|
|
|
3
|
|
23
|
use Filter::signatures; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
18
|
|
15
|
3
|
|
|
3
|
|
90
|
use feature 'signatures'; |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
212
|
|
16
|
3
|
|
|
3
|
|
18
|
no warnings 'experimental::signatures'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
6778
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = '0.52'; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 NAME |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
HTTP::Request::FromWget - create a HTTP::Request from a wget command line |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 SYNOPSIS |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $req = HTTP::Request::FromWget->new( |
27
|
|
|
|
|
|
|
# Note - wget itself may not appear |
28
|
|
|
|
|
|
|
argv => ['https://example.com'], |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $req = HTTP::Request::FromWget->new( |
32
|
|
|
|
|
|
|
command => 'https://example.com', |
33
|
|
|
|
|
|
|
); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my $req = HTTP::Request::FromWget->new( |
36
|
|
|
|
|
|
|
command_wget => 'wget -A mywget/1.0 https://example.com', |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my @requests = HTTP::Request::FromWget->new( |
40
|
|
|
|
|
|
|
command_wget => 'wget -A mywget/1.0 https://example.com https://www.example.com', |
41
|
|
|
|
|
|
|
); |
42
|
|
|
|
|
|
|
# Send the requests |
43
|
|
|
|
|
|
|
for my $r (@requests) { |
44
|
|
|
|
|
|
|
$ua->request( $r->as_request ) |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 RATIONALE |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
C command lines are found everywhere in documentation. The Firefox |
50
|
|
|
|
|
|
|
developer tools can also copy network requests as C command lines from |
51
|
|
|
|
|
|
|
the network panel. This module enables converting these to Perl code. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head1 METHODS |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 C<< ->new >> |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my $req = HTTP::Request::FromWget->new( |
58
|
|
|
|
|
|
|
# Note - wget itself may not appear |
59
|
|
|
|
|
|
|
argv => ['--user-agent', 'myscript/1.0', 'https://example.com'], |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
my $req = HTTP::Request::FromWget->new( |
63
|
|
|
|
|
|
|
# Note - wget itself may not appear |
64
|
|
|
|
|
|
|
command => '--user-agent myscript/1.0 https://example.com', |
65
|
|
|
|
|
|
|
); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
The constructor returns one or more L objects |
68
|
|
|
|
|
|
|
that encapsulate the parameters. If the command generates multiple requests, |
69
|
|
|
|
|
|
|
they will be returned in list context. In scalar context, only the first request |
70
|
|
|
|
|
|
|
will be returned. |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
my $req = HTTP::Request::FromWget->new( |
73
|
|
|
|
|
|
|
command => '--post-file /etc/passwd https://example.com', |
74
|
|
|
|
|
|
|
read_files => 1, |
75
|
|
|
|
|
|
|
); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head3 Options |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=over 4 |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item B |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
An arrayref of commands as could be given in C< @ARGV >. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item B |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
A scalar in a command line, excluding the C command |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item B |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
A scalar in a command line, including the C command |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item B |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Do read in the content of files specified with (for example) |
96
|
|
|
|
|
|
|
C<< --data=@/etc/passwd >>. The default is to not read the contents of files |
97
|
|
|
|
|
|
|
specified this way. |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=back |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head1 GLOBAL VARIABLES |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head2 C<< %default_headers >> |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Contains the default headers added to every request |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=cut |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
our %default_headers = ( |
110
|
|
|
|
|
|
|
'Accept' => '*/*', |
111
|
|
|
|
|
|
|
'Accept-Encoding' => 'identity', |
112
|
|
|
|
|
|
|
'User-Agent' => 'Wget/1.21', |
113
|
|
|
|
|
|
|
'Connection' => 'Keep-Alive', |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=head2 C<< @option_spec >> |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
Contains the L specification of the recognized command line |
119
|
|
|
|
|
|
|
parameters. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
The following C options are recognized but largely ignored: |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=over 4 |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=item B |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item B |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=item B |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item B |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=item B |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
If you want to keep session cookies between subsequent requests, you need to |
136
|
|
|
|
|
|
|
provide a cookie jar in your user agent. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=back |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
our @option_spec = ( |
143
|
|
|
|
|
|
|
'auth-no-challenge', # ignored |
144
|
|
|
|
|
|
|
'bind-address=s', |
145
|
|
|
|
|
|
|
'body-data=s', |
146
|
|
|
|
|
|
|
'body-file=s', |
147
|
|
|
|
|
|
|
'buffer!', |
148
|
|
|
|
|
|
|
'cache!', |
149
|
|
|
|
|
|
|
'ca-directory=s', |
150
|
|
|
|
|
|
|
'check-certificate!', |
151
|
|
|
|
|
|
|
'certificate=s', |
152
|
|
|
|
|
|
|
'compression=s', |
153
|
|
|
|
|
|
|
'content-disposition=s', |
154
|
|
|
|
|
|
|
'cookie|b=s@', |
155
|
|
|
|
|
|
|
'cookies!', # ignored |
156
|
|
|
|
|
|
|
'debug', # ignored |
157
|
|
|
|
|
|
|
'header|H=s@', |
158
|
|
|
|
|
|
|
'http-keep-alive!', |
159
|
|
|
|
|
|
|
'http-password=s', |
160
|
|
|
|
|
|
|
'http-user=s', |
161
|
|
|
|
|
|
|
'load-cookies|c=s', |
162
|
|
|
|
|
|
|
'method=s', |
163
|
|
|
|
|
|
|
'no-verbose|nv', # ignored |
164
|
|
|
|
|
|
|
'output-document|O=s', # ignored |
165
|
|
|
|
|
|
|
'post-data=s', |
166
|
|
|
|
|
|
|
'post-file=s', |
167
|
|
|
|
|
|
|
'progress!', # ignored |
168
|
|
|
|
|
|
|
'quiet', # ignored |
169
|
|
|
|
|
|
|
'referer=s', |
170
|
|
|
|
|
|
|
'timeout|T=i', |
171
|
|
|
|
|
|
|
'user-agent|U=s', |
172
|
|
|
|
|
|
|
'verbose|v', # ignored |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
|
175
|
34
|
|
|
34
|
1
|
2278820
|
sub new( $class, %options ) { |
|
34
|
|
|
|
|
157
|
|
|
34
|
|
|
|
|
300
|
|
|
34
|
|
|
|
|
93
|
|
176
|
34
|
|
|
|
|
168
|
my $cmd = $options{ argv }; |
177
|
|
|
|
|
|
|
|
178
|
34
|
50
|
|
|
|
323
|
if( $options{ command }) { |
|
|
50
|
|
|
|
|
|
179
|
0
|
|
|
|
|
0
|
require Text::ParseWords; |
180
|
0
|
|
|
|
|
0
|
$cmd = [ Text::ParseWords::shellwords($options{ command }) ]; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
} elsif( $options{ command_wget }) { |
183
|
0
|
|
|
|
|
0
|
require Text::ParseWords; |
184
|
0
|
|
|
|
|
0
|
$cmd = [ Text::ParseWords::shellwords($options{ command_wget }) ]; |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# remove the implicit wget command: |
187
|
0
|
|
|
|
|
0
|
shift @$cmd; |
188
|
|
|
|
|
|
|
}; |
189
|
|
|
|
|
|
|
|
190
|
34
|
|
|
|
|
907
|
my $p = Getopt::Long::Parser->new( |
191
|
|
|
|
|
|
|
config => [ 'bundling', 'no_auto_abbrev', 'no_ignore_case_always' ], |
192
|
|
|
|
|
|
|
); |
193
|
34
|
50
|
|
|
|
6452
|
$p->getoptionsfromarray( $cmd, |
194
|
|
|
|
|
|
|
\my %wget_options, |
195
|
|
|
|
|
|
|
@option_spec, |
196
|
|
|
|
|
|
|
) or return; |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
return |
199
|
34
|
50
|
|
|
|
92105
|
wantarray ? map { $class->_build_request( $_, \%wget_options, %options ) } @$cmd |
|
34
|
|
|
|
|
377
|
|
200
|
|
|
|
|
|
|
: ($class->_build_request( $cmd->[0], \%wget_options, %options ))[0] |
201
|
|
|
|
|
|
|
; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head1 METHODS |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=head2 C<< ->squash_uri( $uri ) >> |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
my $uri = HTTP::Request::FromWget->squash_uri( |
209
|
|
|
|
|
|
|
URI->new( 'https://example.com/foo/bar/..' ) |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
# https://example.com/foo/ |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
Helper method to clean up relative path elements from the URI the same way |
214
|
|
|
|
|
|
|
that wget does. |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=cut |
217
|
|
|
|
|
|
|
|
218
|
34
|
|
|
34
|
1
|
94
|
sub squash_uri( $class, $uri ) { |
|
34
|
|
|
|
|
89
|
|
|
34
|
|
|
|
|
88
|
|
|
34
|
|
|
|
|
67
|
|
219
|
34
|
|
|
|
|
157
|
my $u = $uri->clone; |
220
|
34
|
|
|
|
|
530
|
my @segments = $u->path_segments; |
221
|
|
|
|
|
|
|
|
222
|
34
|
0
|
0
|
|
|
1919
|
if( $segments[-1] and ($segments[-1] eq '..' or $segments[-1] eq '.' ) ) { |
|
|
|
33
|
|
|
|
|
223
|
0
|
|
|
|
|
0
|
push @segments, ''; |
224
|
|
|
|
|
|
|
}; |
225
|
|
|
|
|
|
|
|
226
|
34
|
|
|
|
|
109
|
@segments = grep { $_ ne '.' } @segments; |
|
68
|
|
|
|
|
207
|
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# While we find a pair ( "foo", ".." ) remove that pair |
229
|
34
|
|
|
|
|
106
|
while( grep { $_ eq '..' } @segments ) { |
|
68
|
|
|
|
|
449
|
|
230
|
0
|
|
|
|
|
0
|
my $i = 0; |
231
|
0
|
|
|
|
|
0
|
while( $i < $#segments ) { |
232
|
0
|
0
|
0
|
|
|
0
|
if( $segments[$i] ne '..' and $segments[$i+1] eq '..') { |
233
|
0
|
|
|
|
|
0
|
splice @segments, $i, 2; |
234
|
|
|
|
|
|
|
} else { |
235
|
0
|
|
|
|
|
0
|
$i++ |
236
|
|
|
|
|
|
|
}; |
237
|
|
|
|
|
|
|
}; |
238
|
|
|
|
|
|
|
}; |
239
|
|
|
|
|
|
|
|
240
|
34
|
50
|
|
|
|
126
|
if( @segments < 2 ) { |
241
|
0
|
|
|
|
|
0
|
@segments = ('',''); |
242
|
|
|
|
|
|
|
}; |
243
|
|
|
|
|
|
|
|
244
|
34
|
|
|
|
|
131
|
$u->path_segments( @segments ); |
245
|
34
|
|
|
|
|
2408
|
return $u |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Ugh - wget doesn't allow for multiple headers of the same name on the command line |
249
|
145
|
|
|
145
|
|
247
|
sub _add_header( $self, $headers, $h, $value ) { |
|
145
|
|
|
|
|
228
|
|
|
145
|
|
|
|
|
201
|
|
|
145
|
|
|
|
|
221
|
|
|
145
|
|
|
|
|
219
|
|
|
145
|
|
|
|
|
183
|
|
250
|
|
|
|
|
|
|
#if( exists $headers->{ $h }) { |
251
|
|
|
|
|
|
|
# if (!ref( $headers->{ $h })) { |
252
|
|
|
|
|
|
|
# $headers->{ $h } = [ $headers->{ $h }]; |
253
|
|
|
|
|
|
|
# } |
254
|
|
|
|
|
|
|
# push @{ $headers->{ $h } }, $value; |
255
|
|
|
|
|
|
|
#} else { |
256
|
145
|
|
|
|
|
395
|
$headers->{ $h } = $value; |
257
|
|
|
|
|
|
|
#} |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
22
|
|
|
22
|
|
65
|
sub _set_header( $self, $headers, $h, $value ) { |
|
22
|
|
|
|
|
51
|
|
|
22
|
|
|
|
|
51
|
|
|
22
|
|
|
|
|
91
|
|
|
22
|
|
|
|
|
52
|
|
|
22
|
|
|
|
|
39
|
|
261
|
22
|
|
|
|
|
93
|
$headers->{ $h } = $value; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
34
|
|
|
34
|
|
69
|
sub _maybe_set_header( $self, $headers, $h, $value ) { |
|
34
|
|
|
|
|
60
|
|
|
34
|
|
|
|
|
58
|
|
|
34
|
|
|
|
|
96
|
|
|
34
|
|
|
|
|
72
|
|
|
34
|
|
|
|
|
58
|
|
265
|
34
|
100
|
|
|
|
122
|
if( ! exists $headers->{ $h }) { |
266
|
30
|
|
|
|
|
96
|
$headers->{ $h } = $value; |
267
|
|
|
|
|
|
|
}; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
1
|
|
|
1
|
|
7
|
sub _maybe_read_data_file( $self, $read_files, $data ) { |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
11
|
|
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
5
|
|
271
|
1
|
|
|
|
|
6
|
my $res; |
272
|
1
|
50
|
|
|
|
18
|
if( $read_files ) { |
273
|
1
|
50
|
|
|
|
66
|
open my $fh, '<', $data |
274
|
|
|
|
|
|
|
or die "$data: $!"; |
275
|
1
|
|
|
|
|
187
|
local $/; # / for Filter::Simple |
276
|
1
|
|
|
|
|
5
|
binmode $fh; |
277
|
1
|
|
|
|
|
41
|
$res = <$fh> |
278
|
|
|
|
|
|
|
} else { |
279
|
0
|
|
|
|
|
0
|
$res = "... contents of $data ..." |
280
|
|
|
|
|
|
|
} |
281
|
1
|
|
|
|
|
9
|
return $res |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
34
|
|
|
34
|
|
92
|
sub _build_request( $self, $uri, $options, %build_options ) { |
|
34
|
|
|
|
|
88
|
|
|
34
|
|
|
|
|
94
|
|
|
34
|
|
|
|
|
68
|
|
|
34
|
|
|
|
|
114
|
|
|
34
|
|
|
|
|
70
|
|
285
|
34
|
|
|
|
|
88
|
my $body; |
286
|
|
|
|
|
|
|
|
287
|
34
|
100
|
|
|
|
78
|
my @headers = @{ $options->{header} || []}; |
|
34
|
|
|
|
|
335
|
|
288
|
34
|
|
|
|
|
157
|
my $method = $options->{method}; |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Ideally, we shouldn't sort the data but process it in-order |
291
|
34
|
|
|
|
|
78
|
my @post_raw_data; |
292
|
34
|
100
|
|
|
|
157
|
if( exists $options->{ 'post-data' }) { |
293
|
3
|
|
|
|
|
28
|
@post_raw_data = $options->{'post-data'}; |
294
|
3
|
|
|
|
|
10
|
$method = 'POST'; |
295
|
|
|
|
|
|
|
}; |
296
|
34
|
100
|
|
|
|
124
|
if( exists $options->{ 'body-data' }) { |
297
|
2
|
|
|
|
|
20
|
@post_raw_data = $options->{'body-data'}; |
298
|
2
|
|
50
|
|
|
20
|
$method ||= 'POST'; |
299
|
|
|
|
|
|
|
}; |
300
|
|
|
|
|
|
|
; |
301
|
34
|
100
|
|
|
|
148
|
if( my $file = $options->{'post-file'} ) { |
302
|
1
|
|
|
|
|
27
|
@post_raw_data = $self->_maybe_read_data_file( $build_options{ read_files }, $file ); |
303
|
1
|
|
|
|
|
8
|
$method = 'POST'; |
304
|
|
|
|
|
|
|
}; |
305
|
34
|
50
|
|
|
|
146
|
if( my $file = $options->{'body-file'} ) { |
306
|
0
|
|
|
|
|
0
|
@post_raw_data = $self->_maybe_read_data_file( $build_options{ read_files }, $file ); |
307
|
0
|
|
0
|
|
|
0
|
$method ||= 'POST'; |
308
|
|
|
|
|
|
|
}; |
309
|
|
|
|
|
|
|
; |
310
|
34
|
50
|
|
|
|
68
|
my @form_args = @{ $options->{form} || []}; |
|
34
|
|
|
|
|
309
|
|
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# expand the URI here if wanted |
313
|
34
|
|
|
|
|
119
|
my @uris = ($uri); |
314
|
34
|
50
|
|
|
|
140
|
if( ! $options->{ globoff }) { |
315
|
34
|
|
|
|
|
501
|
@uris = map { $_->{url} } generate_requests( pattern => shift @uris, limit => $build_options{ limit } ); |
|
34
|
|
|
|
|
46202
|
|
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
34
|
|
|
|
|
170
|
my @res; |
319
|
34
|
|
|
|
|
138
|
for my $uri (@uris) { |
320
|
34
|
|
|
|
|
151
|
$uri = URI->new( $uri ); |
321
|
34
|
|
|
|
|
3443
|
$uri = $self->squash_uri( $uri ); |
322
|
|
|
|
|
|
|
|
323
|
34
|
50
|
|
|
|
323
|
my $host = $uri->can( 'host_port' ) ? $uri->host_port : "$uri"; |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# Stuff we use unless nothing else hits |
326
|
34
|
|
|
|
|
1276
|
my %request_default_headers = %default_headers; |
327
|
|
|
|
|
|
|
|
328
|
34
|
|
|
|
|
105
|
my $data; |
329
|
34
|
100
|
|
|
|
151
|
if( @post_raw_data ) { |
330
|
6
|
|
|
|
|
42
|
$data = join "&", |
331
|
|
|
|
|
|
|
@post_raw_data, |
332
|
|
|
|
|
|
|
; |
333
|
|
|
|
|
|
|
}; |
334
|
|
|
|
|
|
|
|
335
|
34
|
50
|
|
|
|
186
|
if( @form_args) { |
|
|
100
|
|
|
|
|
|
336
|
0
|
|
0
|
|
|
0
|
$method ||= 'POST'; |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
my $req = HTTP::Request::Common::POST( |
339
|
|
|
|
|
|
|
'https://example.com', |
340
|
|
|
|
|
|
|
Content_Type => 'form-data', |
341
|
0
|
0
|
|
|
|
0
|
Content => [ map { /^([^=]+)=(.*)$/ ? ($1 => $2) : () } @form_args ], |
|
0
|
|
|
|
|
0
|
|
342
|
|
|
|
|
|
|
); |
343
|
0
|
|
|
|
|
0
|
$body = $req->content; |
344
|
0
|
|
|
|
|
0
|
$request_default_headers{ 'Content-Type' } = join "; ", $req->headers->content_type; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
} elsif( defined $data ) { |
347
|
6
|
|
50
|
|
|
50
|
$method ||= 'POST'; |
348
|
6
|
|
|
|
|
18
|
$body = $data; |
349
|
6
|
|
|
|
|
37
|
$request_default_headers{ 'Content-Type' } = 'application/x-www-form-urlencoded'; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
} else { |
352
|
28
|
|
50
|
|
|
262
|
$method ||= 'GET'; |
353
|
|
|
|
|
|
|
}; |
354
|
|
|
|
|
|
|
|
355
|
34
|
100
|
|
|
|
165
|
if( defined $body ) { |
356
|
6
|
|
|
|
|
24
|
$request_default_headers{ 'Content-Length' } = length $body; |
357
|
|
|
|
|
|
|
}; |
358
|
|
|
|
|
|
|
|
359
|
34
|
50
|
33
|
|
|
375
|
if( $options->{ 'user' } || $options->{'http-user'} ) { |
360
|
0
|
0
|
0
|
|
|
0
|
if( $options->{anyauth} |
|
|
|
0
|
|
|
|
|
361
|
|
|
|
|
|
|
|| $options->{ntlm} |
362
|
|
|
|
|
|
|
|| $options->{negotiate} |
363
|
|
|
|
|
|
|
) { |
364
|
|
|
|
|
|
|
# Nothing to do here, just let LWP::UserAgent do its thing |
365
|
|
|
|
|
|
|
# This means one additional request to fetch the appropriate |
366
|
|
|
|
|
|
|
# 401 response asking for credentials, but ... |
367
|
|
|
|
|
|
|
} else { |
368
|
|
|
|
|
|
|
# $options->{basic} or none at all |
369
|
0
|
|
0
|
|
|
0
|
my $info = delete $options->{'user'} || delete $options->{'http-user'}; |
370
|
|
|
|
|
|
|
# We need to bake this into the header here?! |
371
|
0
|
|
|
|
|
0
|
push @headers, sprintf 'Authorization: Basic %s', encode_base64( $info ); |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
}; |
374
|
|
|
|
|
|
|
|
375
|
34
|
|
|
|
|
96
|
my %headers; |
376
|
34
|
|
|
|
|
101
|
for my $kv ( |
377
|
27
|
50
|
|
|
|
250
|
(map { /^\s*([^:\s]+)\s*:\s*(.*)$/ ? [$1 => $2] : () } @headers),) { |
378
|
27
|
|
|
|
|
126
|
$self->_add_header( \%headers, @$kv ); |
379
|
|
|
|
|
|
|
}; |
380
|
|
|
|
|
|
|
|
381
|
34
|
100
|
|
|
|
171
|
if( defined $options->{ 'user-agent' }) { |
382
|
20
|
|
|
|
|
210
|
$self->_set_header( \%headers, "User-Agent", $options->{ 'user-agent' } ); |
383
|
|
|
|
|
|
|
}; |
384
|
|
|
|
|
|
|
|
385
|
34
|
100
|
|
|
|
112
|
if( exists $options->{ 'cache' }) { |
386
|
1
|
50
|
|
|
|
5
|
if(! $options->{ 'cache' } ) { |
387
|
0
|
|
|
|
|
0
|
$self->_maybe_set_header( \%headers, "Cache-Control" => 'no-cache' ); |
388
|
0
|
|
|
|
|
0
|
$self->_maybe_set_header( \%headers, "Pragma" => 'no-cache' ); |
389
|
|
|
|
|
|
|
}; |
390
|
|
|
|
|
|
|
}; |
391
|
|
|
|
|
|
|
|
392
|
34
|
100
|
|
|
|
96
|
if( exists $options->{ 'http-keep-alive' }) { |
393
|
2
|
100
|
|
|
|
48
|
if(! $options->{ 'http-keep-alive' } ) { |
394
|
1
|
|
|
|
|
26
|
$self->_set_header( \%headers, "Connection" => 'Close' ); |
395
|
|
|
|
|
|
|
}; |
396
|
|
|
|
|
|
|
}; |
397
|
|
|
|
|
|
|
|
398
|
34
|
100
|
|
|
|
106
|
if( defined $options->{ referer }) { |
399
|
1
|
|
|
|
|
20
|
$self->_set_header( \%headers, "Referer" => $options->{ 'referer' } ); |
400
|
|
|
|
|
|
|
}; |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# We want to compare the headers case-insensitively |
403
|
34
|
|
|
|
|
122
|
my %headers_lc = map { lc $_ => 1 } keys %headers; |
|
48
|
|
|
|
|
191
|
|
404
|
|
|
|
|
|
|
|
405
|
34
|
|
|
|
|
140
|
for my $k (keys %request_default_headers) { |
406
|
148
|
100
|
|
|
|
390
|
if( ! $headers_lc{ lc $k }) { |
407
|
118
|
|
|
|
|
349
|
$self->_add_header( \%headers, $k, $request_default_headers{ $k }); |
408
|
|
|
|
|
|
|
}; |
409
|
|
|
|
|
|
|
}; |
410
|
34
|
|
|
|
|
234
|
$self->_maybe_set_header( \%headers, 'Host' => $host ); |
411
|
|
|
|
|
|
|
|
412
|
34
|
50
|
|
|
|
117
|
if( defined $options->{ 'cookie-jar' }) { |
413
|
0
|
|
|
|
|
0
|
$options->{'cookie-jar-options'}->{ 'write' } = 1; |
414
|
|
|
|
|
|
|
}; |
415
|
|
|
|
|
|
|
|
416
|
34
|
50
|
|
|
|
116
|
if( defined( my $c = $options->{ cookie })) { |
417
|
0
|
0
|
|
|
|
0
|
if( $c =~ /=/ ) { |
418
|
0
|
|
|
|
|
0
|
$headers{ Cookie } = $options->{ 'cookie' }; |
419
|
|
|
|
|
|
|
} else { |
420
|
0
|
|
|
|
|
0
|
$options->{'cookie-jar'} = $c; |
421
|
0
|
|
|
|
|
0
|
$options->{'cookie-jar-options'}->{ 'read' } = 1; |
422
|
|
|
|
|
|
|
}; |
423
|
|
|
|
|
|
|
}; |
424
|
|
|
|
|
|
|
|
425
|
34
|
50
|
|
|
|
106
|
if( my $c = $options->{ compression }) { |
426
|
0
|
0
|
|
|
|
0
|
if( $c =~ /^(gzip|auto)$/ ) { |
427
|
|
|
|
|
|
|
# my $compressions = HTTP::Message::decodable(); |
428
|
0
|
|
|
|
|
0
|
$self->_set_header( \%headers, 'Accept-Encoding' => 'gzip' ); |
429
|
|
|
|
|
|
|
}; |
430
|
|
|
|
|
|
|
}; |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
push @res, HTTP::Request::CurlParameters->new({ |
433
|
|
|
|
|
|
|
method => $method, |
434
|
|
|
|
|
|
|
uri => $uri, |
435
|
|
|
|
|
|
|
headers => \%headers, |
436
|
|
|
|
|
|
|
body => $body, |
437
|
|
|
|
|
|
|
maybe local_address => $options->{local_address}, |
438
|
|
|
|
|
|
|
maybe cert => $options->{certificate}, |
439
|
|
|
|
|
|
|
maybe capath => $options->{'ca-directory'}, |
440
|
|
|
|
|
|
|
maybe credentials => $options->{ user }, |
441
|
|
|
|
|
|
|
maybe output => $options->{ output }, |
442
|
|
|
|
|
|
|
maybe timeout => $options->{ 'max-time' }, |
443
|
|
|
|
|
|
|
maybe cookie_jar => $options->{'cookie-jar'}, |
444
|
|
|
|
|
|
|
maybe cookie_jar_options => $options->{'cookie-jar-options'}, |
445
|
|
|
|
|
|
|
maybe insecure => !$options->{'check-certificate'}, |
446
|
|
|
|
|
|
|
maybe show_error => $options->{'show_error'}, |
447
|
34
|
|
|
|
|
1985
|
maybe fail => $options->{'fail'}, |
448
|
|
|
|
|
|
|
}); |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
return @res |
452
|
34
|
|
|
|
|
1112
|
}; |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
1; |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head1 LIVE DEMO |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
L |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=head1 KNOWN DIFFERENCES |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head2 Incompatible cookie jar formats |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
Until somebody writes a robust Netscape cookie file parser and proper loading |
465
|
|
|
|
|
|
|
and storage for L, this module will not be able to load and |
466
|
|
|
|
|
|
|
save files in the format that wget uses. |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=head2 Loading/saving cookie jars is the job of the UA |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
You're expected to instruct your UA to load/save cookie jars: |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
use Path::Tiny; |
473
|
|
|
|
|
|
|
use HTTP::CookieJar::LWP; |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
if( my $cookies = $r->cookie_jar ) { |
476
|
|
|
|
|
|
|
$ua->cookie_jar( HTTP::CookieJar::LWP->new()->load_cookies( |
477
|
|
|
|
|
|
|
path($cookies)->lines |
478
|
|
|
|
|
|
|
)); |
479
|
|
|
|
|
|
|
}; |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=head2 Different Content-Length for POST requests |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head2 Different delimiter for form data |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
The delimiter is built by L, and C uses a different |
486
|
|
|
|
|
|
|
mechanism to come up with a unique data delimiter. This results in differences |
487
|
|
|
|
|
|
|
in the raw body content and the C header. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=head1 MISSING FUNCTIONALITY |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=over 4 |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=item * |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
File uploads / content from files |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
While file uploads and reading POST data from files are supported, the content |
498
|
|
|
|
|
|
|
is slurped into memory completely. This can be problematic for large files |
499
|
|
|
|
|
|
|
and little available memory. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=back |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head1 SEE ALSO |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
L - for the inverse function |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
The module HTTP::Request::AsCurl likely also implements a much better version |
509
|
|
|
|
|
|
|
of C<< ->as_curl >> than this module. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=head1 REPOSITORY |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
The public repository of this module is |
514
|
|
|
|
|
|
|
L. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head1 SUPPORT |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
The public support forum of this module is |
519
|
|
|
|
|
|
|
L. |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=head1 BUG TRACKER |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
Please report bugs in this module via the Github bug queue at |
524
|
|
|
|
|
|
|
L |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head1 AUTHOR |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Max Maischein C |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head1 COPYRIGHT (c) |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
Copyright 2018-2023 by Max Maischein C. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=head1 LICENSE |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
This module is released under the same terms as Perl itself. |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |