File Coverage

blib/lib/HTTP/Request/CurlParameters.pm
Criterion Covered Total %
statement 310 476 65.1
branch 106 202 52.4
condition 29 72 40.2
subroutine 27 32 84.3
pod 5 9 55.5
total 477 791 60.3


line stmt bran cond sub pod time code
1             package HTTP::Request::CurlParameters 0.56;
2 18     18   105087 use 5.020;
  18         135  
3 18     18   1031 use HTTP::Request;
  18         45512  
  18         460  
4 18     18   1013 use HTTP::Request::Common;
  18         5339  
  18         1368  
5 18     18   110 use URI;
  18         30  
  18         383  
6 18     18   67 use File::Spec::Unix;
  18         33  
  18         543  
7 18     18   84 use List::Util 'pairmap';
  18         29  
  18         2230  
8 18     18   8425 use PerlX::Maybe;
  18         50329  
  18         76  
9 18     18   886 use Carp 'croak';
  18         32  
  18         1207  
10              
11 18     18   9823 use Moo 2;
  18         174405  
  18         131  
12 18     18   28742 use feature 'signatures';
  18         35  
  18         2938  
13 18     18   115 no warnings 'experimental::signatures';
  18         33  
  18         165399  
14              
15             =head1 NAME
16              
17             HTTP::Request::CurlParameters - container for a Curl-like HTTP request
18              
19             =head1 SYNOPSIS
20              
21             my $ua = LWP::UserAgent->new;
22             my $params = HTTP::Request::CurlParameters->new(argv => \@ARGV);
23             my $response = $ua->request($params->as_request);
24              
25             =head1 DESCRIPTION
26              
27             Objects of this class are mostly created from L. Most
28             likely you want to use that module instead:
29              
30             my $ua = LWP::UserAgent->new;
31             my $params = HTTP::Request::FromCurl->new(command_curl => $cmd);
32             my $response = $ua->request($params->as_request);
33              
34             =head1 METHODS
35              
36             =head2 C<< ->new >>
37              
38             Options:
39              
40             =over 4
41              
42             =item *
43              
44             C
45              
46             method => 'GET'
47              
48             The HTTP method to use.
49              
50             =cut
51              
52             has method => (
53             is => 'ro',
54             default => 'GET',
55             );
56              
57             =item *
58              
59             C
60              
61             uri => 'https://example.com'
62              
63             The URI of the request.
64              
65             =cut
66              
67             has uri => (
68             is => 'ro',
69             default => 'https://example.com',
70             );
71              
72             =item *
73              
74             C
75              
76             headers => {
77             'Content-Type' => 'text/json',
78             'X-Secret' => ['value-1', 'value-2'],
79             }
80              
81             The headers of the request. Multiple headers with the same
82             name can be passed as an arrayref to the header key.
83              
84             =cut
85              
86             has headers => (
87             is => 'ro',
88             default => sub { {} },
89             );
90              
91             =item *
92              
93             C
94              
95             The cookie jar to use.
96              
97             =cut
98              
99             has cookie_jar => (
100             is => 'ro',
101             );
102              
103             =item *
104              
105             C
106              
107             Options for the constructor of the cookie jar.
108              
109             =cut
110              
111             has cookie_jar_options => (
112             is => 'ro',
113             default => sub { {} },
114             );
115              
116             =item *
117              
118             C
119              
120             credentials => 'hunter2:secret'
121              
122             The credentials to use for basic authentication.
123              
124             =cut
125              
126             has credentials => (
127             is => 'ro',
128             );
129              
130             =item *
131              
132             C
133              
134             auth => 'basic'
135              
136             The authentication method to use.
137              
138             =cut
139              
140             has auth => (
141             is => 'ro',
142             );
143              
144             =item *
145              
146             C
147              
148             post_data => ['A string','across multiple','scalars']
149              
150             The POST body to use.
151              
152             =cut
153              
154             has post_data => (
155             is => 'ro',
156             default => sub { [] },
157             );
158              
159             =item *
160              
161             C
162              
163             body => '{"greeting":"Hello"}'
164              
165             The body of the request.
166              
167             =cut
168              
169             has body => (
170             is => 'ro',
171             );
172              
173             =item *
174              
175             C
176              
177             timeout => 50
178              
179             The timeout for the request
180              
181             =cut
182              
183             has timeout => (
184             is => 'ro',
185             );
186              
187             =item *
188              
189             C
190              
191             unix_socket => '/var/run/docker/docker.sock'
192              
193             The timeout for the request
194              
195             =cut
196              
197             has unix_socket => (
198             is => 'ro',
199             );
200              
201             =item *
202              
203             C
204              
205             local_address => '192.0.2.116'
206              
207             The local network address to bind to when making the request
208              
209             =cut
210              
211             has local_address => (
212             is => 'ro',
213             );
214              
215             =item *
216              
217             C
218              
219             The HTML form parameters. These get converted into
220             a body.
221              
222             =cut
223              
224             has form_args => (
225             is => 'ro',
226             default => sub { [] },
227             );
228              
229             =item *
230              
231             C
232              
233             insecure => 1
234              
235             Disable SSL certificate verification
236              
237             =cut
238              
239             has insecure => (
240             is => 'ro',
241             );
242              
243             =item *
244              
245             C
246              
247             cert => '/path/to/certificate',
248              
249             Use the certificate file for SSL
250              
251             =cut
252              
253             has cert => (
254             is => 'ro',
255             );
256              
257             =item *
258              
259             C
260              
261             capath => '/path/to/cadir/',
262              
263             Use the certificate directory for SSL
264              
265             =cut
266              
267             has capath => (
268             is => 'ro',
269             );
270              
271             =item *
272              
273             C
274              
275             Name of the output file
276              
277             =cut
278              
279             has output => (
280             is => 'ro',
281             );
282              
283             =item *
284              
285             C
286              
287             Maximum size (in bytes) of a file to download
288              
289             =cut
290              
291             has max_filesize => (
292             is => 'ro',
293             );
294              
295             =item *
296              
297             C
298              
299             show_error => 0
300              
301             Show error message on HTTP errors
302              
303             =cut
304              
305             has show_error => (
306             is => 'ro',
307             );
308              
309             =item *
310              
311             C
312              
313             fail => 1
314              
315             Let the Perl code C on error
316              
317             =back
318              
319             =cut
320              
321             has fail => (
322             is => 'ro',
323             );
324              
325 151     151   318 sub _build_quoted_body( $self, $body=$self->body ) {
  151         359  
  151         526  
  151         278  
326 151 100       509 if( defined $body ) {
327 26         371 $body =~ s!([\x00-\x1f'"\$\@\%\\])!sprintf '\\x%02x', ord $1!ge;
  88         441  
328 26         270 return sprintf qq{"%s"}, $body
329              
330             } else {
331             # Sluuuurp
332             my @post_data = map {
333 0 0       0 /^\@(.*)/ ? do {
334 0 0       0 open my $fh, '<', $1
335             or die "$1: $!";
336 0         0 local $/; # / for Filter::Simple
337 0         0 binmode $fh;
338             <$fh>
339 0         0 }
340             : $_
341 125         248 } @{ $self->post_data };
  125         620  
342 125         772 return join "", @post_data;
343             }
344             };
345              
346             =head2 C<< ->as_request >>
347              
348             $ua->request( $r->as_request );
349              
350             Returns an equivalent L object
351              
352             =cut
353              
354 219     219   530 sub _explode_headers( $self ) {
  219         602  
  219         631  
355             my @res =
356 726         1257 map { my $h = $_;
357 726         11461 my $v = $self->headers->{$h};
358 726 100       5084 ref $v ? (map { $h => $_ } @$v)
  8         46  
359             : ($h => $v)
360 219         546 } keys %{ $self->headers };
  219         1871  
361             }
362              
363             =head2 C<< $r->as_request >>
364              
365             my $r = $curl->as_request;
366              
367             Returns a L object that represents
368             the Curl options.
369              
370             =cut
371              
372 67     67 1 7264720 sub as_request( $self ) {
  67         324  
  67         232  
373 67         852 HTTP::Request->new(
374             $self->method => $self->uri,
375             [ $self->_explode_headers() ],
376             $self->body(),
377             )
378             };
379              
380 0     0   0 sub _fill_snippet( $self, $snippet ) {
  0         0  
  0         0  
  0         0  
381             # Doesn't parse parameters, yet
382 0         0 $snippet =~ s!\$self->(\w+)!$self->$1!ge;
  0         0  
383 0         0 $snippet
384             }
385              
386 74     74   247 sub _init_cookie_jar_lwp( $self ) {
  74         178  
  74         172  
387 74 100       529 if( my $fn = $self->cookie_jar ) {
388 2 100       24 my $save = $self->cookie_jar_options->{'write'} ? 1 : 0;
389             return {
390 2         25 preamble => [
391             "use Path::Tiny;",
392             "use HTTP::Cookies;",
393             ],
394             code => \"HTTP::Cookies->new(\n file => path('$fn'),\n autosave => $save,\n)",
395             postamble => [
396             #"path('$fn')->spew(\$ua->cookie_jar->dump_cookies())",
397             ],
398             };
399             }
400             }
401              
402 72     72   285 sub _init_cookie_jar_tiny( $self ) {
  72         174  
  72         203  
403 72 100       756 if( my $fn = $self->cookie_jar ) {
404 2         17 my $save = $self->cookie_jar_options->{'write'};
405             return {
406 2 100       32 preamble => [
407             "use Path::Tiny;",
408             "use HTTP::CookieJar;",
409             ],
410             code => \"HTTP::CookieJar->new->load_cookies(path('$fn')->lines),",
411             postamble => [
412             $save ?
413             ("path('$fn')->spew(\$ua->cookie_jar->dump_cookies())")
414             : (),
415             ],
416             };
417             }
418             }
419              
420 5     5   10 sub _init_cookie_jar_mojolicious( $self ) {
  5         11  
  5         10  
421 5 50       41 if( my $fn = $self->cookie_jar ) {
422 0         0 my $save = $self->cookie_jar_options->{'write'};
423             return {
424 0         0 preamble => [
425             # "use Path::Tiny;",
426             "use Mojo::UserAgent::CookieJar;",
427             ],
428             code => \"Mojo::UserAgent::CookieJar->new,",
429             postamble => [
430             #$save ?
431             # ("path('$fn')->spew(\$ua->cookie_jar->dump_cookies())")
432             # : (),
433             ],
434             };
435             }
436             }
437              
438 456     456   11173 sub _pairlist( $self, $l, $prefix = " " ) {
  456         803  
  456         1264  
  456         1289  
  456         841  
439             return join ",\n",
440             pairmap { my $v = ! ref $b ? qq{'$b'}
441             : ref $b eq 'SCALAR' ? $$b
442 6         30 : ref $b eq 'ARRAY' ? '[' . join( ", ", map {qq{'$_'}} @$b ) . ']'
443 523 50   523   1881 : ref $b eq 'HASH' ? '{' . $self->_pairlist([ map { $_ => $b->{$_} } sort keys %$b ]) . '}'
  3 100       20  
    100          
    100          
444             : die "Unknown type of $b";
445 523         6275 qq{$prefix'$a' => $v}
446 456         9312 } @$l
447             }
448              
449 74     74   355 sub _build_lwp_headers( $self, $prefix = " ", %options ) {
  74         199  
  74         281  
  74         307  
  74         222  
450             # This is so we create the standard header order in our output
451 74         280 my @h = $self->_explode_headers;
452 74         509 my $h = HTTP::Headers->new( @h );
453 74         9918 $h->remove_header( @{$options{implicit_headers}} );
  74         511  
454              
455             # also skip the Host: header if it derives from $uri
456 74         1801 my $val = $h->header('Host');
457 74 100 66     4319 if( $val and ($val eq $self->uri->host_port
      66        
458             or $val eq $self->uri->host )) {
459             # trivial host header
460 67         4030 $h->remove_header('Host');
461             };
462              
463 74         2569 $self->_pairlist([ $h->flatten ], $prefix);
464             }
465              
466 72     72   158 sub _build_tiny_headers( $self, $prefix = " ", %options ) {
  72         159  
  72         298  
  72         263  
  72         130  
467 72         427 my @h = $self->_explode_headers;
468 72         917 my $h = HTTP::Headers->new( @h );
469 72         10507 $h->remove_header( @{$options{implicit_headers}} );
  72         401  
470              
471             # HTTP::Tiny does not like overriding the Host: header :-/
472 72         1754 $h->remove_header('Host');
473              
474 72         1481 @h = $h->flatten;
475 72         9581 my %h;
476             my @order;
477 72         299 while( @h ) {
478 173         540 my ($k,$v) = splice(@h,0,2);
479 173 100       600 if( ! exists $h{ $k }) {
    50          
480             # Fresh value
481 172         410 $h{ $k } = $v;
482 172         480 push @order, $k;
483             } elsif( ! ref $h{$k}) {
484             # Second value
485 1         13 $h{ $k } = [$h{$k}, $v];
486             } else {
487             # Multiple values
488 0         0 push @{$h{ $k }}, $v;
  0         0  
489             }
490             };
491              
492 72         186 $self->_pairlist([ map { $_ => $h{ $_ } } @order ], $prefix);
  172         593  
493             }
494              
495 5     5   10 sub _build_mojolicious_headers( $self, $prefix = " ", %options ) {
  5         10  
  5         24  
  5         21  
  5         11  
496             # This is so we create the standard header order in our output
497 5         19 my @h = $self->_explode_headers;
498 5         142 my $h = HTTP::Headers->new( @h );
499 5         322 $h->remove_header( @{$options{implicit_headers}} );
  5         55  
500              
501             # also skip the Host: header if it derives from $uri
502 5         79 my $val = $h->header('Host');
503 5 0 0     346 if( $val and ($val eq $self->uri->host_port
      33        
504             or $val eq $self->uri->host )) {
505             # trivial host header
506 0         0 $h->remove_header('Host');
507             };
508              
509 5         36 @h = $h->flatten;
510 5         253 my %h;
511             my @order;
512 5         13 while( @h ) {
513 4         96 my ($k,$v) = splice(@h,0,2);
514 4 50       15 if( ! exists $h{ $k }) {
    0          
515             # Fresh value
516 4         9 $h{ $k } = $v;
517 4         12 push @order, $k;
518             } elsif( ! ref $h{$k}) {
519             # Second value
520 0         0 $h{ $k } = [$h{$k}, $v];
521             } else {
522             # Multiple values
523 0         0 push @{$h{ $k }}, $v;
  0         0  
524             }
525             };
526              
527 5         19 $self->_pairlist([ map { $_ => $h{ $_ } } @order ], $prefix);
  4         12  
528             }
529              
530 0     0   0 sub _build_rest_client_headers( $self, $prefix = " ", %options ) {
  0         0  
  0         0  
  0         0  
  0         0  
531             # This is so we create the standard header order in our output
532 0         0 my @h = $self->_explode_headers;
533 0         0 my $h = HTTP::Headers->new( @h );
534 0         0 $h->remove_header( @{$options{implicit_headers}} );
  0         0  
535              
536             # also skip the Host: header if it derives from $uri
537 0         0 my $val = $h->header('Host');
538 0 0 0     0 if( $val and ($val eq $self->uri->host_port
      0        
539             or $val eq $self->uri->host )) {
540             # trivial host header
541 0         0 $h->remove_header('Host');
542             };
543              
544 0         0 @h = $h->flatten;
545 0         0 my %h;
546             my @order;
547 0         0 while( @h ) {
548 0         0 my ($k,$v) = splice(@h,0,2);
549 0 0       0 if( ! exists $h{ $k }) {
    0          
550             # Fresh value
551 0         0 $h{ $k } = $v;
552 0         0 push @order, $k;
553             } elsif( ! ref $h{$k}) {
554             # Second value
555 0         0 $h{ $k } = [$h{$k}, $v];
556             } else {
557             # Multiple values
558 0         0 push @{$h{ $k }}, $v;
  0         0  
559             }
560             };
561              
562 0         0 $self->_pairlist([ map { $_ => $h{ $_ } } @order ], $prefix);
  0         0  
563             }
564              
565             =head2 C<< $r->as_snippet( %options ) >>
566              
567             print $r->as_snippet( type => 'LWP' );
568              
569             Returns a code snippet that returns code to create an equivalent
570             L object and to perform the request using L.
571              
572             This is mostly intended as a convenience function for creating Perl demo
573             snippets from C examples.
574              
575             =head3 Options
576              
577             =over 4
578              
579             =item B
580              
581             Arrayref of headers that will not be output.
582              
583             Convenient values are ['Content-Length']
584              
585             =item B
586              
587             type => 'Tiny',
588              
589             Type of snippet. Valid values are C for L,
590             C for L
591             and C for L.
592              
593             =back
594              
595             =cut
596              
597 151     151 1 41912138 sub as_snippet( $self, %options ) {
  151         630  
  151         1694  
  151         376  
598 151   100     1116 my $type = delete $options{ type } || 'LWP';
599 151 100       1326 if( 'LWP' eq $type ) {
    100          
    50          
600 74         698 $self->as_lwp_snippet( %options )
601             } elsif( 'Tiny' eq $type ) {
602 72         861 $self->as_http_tiny_snippet( %options )
603             } elsif( 'Mojolicious' eq $type ) {
604 5         73 $self->as_mojolicious_snippet( %options )
605             } else {
606 0         0 croak "Unknown type '$type'.";
607             }
608             }
609              
610 74     74 0 223 sub as_lwp_snippet( $self, %options ) {
  74         199  
  74         230  
  74         139  
611 74   50     1480 $options{ prefix } ||= '';
612 74   100     762 $options{ implicit_headers } ||= [];
613              
614 74         338 my @preamble;
615             my @postamble;
616 74         0 my %ssl_options;
617 74 100       363 push @preamble, @{ $options{ preamble } } if $options{ preamble };
  72         307  
618 74 50       355 push @postamble, @{ $options{ postamble } } if $options{ postamble };
  0         0  
619 74         423 my @setup_ua = ('');
620              
621 74         804 my $request_args = join ", ",
622             '$r',
623             $self->_pairlist([
624             maybe ':content_file', $self->output
625             ], '')
626             ;
627 74         1178 my $init_cookie_jar = $self->_init_cookie_jar_lwp();
628 74 100       404 if( my $p = $init_cookie_jar->{preamble}) {
629 2         9 push @preamble, @{$p}
  2         8  
630             };
631              
632 74 100       374 if( $self->insecure ) {
633 1         11 push @preamble, 'use IO::Socket::SSL;';
634 1         9 $ssl_options{ SSL_verify_mode } = \'IO::Socket::SSL::SSL_VERIFY_NONE';
635 1         5 $ssl_options{ SSL_hostname } = '';
636 1         8 $ssl_options{ verify_hostname } = '';
637             };
638              
639 74 50       1176 if( $self->cert ) {
640 0         0 push @preamble, 'use IO::Socket::SSL;';
641 0         0 $ssl_options{ SSL_ca_file } = $self->cert;
642             };
643 74 50       318 if( $self->capath ) {
644 0         0 push @preamble, 'use IO::Socket::SSL;';
645 0         0 $ssl_options{ SSL_ca_path } = $self->capath;
646             };
647             my $constructor_args = join ",",
648             $self->_pairlist([
649             send_te => 0,
650             maybe local_address => $self->local_address,
651             maybe max_size => $self->max_filesize,
652             maybe timeout => $self->timeout,
653             maybe cookie_jar => $init_cookie_jar->{code},
654 74 100       1331 maybe SSL_opts => keys %ssl_options ? \%ssl_options : undef,
655             ], '')
656             ;
657 74 50       832 if( defined( my $credentials = $self->credentials )) {
658 0         0 my( $user, $pass ) = split /:/, $credentials, 2;
659 0         0 my $setup_credentials = sprintf qq{\$ua->credentials("%s","%s");},
660             quotemeta $user,
661             quotemeta $pass;
662 0         0 push @setup_ua, $setup_credentials;
663             };
664 74 100       495 if( $self->show_error ) {
    50          
665 8         66 push @postamble,
666             ' die $res->message if $res->is_error;',
667             } elsif( $self->fail ) {
668 0         0 push @postamble,
669             ' exit 1 if !$res->{success};',
670             };
671              
672 74 50       311 @setup_ua = ()
673             if @setup_ua == 1;
674              
675 74         150 my $request_constructor;
676              
677 74 100 100     549 if( $self->method ne 'GET' and @{ $self->form_args }) {
  19         140  
678 2         3 push @preamble, 'use HTTP::Request::Common;';
679 2         4 push @{$options{ implicit_headers }}, 'Content-Type';
  2         4  
680 2         4 $request_constructor = <
681 2         11 my \$r = HTTP::Request::Common::@{[$self->method]}(
682 2         21 '@{[$self->uri]}',
683             Content_Type => 'form-data',
684             Content => [
685 2         17 @{[$self->_pairlist($self->form_args, ' ')]}
686             ],
687 2         12 @{[$self->_build_lwp_headers(' ', %options)]}
688             );
689             SNIPPET
690             } else {
691 72         322 $request_constructor = <
692             my \$r = HTTP::Request->new(
693 72         552 '@{[$self->method]}' => '@{[$self->uri]}',
  72         1156  
694             [
695 72         870 @{[$self->_build_lwp_headers(' ', %options)]}
696             ],
697 72         433 @{[$self->_build_quoted_body()]}
698             );
699             SNIPPET
700             }
701              
702 74         779 @preamble = map { "$options{prefix} $_\n" } @preamble;
  151         572  
703 74         211 @postamble = map { "$options{prefix} $_\n" } @postamble;
  8         55  
704 74         195 @setup_ua = map { "$options{prefix} $_\n" } @setup_ua;
  0         0  
705              
706 74         1147 return <
707             @preamble
708             my \$ua = LWP::UserAgent->new($constructor_args);@setup_ua
709             $request_constructor
710             my \$res = \$ua->request( $request_args );
711             @postamble
712             SNIPPET
713             };
714              
715 72     72 0 232 sub as_http_tiny_snippet( $self, %options ) {
  72         242  
  72         220  
  72         293  
716 72   50     1422 $options{ prefix } ||= '';
717 72   50     723 $options{ implicit_headers } ||= [];
718              
719 72         168 push @{ $options{ implicit_headers }}, 'Host'; # HTTP::Tiny dislikes that header
  72         437  
720              
721 72         442 my @preamble;
722             my @postamble;
723 72         0 my %ssl_options;
724 72 50       389 push @preamble, @{ $options{ preamble } } if $options{ preamble };
  72         331  
725 72 50       348 push @postamble, @{ $options{ postamble } } if $options{ postamble };
  0         0  
726 72         301 my @setup_ua = ('');
727              
728 72         1229 my $request_args = join ", ",
729             '$r',
730             $self->_pairlist([
731             maybe ':content_file', $self->output
732             ], '')
733             ;
734 72         2100 my $init_cookie_jar = $self->_init_cookie_jar_tiny();
735 72 100       622 if( my $p = $init_cookie_jar->{preamble}) {
736 2         8 push @preamble, @{$p}
  2         6  
737             };
738              
739 72         268 my @ssl;
740 72 100       506 if( $self->insecure ) {
741             } else {
742 71         454 push @ssl, verify_SSL => 1;
743             };
744 72 50       365 if( $self->cert ) {
745 0         0 push @preamble, 'use IO::Socket::SSL;';
746 0         0 $ssl_options{ SSL_cert_file } = $self->cert;
747             };
748 72 100       524 if( $self->show_error ) {
    50          
749 8         56 push @postamble,
750             ' die $res->{reason} if !$res->{success};',
751             } elsif( $self->fail ) {
752 0         0 push @postamble,
753             ' exit 1 if !$res->{success};',
754             };
755             my $constructor_args = join ",",
756             $self->_pairlist([
757             @ssl,
758             maybe timeout => $self->timeout,
759             maybe local_address => $self->local_address,
760             maybe max_size => $self->max_filesize,
761             maybe cookie_jar => $init_cookie_jar->{code},
762 72 50       1362 maybe SSL_options => keys %ssl_options ? \%ssl_options : undef,
763             ], '')
764             ;
765 72 50       926 if( defined( my $credentials = $self->credentials )) {
766 0         0 my( $user, $pass ) = split /:/, $credentials, 2;
767 0         0 my $setup_credentials = sprintf qq{\$ua->credentials("%s","%s");},
768             quotemeta $user,
769             quotemeta $pass;
770 0         0 push @setup_ua, $setup_credentials;
771             };
772              
773 72 50       360 @setup_ua = ()
774             if @setup_ua == 1;
775              
776 72         210 @preamble = map { "$options{prefix} $_\n" } @preamble;
  148         723  
777 72         288 @postamble = map { "$options{prefix} $_\n" } @postamble;
  8         89  
778 72         192 @setup_ua = map { "$options{prefix} $_\n" } @setup_ua;
  0         0  
779              
780 72         380 my @content = $self->_build_quoted_body();
781 72 100       191 if( grep {/\S/} @content ) {
  72 100       481  
782 11         72 unshift @content, 'content => ',
783 61         468 } elsif( @{ $self->form_args }) {
784 2         18 my $req = HTTP::Request::Common::POST(
785             'https://example.com',
786             Content_Type => 'form-data',
787             Content => $self->form_args,
788             );
789 2         3339 @content = ('content => ', $self->_build_quoted_body( $req->content ));
790 2         12 $self->headers->{ 'Content-Type' } = join "; ", $req->headers->content_type;
791             }
792              
793 72         666 return <
794             @preamble
795             my \$ua = HTTP::Tiny->new($constructor_args);@setup_ua
796             my \$res = \$ua->request(
797 72         654 '@{[$self->method]}' => '@{[$self->uri]}',
  72         763  
798             {
799             headers => {
800 72         923 @{[$self->_build_tiny_headers(' ', %options)]}
801             },
802             @content
803             },
804             );
805             @postamble
806             SNIPPET
807             };
808              
809 5     5 0 13 sub as_mojolicious_snippet( $self, %options ) {
  5         25  
  5         15  
  5         17  
810 5   50     74 $options{ prefix } ||= '';
811 5   50     70 $options{ implicit_headers } ||= [];
812              
813 5         32 my @preamble;
814             my @postamble;
815 5         0 my %ssl_options;
816 5 50       23 push @preamble, @{ $options{ preamble } } if $options{ preamble };
  5         19  
817 5 50       25 push @postamble, @{ $options{ postamble } } if $options{ postamble };
  0         0  
818 5         29 my @setup_ua = ('');
819              
820 5         77 my $request_args = join ", ",
821             '$r',
822             $self->_pairlist([
823             maybe ':content_file', $self->output
824             ], '')
825             ;
826 5         84 my $init_cookie_jar = $self->_init_cookie_jar_mojolicious();
827 5 50       24 if( my $p = $init_cookie_jar->{preamble}) {
828 0         0 push @preamble, @{$p}
  0         0  
829             };
830              
831 5         9 my @ssl;
832 5 50       26 if( $self->insecure ) {
833 0         0 push @ssl, insecure => 1,
834             };
835 5 50       27 if( $self->cert ) {
836 0         0 push @ssl, cert => $self->cert,
837             };
838 5 50       50 if( $self->show_error ) {
    50          
839 0         0 push @postamble,
840             ' die $res->message if $res->is_error;',
841             } elsif( $self->fail ) {
842 0         0 push @postamble,
843             ' exit 1 if !$res->is_error;',
844             };
845 5         10 my $socket_options = {};
846 5 50       41 if( my $host = $self->local_address ) {
847 0         0 $socket_options->{ LocalAddr } = $host;
848             }
849             my $constructor_args = join ",",
850             $self->_pairlist([
851             @ssl,
852             keys %$socket_options ? ( socket_options => $socket_options ) : (),
853             maybe request_timeout => $self->timeout,
854             maybe max_response_size => $self->max_filesize,
855             maybe cookie_jar => $init_cookie_jar->{code},
856 5 50       138 maybe SSL_options => keys %ssl_options ? \%ssl_options : undef,
    50          
857             ], '')
858             ;
859 5 50       56 if( defined( my $credentials = $self->credentials )) {
860 0         0 my( $user, $pass ) = split /:/, $credentials, 2;
861 0         0 my $setup_credentials = sprintf qq{\$ua->userinfo("%s","%s");},
862             quotemeta $user,
863             quotemeta $pass;
864 0         0 push @setup_ua, $setup_credentials;
865             };
866              
867 5 50       22 @setup_ua = ()
868             if @setup_ua == 1;
869              
870 5         15 @preamble = map { "$options{prefix} $_\n" } @preamble;
  10         56  
871 5         32 @postamble = map { "$options{prefix} $_\n" } @postamble;
  0         0  
872 5         12 @setup_ua = map { "$options{prefix} $_\n" } @setup_ua;
  0         0  
873              
874 5         23 my $content = $self->_build_quoted_body();
875 5 50       23 if( @{ $self->form_args }) {
  5         34  
876 0         0 my $req = HTTP::Request::Common::POST(
877             'https://example.com',
878             Content_Type => 'form-data',
879             Content => $self->form_args,
880             );
881 0   0     0 $content ||= $self->_build_quoted_body( $req->content );
882 0         0 $self->headers->{ 'Content-Type' } = join "; ", $req->headers->content_type;
883             }
884              
885 5         29 return <
886             @preamble
887             my \$ua = Mojo::UserAgent->new($constructor_args);@setup_ua
888             my \$tx = \$ua->build_tx(
889 5         38 '@{[$self->method]}' => '@{[$self->uri]}',
  5         51  
890             {
891 5         53 @{[$self->_build_mojolicious_headers(' ', %options)]}
892             },
893             $content
894             );
895             my \$res = \$ua->start(\$tx)->result;
896             @postamble
897             SNIPPET
898             };
899              
900 0     0 0 0 sub as_rest_client_snippet( $self, %options ) {
  0         0  
  0         0  
  0         0  
901 0   0     0 $options{ prefix } ||= '';
902 0   0     0 $options{ implicit_headers } ||= [];
903              
904 0         0 my @preamble;
905             my @postamble;
906 0         0 my %ssl_options;
907 0 0       0 push @preamble, @{ $options{ preamble } } if $options{ preamble };
  0         0  
908 0 0       0 push @postamble, @{ $options{ postamble } } if $options{ postamble };
  0         0  
909 0         0 my @setup_ua = ('');
910              
911 0         0 my $request_args = join ", ",
912             '$r',
913             $self->_pairlist([
914             maybe ':content_file', $self->output
915             ], '')
916             ;
917 0         0 my $init_cookie_jar = $self->_init_cookie_jar_tiny();
918 0 0       0 if( my $p = $init_cookie_jar->{preamble}) {
919 0         0 push @preamble, @{$p}
  0         0  
920             };
921              
922 0 0       0 if( $self->show_error ) {
    0          
923 0         0 push @postamble,
924             ' die $res->{reason} if !$res->{success};',
925             } elsif( $self->fail ) {
926 0         0 push @postamble,
927             ' exit 1 if !$res->{success};',
928             };
929 0         0 my $constructor_args = join ",",
930             $self->_pairlist([
931             maybe timeout => $self->timeout,
932             maybe host => $self->host,
933             maybe cert => $self->cert,
934             maybe timeout => $self->timeout,
935             ], '')
936             ;
937 0 0       0 if( defined( my $credentials = $self->credentials )) {
938 0         0 my( $user, $pass ) = split /:/, $credentials, 2;
939 0         0 my $setup_credentials = sprintf qq{\$ua->credentials("%s","%s");},
940             quotemeta $user,
941             quotemeta $pass;
942 0         0 push @setup_ua, $setup_credentials;
943             };
944              
945 0 0       0 @setup_ua = ()
946             if @setup_ua == 1;
947              
948 0         0 @preamble = map { "$options{prefix} $_\n" } @preamble;
  0         0  
949 0         0 @postamble = map { "$options{prefix} $_\n" } @postamble;
  0         0  
950 0         0 @setup_ua = map { "$options{prefix} $_\n" } @setup_ua;
  0         0  
951              
952 0         0 my @content = $self->_build_quoted_body();
953 0 0       0 if( grep {/\S/} @content ) {
  0 0       0  
954 0         0 unshift @content, 'content => ',
955 0         0 } elsif( @{ $self->form_args }) {
956 0         0 my $req = HTTP::Request::Common::POST(
957             'https://example.com',
958             Content_Type => 'form-data',
959             Content => $self->form_args,
960             );
961 0         0 @content = ('content => ', $self->_build_quoted_body( $req->content ));
962 0         0 $self->headers->{ 'Content-Type' } = join "; ", $req->headers->content_type;
963             }
964              
965 0         0 return <
966             @preamble
967             my \$ua = REST::Client->new($constructor_args);@setup_ua
968             my \$res = \$ua->request(
969 0         0 '@{[$self->method]}' => '@{[$self->uri]}',
  0         0  
970             {
971             headers => {
972 0         0 @{[$self->_build_rest_client_headers(' ', %options)]}
973             },
974             @content
975             },
976             );
977             @postamble
978             SNIPPET
979             };
980              
981             =head2 C<< $r->as_curl >>
982              
983             print $r->as_curl;
984              
985             Returns a curl command line representing the request
986              
987             This is convenient if you started out from something else or want a canonical
988             representation of a curl command line.
989              
990             =over 4
991              
992             =item B
993              
994             The curl command to be used. Default is C.
995              
996             =back
997              
998             =cut
999              
1000             # These are what curl uses as defaults, not what Perl should use as default!
1001             our %curl_header_defaults = (
1002             'Accept' => '*/*',
1003             #'Accept-Encoding' => 'deflate, gzip',
1004             # For Perl, use HTTP::Message::decodable() instead of the above list
1005             );
1006              
1007 59     59 1 1308 sub as_curl($self,%options) {
  59         262  
  59         444  
  59         104  
1008             $options{ curl } = 'curl'
1009 59 50       295 if ! exists $options{ curl };
1010             $options{ long_options } = 1
1011 59 50       383 if ! exists $options{ long_options };
1012              
1013 59         226 my @request_commands;
1014              
1015 59 100       637 if( $self->method eq 'HEAD' ) {
    100          
1016             push @request_commands,
1017 1 50       15 $options{ long_options } ? '--head' : '-I';
1018              
1019             } elsif( $self->method ne 'GET' ) {
1020             push @request_commands,
1021 16 50       146 $options{ long_options } ? '--request' : '-X',
1022             $self->method;
1023             };
1024              
1025 59 50       119 if( scalar keys %{ $self->headers }) {
  59         313  
1026 59         116 for my $h (sort keys %{$self->headers}) {
  59         626  
1027 209         2539 my $v = $self->headers->{$h};
1028              
1029 209         335 my $default;
1030 209 100       561 if( exists $curl_header_defaults{ $h }) {
1031 58         303 $default = $curl_header_defaults{ $h };
1032             };
1033              
1034 209 100       495 if( ! ref $v ) {
1035 208         444 $v = [$v];
1036             };
1037 209         462 for my $val (@$v) {
1038 210 100 100     1216 if( !defined $default or $val ne $default ) {
1039             # also skip the Host: header if it derives from $uri
1040 153 100 66     1572 if( $h eq 'Host' and ($val eq $self->uri->host_port
    100 100        
    100 66        
      66        
1041             or $val eq $self->uri->host )) {
1042             # trivial host header
1043              
1044             # also skip the Content-Length header if it derives from the body
1045             } elsif( $h eq 'Content-Length' and
1046             (
1047             ($self->post_data and $val == length $self->post_data)
1048             or ($self->body and $val == length $self->body)
1049             )) {
1050             # trivial content-length header
1051              
1052             } elsif( $h eq 'User-Agent' ) {
1053             push @request_commands,
1054 59 50       564 $options{ long_options } ? '--user-agent' : '-A',
1055             $val;
1056             } else {
1057             push @request_commands,
1058 26 50       493 $options{ long_options } ? '--header' : '-h',
1059             "$h: $val";
1060             };
1061             };
1062             };
1063             };
1064             };
1065              
1066 59 100       147 if( @{$self->form_args} ) {
  59 100       516  
1067 2         6 my $form_args = $self->form_args;
1068 2         4 for (0..(@{$form_args}/2-1)) {
  2         10  
1069 4         13 my( $name, $val) = ($form_args->[$_*2], $form_args->[$_*2+1]);
1070 4         19 push @request_commands,
1071             '--form-string',
1072             "$name=$val";
1073             }
1074              
1075             } elsif( defined( my $body = $self->body )) {
1076             # Can we collapse stuff into --json or other nicer representations
1077             # here ?!
1078 11         45 push @request_commands,
1079             '--data-raw',
1080             $body;
1081             };
1082              
1083 59         205 push @request_commands, $self->uri;
1084              
1085             return
1086             #(defined $options{ curl } ? $options{curl} : () ),
1087 59         365 @request_commands;
1088             }
1089              
1090             =head2 C<< $r->as_wget >>
1091              
1092             print $r->as_wget;
1093              
1094             Returns a curl command line representing the request
1095              
1096             This is convenient if you started out from something else or want a canonical
1097             representation of a curl command line.
1098              
1099             =over 4
1100              
1101             =item B
1102              
1103             The curl command to be used. Default is C.
1104              
1105             =back
1106              
1107             =cut
1108              
1109             # These are what wget uses as defaults, not what Perl should use as default!
1110             our %wget_header_defaults = (
1111             'Accept' => '*/*',
1112             'Accept-Encoding' => 'identity',
1113             'User-Agent' => 'Wget/1.21',
1114             'Connection' => 'Keep-Alive',
1115             );
1116              
1117 0     0 1   sub as_wget($self,%options) {
  0            
  0            
  0            
1118             $options{ wget } = 'wget'
1119 0 0         if ! exists $options{ wget };
1120             $options{ long_options } = 1
1121 0 0         if ! exists $options{ long_options };
1122              
1123 0           my @request_commands;
1124              
1125 0 0         if( $self->method ne 'GET' ) {
1126 0 0 0       if( $self->method eq 'POST' and $self->body ) {
1127             # This is implied by '--post-data', below
1128             } else {
1129 0           push @request_commands,
1130             '--method' => $self->method;
1131             };
1132             };
1133              
1134 0 0         if( scalar keys %{ $self->headers }) {
  0            
1135 0           my %h = %{ $self->headers };
  0            
1136              
1137             # "--no-cache" implies two headers, Cache-Control and Pragma
1138             my $is_cache = exists $h{ 'Pragma' }
1139             && exists $h{ 'Cache-Control' }
1140             && $h{ 'Cache-Control' } =~ /^no-cache\b/
1141 0   0       && $h{ 'Pragma' } eq 'no-cache'
1142             ;
1143 0 0         if( $is_cache ) {
1144 0           delete $h{ 'Pragma' };
1145 0           delete $h{ 'Cache-Control' };
1146 0           push @request_commands, '--no-cache';
1147             };
1148              
1149 0           for my $name (sort keys %h) {
1150 0           my $v = $h{ $name };
1151              
1152 0           my $default;
1153 0 0         if( exists $wget_header_defaults{ $name }) {
1154 0           $default = $wget_header_defaults{ $name };
1155             };
1156              
1157 0 0         if( ! ref $v ) {
1158 0           $v = [$v];
1159             };
1160 0           for my $val (@$v) {
1161 0 0 0       if( !defined $default or $val ne $default ) {
1162             # also skip the Host: header if it derives from $uri
1163 0 0 0       if( $name eq 'Host' and ($val eq $self->uri->host_port
    0 0        
1164             or $val eq $self->uri->host )) {
1165             # trivial host header, ignore
1166             } elsif( $name eq 'User-Agent' ) {
1167 0           push @request_commands,
1168             '--user-agent',
1169             $val;
1170             } else {
1171 0           push @request_commands,
1172             '--header',
1173             "$name: $val";
1174             };
1175             };
1176             };
1177             };
1178             };
1179              
1180 0 0         if( my $body = $self->body ) {
1181 0 0         if( $self->method eq 'POST' ) {
1182 0           push @request_commands,
1183             '--post-data',
1184             $body;
1185             } else {
1186 0           push @request_commands,
1187             '--body-data',
1188             $body;
1189             };
1190             };
1191              
1192 0           push @request_commands, $self->uri;
1193              
1194             return
1195             #(defined $options{ curl } ? $options{curl} : () ),
1196 0           @request_commands;
1197             }
1198              
1199              
1200             =head2 C<< $r->clone >>
1201              
1202             Returns a shallow copy of the object
1203              
1204             =cut
1205              
1206 0     0 1   sub clone( $self, %options ) {
  0            
  0            
  0            
1207 0           (ref $self)->new( %$self, %options )
1208             }
1209              
1210             1;
1211              
1212             =head1 REPOSITORY
1213              
1214             The public repository of this module is
1215             L.
1216              
1217             =head1 SUPPORT
1218              
1219             The public support forum of this module is
1220             L.
1221              
1222             =head1 BUG TRACKER
1223              
1224             Please report bugs in this module via the Github bug queue at
1225             L
1226              
1227             =head1 AUTHOR
1228              
1229             Max Maischein C
1230              
1231             =head1 COPYRIGHT (c)
1232              
1233             Copyright 2018-2026 by Max Maischein C.
1234              
1235             =head1 LICENSE
1236              
1237             This module is released under the same terms as Perl itself.
1238              
1239             =cut