File Coverage

lib/HTTP/Promise/Request.pm
Criterion Covered Total %
statement 113 204 55.3
branch 18 88 20.4
condition 21 87 24.1
subroutine 26 32 81.2
pod 17 17 100.0
total 195 428 45.5


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Request.pm
3             ## Version v0.2.0
4             ## Copyright(c) 2023 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2022/03/21
7             ## Modified 2023/09/08
8             ## All rights reserved.
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTTP::Promise::Request;
15             BEGIN
16             {
17 7     7   263082 use strict;
  7         26  
  7         320  
18 7     7   47 use warnings;
  7         22  
  7         339  
19 7     7   57 use warnings::register;
  7         20  
  7         1672  
20 7     7   68 use parent qw( HTTP::Promise::Message );
  7         19  
  7         121  
21 7         909 use vars qw( $VERSION $EXCEPTION_CLASS $KNOWN_METHODS $KNOWN_METHODS_I $TIMEOUT
22 7     7   734 $DEFAULT_MIME_TYPE $DEFAULT_METHOD $DEFAULT_PROTOCOL $SCHEME_RE $INTL_URI_RE );
  7         14  
23 7     7   4858 use Cookie::Jar;
  7         4456623  
  7         130  
24 7     7   5778 use Crypt::Misc 0.076;
  7         70944  
  7         422  
25             # use HTTP::Parser2::XS 0.01 ();
26 7     7   76 use HTTP::Promise::Exception;
  7         20  
  7         126  
27 7     7   4983 use HTTP::Promise::Parser;
  7         28  
  7         106  
28 7     7   7889 use HTTP::Promise::Stream;
  7         36  
  7         83  
29             # use Nice::Try v1.2.0;
30 7     7   2068 use Regexp::Common qw( URI net );
  7         18  
  7         96  
31             # URI::Fast is great, but only supports simple protocols
32             # use URI::Fast 0.55;
33 7     7   1954 use URI 5.10;
  7         10021  
  7         1800  
34 7     7   43 our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
35             # rc7231, section 4.1
36             # Ref: <https://datatracker.ietf.org/doc/html/rfc7231#section-4.1>
37             # + PATCH
38             # HTTP "method token is case-sensitive"
39             # rfc7231, section 4.1 <https://tools.ietf.org/html/rfc7231#section-4.1>
40             # our $KNOWN_METHODS = qr/^(?:CONNECT|DELETE|GET|HEAD|OPTIONS|PATCH|POST|PUT|TRACE)$/;
41 7         20 local $" = '|';
42 7         33 my @methods = qw( CONNECT DELETE GET HEAD OPTIONS PATCH POST PUT TRACE );
43 7         474 our $KNOWN_METHODS = qr/(?:@methods)/;
44 7         499 our $KNOWN_METHODS_I = qr/(?:@methods)/i;
45 7         33 our $TIMEOUT = 10;
46 7         15 our $DEFAULT_MIME_TYPE = 'application/octet-stream';
47 7         15 our $DEFAULT_METHOD = 'GET';
48 7         30 our $DEFAULT_PROTOCOL = 'HTTP/1.1';
49             # Borrowed from URI::_server
50 7         29 our $GROSS_URI_RE = qr{(?<scheme>(?:https?:)?)//(?<host>[^/?\#]*)(?<rest>.*)};
51             # [\x00-\x7f]
52 7         20 our $INTL_URI_RE = qr{(?<scheme>(?:https?:)?)//(?<host>[^\x00-\x7f]+\.[^/?\#]*)(?<rest>.*)};
53 7         238 our $VERSION = 'v0.2.0';
54             };
55              
56 7     7   65 use strict;
  7         13  
  7         161  
57 7     7   50 use warnings;
  7         18  
  7         28775  
58              
59             # $req->new( $method, $uri, $headers, $content, k1 => v1, k2 => v2);
60             # $req->new( $method, $uri, $headers, $content, { k1 => v1, k2 => v2 });
61             # $req->new( $method, $uri, $headers, k1 => v1, k2 => v2);
62             # $req->new( $method, $uri, $headers, { k1 => v1, k2 => v2 });
63             # $req->new( $method, $uri, k1 => v1, k2 => v2);
64             # $req->new( $method, k1 => v1, k2 => v2);
65             # $req->new( k1 => v1, k2 => v2 );
66             sub init
67             {
68 37     37 1 60841 my $self = shift( @_ );
69 37         118 my( $method, $uri );
70 37         317 my $iKNOWN_METHODS = qr/$KNOWN_METHODS/i;
71 37 100       150 if( @_ )
72             {
73 32 50 33     1543 if( @_ == 1 && ref( $_[0] ) eq 'HASH' )
    100 33        
      100        
      66        
      66        
      66        
74             {
75 0         0 my $opts = $_[0];
76 0         0 ( $method, $uri ) = CORE::delete( @$opts{qw( method uri )} );
77             }
78             elsif( @_ >= 2 &&
79             defined( $_[0] ) &&
80             # rfc7230 says methods are case sensitive, so unless this is an unknown method we care about the case
81             ( $_[0] =~ /^(?:$KNOWN_METHODS)$/ || ( $_[0] =~ /^[A-Za-z]{3,12}$/ && $_[0] !~ /^(?:$KNOWN_METHODS_I)$/i ) ) &&
82             (
83             ( defined( $_[1] ) && $_[1] =~ m,^(?:$RE{URI}{HTTP}|$RE{URI}{HTTP}{-scheme => 'https'}|(?:https?\:\/{2}\[?(?:$RE{net}{IPv4}|$RE{net}{IPv6}|\:{2}1)\]?(?:\:\d+)?)|$INTL_URI_RE|/), ) ||
84             !defined( $_[1] )
85             ) )
86             {
87 22         19581 ( $method, $uri ) = splice( @_, 0, 2 );
88             }
89             else
90             {
91 10         98 return( $self->error( "Invalid parameters received. I was expecting either an hash reference or at least a method and a valid uri, but instead got: '", join( "', '", @_ ), "'" ) );
92             }
93             }
94 27         1035 $self->{default_protocol} = $DEFAULT_PROTOCOL;
95             # properties headers and content are set by our parent class HTTP::Promise::Message
96 27         87 $self->{method} = $method;
97 27         74 $self->{timeout} = $TIMEOUT;
98 27         71 $self->{uri} = $uri;
99             # Should as_string return an absolute uri or just the absolute path?
100 27         73 $self->{uri_absolute} = 0;
101 27         71 $self->{_init_strict_use_sub} = 1;
102 27         106 $self->{_init_params_order} = [qw( content headers )];
103 27         104 $self->{_exception_class} = $EXCEPTION_CLASS;
104             # $self->SUPER::init( ( defined( $headers ) ? $headers : () ), @_ ) || return( $self->pass_error );
105 27 50       212 $self->SUPER::init( @_ ) || return( $self->pass_error );
106 27         117 return( $self );
107             }
108              
109             sub accept_decodable
110             {
111 1     1 1 14 my $self = shift( @_ );
112 1         8 return( $self->header( 'Accept-Encoding', $self->decodable->join( ', ' )->scalar ) );
113             }
114              
115             sub clone
116             {
117 1     1 1 5 my $self = shift( @_ );
118 1         27 my $new = $self->SUPER::clone;
119 1         12 $new->method( $self->method );
120 1         864 $new->uri( $self->uri );
121 1         1112 return( $new );
122             }
123              
124             # NOTE: method content is inherited from HTTP::Promise::Message
125              
126 0     0 1 0 sub cookie_jar { return( shift->_set_get_object( 'cookie_jar', 'Cookie::Jar', @_ ) ); }
127              
128 3     3 1 9 sub default_protocol { return( shift->_set_get_scalar_as_object( 'default_protocol', @_ ) ); }
129              
130             sub dump
131             {
132 3     3 1 1778 my $self = shift( @_ );
133 3         27 my $start_line = $self->start_line;
134 3         58 return( $self->SUPER::dump( preheader => $start_line ) );
135             }
136              
137 25     25 1 2465 sub headers { return( shift->_set_get_object_without_init( 'headers', 'HTTP::Promise::Headers', @_ ) ); }
138              
139             sub make_form_data
140             {
141 0     0 1 0 my $self = shift( @_ );
142 0         0 my $this = shift( @_ );
143 0 0       0 $self->_load_class( 'HTTP::Promise::Entity' ) || return( $self->pass_error );
144 0 0       0 $self->_load_class( 'HTTP::Promise::Headers' ) || return( $self->pass_error );
145 0         0 my $ent = HTTP::Promise::Entity->new( debug => $self->debug );
146 0         0 my( $headers, $ct );
147 0 0       0 if( $headers = $self->headers )
148             {
149 0         0 $ct = $headers->new_field( 'Content-Type' => $headers->content_type );
150 0 0       0 $ct->boundary( $ent->make_boundary ) unless( $ct->boundary );
151             }
152             else
153             {
154 0         0 $headers = HTTP::Promise::Headers->new;
155 0         0 $ct = $headers->new_field( 'Content-Type' );
156 0         0 my $boundary = $ent->make_boundary;
157 0         0 $ct->boundary( $boundary );
158             }
159 0         0 $ent->headers( $headers );
160 0         0 $ct->type( 'multipart/form-data' );
161 0         0 $headers->content_type( "$ct" );
162 0         0 $self->entity( $ent );
163            
164 0 0       0 if( $self->_is_a( $this => 'HTTP::Promise::Body::Form' ) )
    0          
165             {
166 0   0     0 my $form_data = $this->as_form_data ||
167             return( $self->pass_error( $this->error ) );
168 0   0     0 my $parts = $form_data->make_parts ||
169             return( $self->pass_error( $form_data->error ) );
170 0         0 $ent->parts( $parts );
171 0         0 return( $ent );
172             }
173             elsif( $self->_is_a( $this => 'HTTP::Promise::Body::Form::Data' ) )
174             {
175 0   0     0 my $parts = $this->make_parts ||
176             return( $self->pass_error( $this->error ) );
177 0         0 $ent->parts( $parts );
178 0         0 return( $ent );
179             }
180            
181 0 0       0 my $args = $self->_is_array( $this ) ? $this : [%$this];
182 0         0 for( my $i = 0; $i < scalar( @$args ); $i += 2 )
183             {
184 0         0 my $k = $args->[$i];
185 0         0 my $v = $args->[$i+1];
186             # Content-Type: image/gif
187             # Content-Transfer-Encoding: base64
188             # Content-Disposition: inline; filename="3d-vise.gif"
189            
190             # Content-Disposition: form-data; name="file_upload"; filename="&#22825;&#29399;.png"
191             # Content-Type: image/png
192            
193 0         0 my $def;
194             # This will be true also for HTTP::Promise::Body::File, because it inherits from Module::Generic::File
195 0 0       0 if( $self->is_a( $v => 'Module::Generic::File' ) )
    0          
196             {
197 0   0     0 $def =
198             {
199             filename => $v->basename,
200             type => ( $v->finfo->mime_type || $DEFAULT_MIME_TYPE ),
201             };
202             }
203             # An hash referenece can be passed as the value to provide granularity.
204             # It can be used for regular scalar or file upload
205             elsif( ref( $v ) eq 'HASH' )
206             {
207 0         0 $def = $v;
208 0         0 my $f;
209 0 0       0 if( $def->{file} )
210             {
211             $f = $self->_is_a( $def->{file} => 'Module::Generic::File' )
212             ? $def->{file}
213 0 0       0 : $self->new_file( $def->{file} );
214 0 0       0 return( $self->pass_error ) if( !defined( $f ) );
215 0         0 $def->{file} = $f;
216             }
217            
218 0 0 0     0 if( $f &&
      0        
      0        
219             !$def->{filename} &&
220             ( !$def->{headers} ||
221             ( ref( $def->{headers} ) ne 'ARRAY' &&
222             !$self->_is_a( $def->{headers} => 'HTTP::Promise::Headers' )
223             ) ||
224             (
225             $self->_is_a( $def->{headers} => 'HTTP::Promise::Headers' ) &&
226             $def->{headers}->content_disposition->index( 'filename' ) == -1
227             )
228             ) )
229             {
230 0         0 $def->{filename} = $f->basename;
231             }
232 0 0 0     0 if( $f &&
      0        
      0        
233             !$def->{type} &&
234             ( !$def->{headers} ||
235             ( ref( $def->{headers} ) ne 'ARRAY' &&
236             !$self->_is_a( $def->{headers} => 'HTTP::Promise::Headers' )
237             ) ||
238             (
239             $self->_is_a( $def->{headers} => 'HTTP::Promise::Headers' ) &&
240             !$def->{headers}->content_type
241             )
242             ) )
243             {
244 0         0 $def->{type} = $f->finfo->mime_type;
245             }
246             }
247            
248 0         0 my $part = HTTP::Promise::Entity->new;
249 0         0 my $disp = $headers->new_field( 'Content-Disposition' => 'form-data' );
250 0         0 $disp->name( "$k" );
251 0         0 my( $ph, $body );
252 0 0 0     0 if( defined( $def ) && ref( $def ) eq 'HASH' )
253             {
254 0 0 0     0 if( exists( $def->{headers} ) && ref( $def->{headers} ) eq 'ARRAY' )
    0 0        
255             {
256 0   0     0 $ph = HTTP::Promise::Headers->new( @{$def->{headers}} ) ||
257             return( $self->pass_error( HTTP::Promise::Headers->error ) );
258             }
259             elsif( exists( $def->{headers} ) && $self->_is_a( $def->{headers} => 'HTTP::Promise::Headers' ) )
260             {
261 0         0 $ph = $def->{headers};
262             }
263 0 0       0 $disp->filename( $def->{filename} ) if( $def->{filename} );
264 0 0 0     0 $ph->content_type( "$def->{type}" ) if( $def->{type} && !$ph->exists( 'Content-Type' ) );
265            
266             # If the user asks to encode the file and the file is not zero-byte big
267 0 0 0     0 if( $def->{file} && $def->{encoding} && $def->{file}->length )
      0        
268             {
269 0 0       0 my $encodings = $self->_is_array( $def->{encoding} ) ? $def->{encoding} : [$def->{encoding}];
270 0 0       0 $self->_load_class( 'HTTP::Promise::Stream' ) ||
271             return( $self->pass_error );
272 0         0 my $source = $def->{file};
273 0         0 foreach my $enc ( @$encodings )
274             {
275 0   0     0 my $s = HTTP::Promise::Stream->new( $source, encoding => $def->{encoding} ) ||
276             return( $self->pass_error( HTTP::Promise::Stream->error ) );
277 0         0 my $file = $self->new_file;
278 0         0 my $bytes = $s->read( $file );
279 0 0       0 return( $self->pass_error( $s->error ) ) if( !defined( $bytes ) );
280 0 0       0 return( $self->error( "No encoded byte could be writen to file '$file' for encoding '$enc' with source file '$source'." ) ) if( !$bytes );
281 0         0 $source = $file;
282             }
283 0 0       0 $ph->content_transfer_encoding( join( ' ', @$encodings ) ) if( scalar( @$encodings ) );
284 0         0 $part->is_encoded(1);
285             }
286            
287 0 0       0 if( $def->{file} )
    0          
288             {
289 0         0 $body = $part->new_body( file => $def->{file} );
290             }
291             elsif( $def->{value} )
292             {
293 0         0 $body = $part->new_body( string => $def->{value} );
294             }
295             }
296             else
297             {
298 0   0     0 $ph = HTTP::Promise::Headers->new ||
299             return( $self->pass_error( HTTP::Promise::Headers->error ) );
300 0         0 $body = $part->new_body( string => $v );
301             }
302            
303 0         0 $ph->content_disposition( "${disp}" );
304 0 0       0 $part->headers( $ph ) if( defined( $ph ) );
305 0 0       0 $part->body( $body ) if( defined( $body ) );
306 0         0 $ent->parts->push( $part );
307             }
308 0         0 return( $self );
309             }
310              
311 29     29 1 18941 sub method { return( shift->_set_get_scalar_as_object( 'method', @_ ) ); }
312              
313             sub parse
314             {
315 7     7 1 44176 my $self = shift( @_ );
316 7         26 my $str = shift( @_ );
317 7 100 33     652 warnings::warnif( 'Undefined argument to ' . ( ref( $self ) || $self ) . '->parse()' ) if( !defined( $str ) );
318 7         210 $self->clear_error;
319 7 100 66     498 if( !defined( $str ) || !length( $str ) )
320             {
321 2 50       13 return( ref( $self ) ? $self : $self->new );
322             }
323 5         53 my $opts = $self->_get_args_as_hash( @_ );
324 5   33     591 my $p = HTTP::Promise::Parser->new( debug => ( delete( $opts->{debug} ) || $self->debug ) );
325 5         162 $opts->{request} = 1;
326 5   100     109 my $ent = $p->parse( $str, ( %$opts ? ( $opts ) : () ) ) || return( $self->pass_error( $p->error ) );
327 3         1099 return( $ent->http_message );
328             }
329              
330             # See rfc7230, section 3.1 <https://tools.ietf.org/html/rfc7230#section-3.1>
331             sub start_line
332             {
333 6     6 1 15 my $self = shift( @_ );
334 6         15 my $eol = shift( @_ );
335 6 50       38 $eol = "\n" if( !defined( $eol ) );
336 6   50     29 my $req_line = $self->method || $DEFAULT_METHOD || 'GET';
337 6         5206 my $uri = $self->uri;
338 6 100       4876 if( defined( $uri ) )
339             {
340 3 50       17 if( $self->uri_absolute )
341             {
342 0         0 $uri = "$uri";
343             }
344             else
345             {
346 3         2205 $uri = $uri->path_query;
347             }
348 3 50       62 $uri = '/' if( !length( $uri ) );
349             }
350             else
351             {
352             # $uri = '-';
353 3         18 $uri = '/';
354             }
355 6         33 $req_line .= " $uri";
356 6         76 my $proto = $self->protocol;
357             # $req_line .= " $proto" if( defined( $proto ) && length( $proto ) );
358 6 100 66     4877 if( defined( $proto ) && length( $proto ) )
359             {
360 3         48 $req_line .= " $proto";
361             }
362             else
363             {
364 3   50     16 $req_line .= ' ' . ( $self->default_protocol || 'HTTP/1.1' );
365             }
366 6         2540 return( $req_line );
367             }
368              
369             # NOTE: method protocol is inherited from HTTP::Promise::Message
370 0     0 1 0 sub timeout { return( shift->_set_get_number( 'timeout', @_ ) ); }
371              
372 30     30 1 41664 sub uri { return( shift->_set_get_uri( { field => 'uri', class => 'URI' }, @_ ) ); }
373              
374 3     3 1 21 sub uri_absolute { return( shift->_set_get_boolean( 'uri_absolute', @_ ) ); }
375              
376 0     0 1   sub uri_canonical { return( shift->uri->canonical ); }
377              
378             sub url_encode
379             {
380 0     0 1   my $self = shift( @_ );
381 0           my $str = shift( @_ );
382 0 0         $self->_load_class( 'URL::Encode::XS' ) || return( $self->pass_error );
383 0 0         if( Encode::is_utf8( $str ) )
384             {
385 0           return( URL::Encode::XS::url_encode_utf8( "$str" ) );
386             }
387             else
388             {
389 0           return( URL::Encode::XS::url_encode( "$str" ) );
390             }
391             }
392              
393             sub url_encode_utf8
394             {
395 0     0 1   my $self = shift( @_ );
396 0           my $str = shift( @_ );
397 0 0         $self->_load_class( 'URL::Encode::XS' ) || return( $self->pass_error );
398 0           return( URL::Encode::XS::url_encode_utf8( "$str" ) );
399             }
400              
401             # NOTE: sub FREEZE is inherited
402              
403             # NOTE: sub STORABLE_freeze is inherited
404              
405             # NOTE: sub STORABLE_thaw is inherited
406              
407             # NOTE: sub THAW is inherited
408              
409             1;
410             # NOTE: POD
411             __END__
412              
413             =encoding utf-8
414              
415             =head1 NAME
416              
417             HTTP::Promise::Request - HTTP Request Class
418              
419             =head1 SYNOPSIS
420              
421             use HTTP::Promise::Request;
422             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, $content, k1 => v1, k2 => v2);
423             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, $content, { k1 => v1, k2 => v2 });
424             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, k1 => v1, k2 => v2);
425             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, { k1 => v1, k2 => v2 });
426             my $r = HTTP::Promise::Request->new( $method, $uri, k1 => v1, k2 => v2);
427             my $r = HTTP::Promise::Request->new( $method, k1 => v1, k2 => v2);
428             my $r = HTTP::Promise::Request->new( k1 => v1, k2 => v2 );
429             die( HTTP::Promise::Request->error ) if( !defined( $r ) );
430              
431             =head1 VERSION
432              
433             v0.2.0
434              
435             =head1 DESCRIPTION
436              
437             L<HTTP::Promise::Request> implements a similar interface to L<HTTP::Request>, but does not inherit from it. It uses a different API internally and relies on XS modules for speed while offering more features.
438              
439             L<HTTP::Promise::Request> inherits from L<HTTP::Promise::Message>
440              
441             One major difference with C<HTTP::Request> is that the HTTP request content is not necessarily stored in memory, but it relies on L<HTTP::Promise::Body> as you can see below, and this class has 2 subclasses: 1 storing data in memory when the size is reasonable (threshold set by you) and 1 storing data in a file on the filesystem for larger content.
442              
443             Here is how it fits in overall relation with other classes.
444            
445             +-------------------------+ +--------------------------+
446             | | | |
447             | HTTP::Promise::Request | | HTTP::Promise::Response |
448             | | | |
449             +------------|------------+ +-------------|------------+
450             | |
451             | |
452             | |
453             | +------------------------+ |
454             | | | |
455             +--- HTTP::Promise::Message |---+
456             | |
457             +------------|-----------+
458             |
459             |
460             +------------|-----------+
461             | |
462             | HTTP::Promise::Entity |
463             | |
464             +------------|-----------+
465             |
466             |
467             +------------|-----------+
468             | |
469             | HTTP::Promise::Body |
470             | |
471             +------------------------+
472              
473             =head1 CONSTRUCTOR
474              
475             =head2 new
476              
477             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, $content, k1 => v1, k2 => v2);
478             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, $content, { k1 => v1, k2 => v2 });
479             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, k1 => v1, k2 => v2);
480             my $r = HTTP::Promise::Request->new( $method, $uri, $headers, { k1 => v1, k2 => v2 });
481             my $r = HTTP::Promise::Request->new( $method, $uri, k1 => v1, k2 => v2);
482             my $r = HTTP::Promise::Request->new( $method, k1 => v1, k2 => v2);
483             my $r = HTTP::Promise::Request->new( k1 => v1, k2 => v2 );
484             die( HTTP::Promise::Request->error ) if( !defined( $r ) );
485              
486             my $decodable = $r->accept_decodable;
487             my $r2 = $r->clone;
488             $r->cookie_jar( Cookie::Jar->new );
489             my $jar = $r->cookie_jar;
490             my $proto = $r->default_protocol; # HTTP/1.1
491             say $r->dump;
492             my $headers = $r->headers; # Returns an HTTP::Promise::Headers object
493             $r->make_form_data;
494             $r->method( 'GET' );
495             my $method = $r->method;
496             my $r3 = $r->parse( $data );
497             say $r->start_line;
498             say $r->start_line( "\015\012" );
499             $r->timeout(5);
500             $r->uri( 'https://example.org/some/where' );
501             my $uri = $r->uri; # Returns an URI object
502             my $bool = $r->uri_absolute;
503             my $canonical_uri = $r->uri_canonical;
504              
505             Provided with an HTTP method, URI, an optional set of headers, as either an array reference or a L<HTTP::Promise::Headers> or a L<HTTP::Headers> object, some optional content and an optional hash reference of options (as the last or only parameter), and this instantiates a new L<HTTP::Promise::Request> object. The supported arguments are as follow. Each arguments can be set or changed later using the method with the same name.
506              
507             It returns the newly created object upon success, and upon error, such as bad argument provided, this sets an L<error|Module::Generic/error> and returns C<undef>
508              
509             It takes the following arguments:
510              
511             =over 4
512              
513             =item 1. C<$method>
514              
515             This is a proper HTTP method in upper case. Note that you can also provide non-standard method in any case you want.
516              
517             =item 2. C<$uri>
518              
519             The request uri such as C</> or an absolute uri, typical for making request to proxy, such as C<https://example.org/some/where>
520              
521             =item 3. C<$headers>
522              
523             An L<HTTP::Promise::Headers> object or an array reference of header field-value pairs, such as:
524              
525             my $r = HTTP::Promise::Request->new( $method, $uri, [
526             'Content-Type' => 'text/html; charset=utf-8',
527             Content_Encoding => 'gzip',
528             ]);
529              
530             =item 4. C<$content>
531              
532             C<$content> can either be a string, a scalar reference, or an L<HTTP::Promise::Body> object (L<HTTP::Promise::Body::File> and L<HTTP::Promise::Body::Scalar>)
533              
534             =back
535              
536             Each supported option below can also be set using its corresponding method.
537              
538             Supported options are:
539              
540             =over 4
541              
542             =item * C<content>
543              
544             Same as C<$content> above.
545              
546             =item * C<headers>
547              
548             Same as C<$headers> above.
549              
550             =item * C<method>
551              
552             Same as C<$method> above.
553              
554             =item * C<protocol>
555              
556             The HTTP protocol, such as C<HTTP/1.1> or C<HTTP/2>
557              
558             =item * C<uri>
559              
560             The request uri, such as C</chat> or it could also be a fully qualified uri such as C<wss://example.com/chat>
561              
562             =item * C<version>
563              
564             The HTTP protocol version. Defaults to C<1.17>
565              
566             =back
567              
568             =head1 METHODS
569              
570             =head2 accept_decodable
571              
572             This sets and returns the header C<Accept-Encoding> after having set it the value of L</decodable>
573              
574             =head2 add_content
575              
576             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/add_content>
577              
578             =head2 add_content_utf8
579              
580             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/add_content_utf8>
581              
582             =head2 add_part
583              
584             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/add_part>
585              
586             =head2 as_string
587              
588             Depending on whether L<uri_absolute> is true, this returns a HTTP request with an URI including the HTTP host (a.k.a C<absolute-form>) or only the absolute path (a.k.a C<origin-form>). The former is used when issuing requests to proxies.
589              
590             C<origin-form>:
591              
592             GET /where?q=now HTTP/1.1
593             Host: www.example.org
594              
595             C<absolute-form>:
596              
597             GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1
598              
599             See L<rfc7230, section 5.3|https://tools.ietf.org/html/rfc7230#section-5.3>
600              
601             =head2 boundary
602              
603             This is inherited from L<HTTP::Promise::Message> and returns the multipart boundary currently set in the C<Content-Type> header.
604              
605             =head2 can
606              
607             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/can>
608              
609             =head2 clear
610              
611             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/clear>
612              
613             =head2 clone
614              
615             This clones the current object and returns the clone version.
616              
617             =head2 content
618              
619             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/content>
620              
621             Use this method with care, because it will stringify the request body, thus loading it into memory, which could potentially be important if the body size is large. Maybe you can check the body size first? Something like:
622              
623             my $content;
624             $content = $r->content if( $r->body->length < 102400 );
625              
626             =head2 content_charset
627              
628             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/content_charset>
629              
630             =head2 content_ref
631              
632             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/content_ref>
633              
634             =head2 cookie_jar
635              
636             Sets or gets the L<Cookie::Jar> object. This is used to read and store cookies.
637              
638             =head2 decodable
639              
640             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decodable>
641              
642             =head2 decode
643              
644             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decode>
645              
646             =head2 decode_content
647              
648             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decode_content>
649              
650             =head2 decoded_content
651              
652             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decoded_content>
653              
654             =head2 decoded_content_utf8
655              
656             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/decoded_content_utf8>
657              
658             =head2 default_protocol
659              
660             Sets or gets the default HTTP protocol to use. This defaults to C<HTTP/1.1>
661              
662             =head2 dump
663              
664             This dumps the HTTP request and prints it on the C<STDOUT> in void context, or returns a string of it.
665              
666             =head2 encode
667              
668             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/encode>
669              
670             =head2 entity
671              
672             Sets or gets an L<HTTP::Promise::Entity> object.
673              
674             This object is automatically created upon instantiation of the HTTP request, and if you also provide some content when creating a new object, an L<HTTP::Promise::Body> object will also be created.
675              
676             =head2 header
677              
678             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/header>
679              
680             =head2 headers
681              
682             Sets or gets a L<HTTP::Promise::Headers> object.
683              
684             A header object is always created upon instantiation, whether you provided headers fields or not.
685              
686             =head2 headers_as_string
687              
688             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/headers_as_string>
689              
690             =head2 is_encoding_supported
691              
692             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/is_encoding_supported>
693              
694             =head2 make_boundary
695              
696             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/make_boundary>
697              
698             =head2 make_form_data
699              
700             This takes either an L<HTTP::Promise::Body::Form> object, an L<HTTP::Promise::Body::Form::Data> object, an array reference or an hash reference of form name-value pairs and builds a C<multipart/form-data>
701              
702             If a C<boundary> is already set in the C<Content-Type> header field, it will be used, otherwise a new one will be generated.
703              
704             Each name provided will be the C<form-data> name for each part.
705              
706             It returns the current entity object, or upon error, sets an L<error|Module::Generic/error> and returns C<undef>.
707              
708             Each value provided can be either one of the following:
709              
710             =over 4
711              
712             =item 1. string
713              
714             =item 2. a L<Module::Generic::File> object
715              
716             In this case, the file-mime type will try to be guessed. If you prefer to be specific about the file mime-type, use the alternate C<hash reference> below.
717              
718             =item 3. an hash reference
719              
720             For more granular control, you can provide an hash reference with the following supported properties:
721              
722             =over 8
723              
724             =item * C<encoding>
725              
726             The encoding to be applied to the content. This will also set the C<Content-Encoding> for this C<form-data> part.
727              
728             Note that when provided, the encodings will be applied immediately on the C<form-data> content, whether it is a string or a file.
729              
730             =item * C<file>
731              
732             A filepath to content for this part. The file content will not be loaded into memory, but instead will be used as-is. When it will need to be sent, it will be read from in chunks.
733              
734             If this provided, the L<body object|HTTP::Promise::Body> will be a L<HTTP::Promise::Body::File>
735              
736             =item * C<filename>
737              
738             The C<filename> attribute of the C<Content-Disposition> or this C<form-data> part.
739              
740             If this is not provided and C<headers> property is not provided, or C<headers> is specified, but the C<Content-Disposition> C<filename> attribute is not set, then the C<file> basename will be used instead.
741              
742             =item * C<headers>
743              
744             An L<HTTP::Promise::Headers> object or an array reference of header field-value pairs.
745              
746             =item * C<type>
747              
748             The mime-type to be used for this C<form-data> part.
749              
750             If a C<file> is provided and this is not specified, it will try to guess the mime-type using L<HTTP::Promise::MIME>
751              
752             Note that even if this provided, and if a C<headers> has been specified, it will not override an existing C<Content-Type> header that would have been set.
753              
754             =item * C<value>
755              
756             The C<form-data> value. This is an alternative to providing the C<form-data> content as a C<file>
757              
758             Obviously you should not use both and if you do, C<file> will take priority.
759              
760             If this provided, the L<body object|HTTP::Promise::Body> will be a L<HTTP::Promise::Body::Scalar>
761              
762             =back
763              
764             =back
765              
766             C<multipart/form-data> is the only valid Content-Type for sending multiple data. L<rfc7578 in section 4.3|https://tools.ietf.org/html/rfc7578#section-4.3> states: "[RFC2388] suggested that multiple files for a single form field be transmitted using a nested "multipart/mixed" part. This usage is deprecated."
767              
768             See also this L<Stackoverflow discussion|https://stackoverflow.com/questions/36674161/http-multipart-form-data-multiple-files-in-one-input/41204533#41204533> and L<this one too|https://stackoverflow.com/questions/51575746/http-header-content-type-multipart-mixed-causes-400-bad-request>
769              
770             See also L<HTTP::Promise::Body::Form::Data> for an alternate easy way to create and manipulate C<form-data>, and see also L<HTTP::Promise::Entity/as_form_data>, which will create and return a L<HTTP::Promise::Body::Form::Data> object.
771              
772             =head2 method
773              
774             Sets or gets the HTTP C<method>, such as C<CONNECT>, C<DELETE>, C<GET>, C<HEAD>, C<OPTIONS>, C<PATCH>, C<POST>, C<PUT>, C<TRACE> which are the standard ones as defined by L<rfc7231, section 4.1|https://tools.ietf.org/html/rfc7231#section-4.1>
775              
776             Note that casing must be uppercase for standard methods, but non-standard ones can be whatever you want as long as it complies with the rfc7231.
777              
778             This returns the current method set, if any, as an L<scalar object|Module::Generic::Scalar>
779              
780             =head2 parse
781              
782             Provided with a scalar reference of data, a glob or a file path, and an hash or hash reference of options and this will parse the data provided using L<HTTP::Promise::Parser/parse>, passing it whatever options has been provided. See L<HTTP::Promise::Parser/parse_fh> for the supported options.
783              
784             This returns the resulting L<HTTP::Promise::Message> object from the parsing, or, upon error, sets an L<error|Module::Generic/error> and returns C<undef>.
785              
786             Note that the resulting L<HTTP::Promise::Message> object can be a L<HTTP::Promise::Request> or L<HTTP::Promise::Response> object (both of which inherits from L<HTTP::Promise::Message>) if a start-line was found, or else just an L<HTTP::Promise::Message> object.
787              
788             =head2 parts
789              
790             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/parts>
791              
792             =head2 protocol
793              
794             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/protocol>
795              
796             =head2 start_line
797              
798             Read-only.
799              
800             Returns a regular string representing the start-line containing the L<method|/method>, the L<uri|/uri> and the L<protocol|/protocol> of the request.
801              
802             For example:
803              
804             GET / HTTP/1.1
805              
806             See L<rfc7230, section 3.1|https://tools.ietf.org/html/rfc7230#section-3.1>
807              
808             =head2 timeout
809              
810             Sets or gets the C<timeout> as an integer. This returns the value as an L<number object|Module::Generic::Number>
811              
812             =head2 uri
813              
814             Sets or gets the C<uri>. Returns the current value, if any, as an L<URI> object.
815              
816             =head2 uri_absolute
817              
818             Boolean. Sets or gets whether L</as_string> will return a request including an uri in C<absolute-form> (with the host included) or in C<origin-form> (only with the absolute path).
819              
820             If true, it sets the former otherwise the latter. Default to false.
821              
822             =head2 uri_canonical
823              
824             Returns the current L</uri> in its canonical form by calling L<URI/canonical>
825              
826             =head2 url_encode
827              
828             my $v = $req->url_encode( "=encoding utf-8\n\n=head1 Hello World" );
829             # %3Dencoding+utf-8%0A%0A%3Dhead1+Hello+World
830              
831             or
832              
833             use utf8;
834             my $v = $req->url_encode( "文字化けかな" );
835             # %C3%A6%C2%96%C2%87%C3%A5%C2%AD%C2%97%C3%A5%C2%8C%C2%96%C3%A3%C2%81%C2%91%C3%A3%C2%81%C2%8B%C3%A3%C2%81%C2%AA
836              
837             This returns the string provided properly URL-encoded.
838              
839             This is actually a convenient wrapper around C<URL::Encode::XS::url_encode> or C<URL::Encode::XS::url_encode_utf8> if this is regarded as a Perl internal UTF-8 string.
840              
841             =head2 url_encode_utf8
842              
843             Returns the string provided URL-encoded.
844              
845             This is to be used on Perl internal UTF-8 strings.
846              
847             =head2 version
848              
849             This is inherited from L<HTTP::Promise::Message>. See L<HTTP::Promise::Message/version>
850              
851             =head1 AUTHOR
852              
853             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
854              
855             =head1 SEE ALSO
856              
857             L<rfc7230|https://tools.ietf.org/html/rfc7230>, and L<rfc7231|https://tools.ietf.org/html/rfc7231>
858              
859             L<HTTP::Promise>, L<HTTP::Promise::Request>, L<HTTP::Promise::Response>, L<HTTP::Promise::Message>, L<HTTP::Promise::Entity>, L<HTTP::Promise::Headers>, L<HTTP::Promise::Body>, L<HTTP::Promise::Body::Form>, L<HTTP::Promise::Body::Form::Data>, L<HTTP::Promise::Body::Form::Field>, L<HTTP::Promise::Status>, L<HTTP::Promise::MIME>, L<HTTP::Promise::Parser>, L<HTTP::Promise::IO>, L<HTTP::Promise::Stream>, L<HTTP::Promise::Exception>
860              
861             =head1 COPYRIGHT & LICENSE
862              
863             Copyright(c) 2022 DEGUEST Pte. Ltd.
864              
865             All rights reserved.
866              
867             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
868              
869             =cut