File Coverage

blib/lib/HTTP/Request/Generator.pm
Criterion Covered Total %
statement 194 295 65.7
branch 47 68 69.1
condition 20 23 86.9
subroutine 22 25 88.0
pod 5 8 62.5
total 288 419 68.7


line stmt bran cond sub pod time code
1             package HTTP::Request::Generator;
2 6     6   79513 use strict;
  6         45  
  6         188  
3 6     6   3156 use Filter::signatures;
  6         164862  
  6         45  
4 6     6   237 use feature 'signatures';
  6         15  
  6         187  
5 6     6   36 no warnings 'experimental::signatures';
  6         14  
  6         192  
6 6     6   3836 use Algorithm::Loops 'NestedLoops';
  6         14728  
  6         388  
7 6     6   5435 use List::MoreUtils 'zip';
  6         91173  
  6         37  
8 6     6   9973 use URI;
  6         28489  
  6         187  
9 6     6   40 use URI::Escape;
  6         14  
  6         344  
10 6     6   41 use Exporter 'import';
  6         14  
  6         151  
11 6     6   30 use Carp 'croak';
  6         25  
  6         23141  
12              
13             =head1 NAME
14              
15             HTTP::Request::Generator - generate HTTP requests
16              
17             =head1 SYNOPSIS
18              
19             use HTTP::Request::Generator 'generate_requests';
20              
21             @requests = generate_requests(
22             method => 'GET',
23             pattern => 'https://example.com/{bar,foo,gallery}/[00..99].html',
24             );
25              
26             # generates 300 requests from
27             # https://example.com/bar/00.html to
28             # https://example.com/gallery/99.html
29              
30             @requests = generate_requests(
31             method => 'POST',
32             host => ['example.com','www.example.com'],
33             path => '/profiles/:name',
34             url_params => {
35             name => ['Corion','Co-Rion'],
36             },
37             query_params => {
38             stars => [2,3],
39             },
40             body_params => {
41             comment => ['Some comment', 'Another comment, A++'],
42             },
43             headers => [
44             {
45             "Content-Type" => 'text/plain; encoding=UTF-8',
46             Cookie => 'my_session_id',
47             },
48             {
49             "Content-Type" => 'text/plain; encoding=Latin-1',
50             Cookie => 'my_session_id',
51             },
52             ],
53             );
54             # Generates 32 requests out of the combinations
55              
56             for my $req (@requests) {
57             $ua->request( $req );
58             };
59              
60             =cut
61              
62             our $VERSION = '0.11';
63             our @EXPORT_OK = qw( generate_requests as_dancer as_plack as_http_request
64             expand_curl_pattern
65             );
66              
67 162     162 0 221 sub unwrap($item,$default) {
  162         271  
  162         207  
  162         196  
68 162 100       401 defined $item
    100          
69             ? (ref $item ? $item : [$item])
70             : $default
71             }
72              
73 26     26 0 89 sub fetch_all( $iterator, $limit=0 ) {
  26         43  
  26         55  
  26         39  
74 26         34 my @res;
75 26         55 while( my @r = $iterator->()) {
76 190         364 push @res, @r;
77 190 100 100     568 if( $limit && (@res > $limit )) {
78 1         3 splice @res, $limit;
79             last
80 1         3 };
81             };
82             return @res
83 26         825 };
84              
85             our %defaults = (
86             method => ['GET'],
87             path => ['/'],
88             host => [''],
89             port => [0],
90             scheme => ['http'],
91              
92             # How can we specify various values for the headers?
93             headers => [{}],
94              
95             #query_params => [],
96             #body_params => [],
97             #url_params => [],
98             #values => [[]], # the list over which to iterate for *_params
99             );
100              
101             # We want to skip a set of values if they make a test fail
102             # if a value appears anywhere with a failing test, skip it elsewhere
103             # or look at the history to see whether that value has passing tests somewhere
104             # and then keep it?!
105              
106 70538     70538 0 88784 sub fill_url( $url, $values, $raw=undef ) {
  70538         105638  
  70538         88476  
  70538         91341  
  70538         90396  
107 70538 50       119511 if( $values ) {
108 70538 100       109643 if( $raw ) {
109 70490 50       157218 $url =~ s!:(\w+)!exists $values->{$1} ? $values->{$1} : ":$1"!ge;
  52795         199836  
110             } else {
111 48 50       127 $url =~ s!:(\w+)!exists $values->{$1} ? uri_escape($values->{$1}) : ":$1"!ge;
  24         269  
112             };
113             };
114 70538         183563 $url
115             };
116              
117             # Convert nonref arguments to arrayrefs
118             sub _makeref {
119             map {
120 108 100   108   194 ref $_ ne 'ARRAY' ? [$_] : $_
  197         462  
121             } @_
122             }
123              
124 17     17   28 sub _extract_enum( $name, $item ) {
  17         22  
  17         25  
  17         54  
125             # Explicitly enumerate all ranges
126 17 50       38 my @res = @{ $defaults{ $name } || []};
  17         90  
127              
128 17 100       58 if( $item ) {
129             # Expand all ranges into the enumerated lists
130 1         4 $item =~ s!\[([^.\]]+?)\.\.([^.\]]+?)\]!"{" . join(",", $1..$2 )."}"!ge;
  0         0  
131              
132             # Explode all enumerated items into their list
133             # We should punt this into a(nother) iterator, maybe?!
134 1 50       4 if( $item =~ /\{.*\}/ ) {
135 0         0 my $changed = 1;
136 0         0 @res = $item;
137 0         0 while ($changed) {
138 0         0 undef $changed;
139             @res = map {
140 0         0 my $i = $_;
  0         0  
141 0         0 my @r;
142 0 0       0 if( $i =~ /^([^{]*)\{([^}]+)\}([^{]*)/ ) {
143 0         0 my($pre, $m, $post) = ($1,$2,$3);
144 0         0 $changed = 1;
145 0         0 @r = map { "$pre$_$post" } split /,/, $m, -1;
  0         0  
146             } else {
147 0         0 @r = $i
148             };
149             @r
150 0         0 } @res;
151             }
152             } else {
153 1         4 @res = $item;
154             };
155             };
156             return \@res
157 17         37 }
158              
159 17     17   26 sub _extract_enum_query( $query ) {
  17         24  
  17         23  
160 17         38 my $items = _extract_enum( 'query', $query );
161 17         29 my %parameters;
162 17         39 for my $q (@$items) {
163 1         6 my $u = URI->new('example.com', 'http');
164 1         62 $u->query($q);
165 1         25 my %f = $u->query_form;
166 1         75 for my $k (keys %f) {
167 2         9 $parameters{ $k }->{ $f{ $k }} = 1;
168             };
169             };
170              
171 17         46 for my $v (values %parameters) {
172 2         8 $v = [ sort keys %$v ];
173             };
174              
175 17         119 \%parameters
176             }
177              
178              
179             =head2 C<< expand_curl_pattern >>
180              
181             my %res = expand_curl_pattern( 'https://' );
182             #
183              
184             Expands a curl-style pattern to a pattern using positional placeholders.
185             See the C documentation on the patterns.
186              
187             =cut
188              
189 17     17 1 27 sub expand_curl_pattern( $pattern ) {
  17         26  
  17         25  
190 17         26 my %ranges;
191              
192             # Split up the URL pattern into a scheme, host(pattern), port number and
193             # path (pattern)
194             #use Regexp::Debugger;
195 17         26 my $ipv4_digit = '(?:[01]?\d?\d|25[0-5]|2[0-4]\d)';
196 17         54 my $ipv4 = "(?:$ipv4_digit\\.$ipv4_digit\\.$ipv4_digit\\.$ipv4_digit)";
197 17         65 my $ipv6s = qr!(?:[\da-fA-F]{1,4})!;
198              
199 17         971 my( $scheme, $host, $port, $path, $query )
200             = $pattern =~ m!^(?:([^:]+):)? # scheme
201             /?/? # optional? slashes
202             ( # hostname
203             \[(?: # ipv6
204             (?:$ipv6s ){1,1}(?::$ipv6s){7,7} # plain ipv6
205             |(?:$ipv6s:){1,7}(?:: ){1,1} # plain ipv6
206             |(?:$ipv6s:){1,6}(?::$ipv6s){1,1} # plain ipv6
207             |(?:$ipv6s:){1,5}(?::$ipv6s){1,2} # plain ipv6
208             |(?:$ipv6s:){1,4}(?::$ipv6s){1,3} # plain ipv6
209             |(?:$ipv6s:){1,3}(?::$ipv6s){1,4} # plain ipv6
210             |(?:$ipv6s:){1,2}(?::$ipv6s){1,5} # plain ipv6
211             |(?:$ipv6s:){1,1}(?::$ipv6s){1,6} # plain ipv6
212             |(?: :){1,1}(?::$ipv6s){1,7} # plain ipv6
213             |fe80:(?:$ipv6s){0,4}%%[0-9a-zA-Z]+ # link local+index
214             |(?:(?:0{1,4}:){1,5}|::)(?:ffff:(?:0{1,4}:)?)$ipv4 # ipv4-in-ipv6
215             )\]
216             |[^/:\[]+
217             (?:\[[^/\]]+\][^/:\[]*)*
218             (?=[:/]|$) # plain, or expansion
219             |\[[^:\]]+\][^/:]* # expansion
220             )
221             (?::(\d+))? # optional port
222             ([^?]*) # path
223             (?:\?(.*))? # optional query part
224             $!xi;
225              
226             # Explicitly enumerate all ranges
227 17         54 my $idx = 0;
228              
229 17 100       44 if( $scheme ) {
230 16         29 $scheme =~ s!\[([^.\]]+?)\.\.([^.\[]+?)\]!$ranges{$idx} = [$1..$2]; ":".$idx++!ge;
  0         0  
  0         0  
231 16         39 $scheme =~ s!\{([^\}]*)\}!$ranges{$idx} = [split /,/, $1, -1]; ":".$idx++!ge;
  2         15  
  2         12  
232             };
233              
234 17 50       33 if( $host ) {
235 17         57 $host =~ s!\[([^.\]]+?)\.\.([^.\[]+?)\]!$ranges{$idx} = [$1..$2]; ":".$idx++!ge;
  6         58  
  6         30  
236 17         35 $host =~ s!\{([^\}]*)\}!$ranges{$idx} = [split /,/, $1, -1]; ":".$idx++!ge;
  1         7  
  1         5  
237             };
238              
239 17 100       38 if( $port ) {
240 6         9 $port =~ s!\[([^.\]]+?)\.\.([^.\[]+?)\]!$ranges{$idx} = [$1..$2]; ":".$idx++!ge;
  0         0  
  0         0  
241 6         12 $port =~ s!\{([^\}]*)\}!$ranges{$idx} = [split /,/, $1, -1]; ":".$idx++!ge;
  0         0  
  0         0  
242             };
243              
244 17 100       37 if( $path ) {
245 13         56 $path =~ s!\[([^.\]]+?)\.\.([^.\[]+?)\]!$ranges{$idx} = [$1..$2]; ":".$idx++!ge;
  9         61  
  9         40  
246             # Move all explicitly enumerated parts into lists:
247 13         38 $path =~ s!\{([^\}]*)\}!$ranges{$idx} = [split /,/, $1, -1]; ":".$idx++!ge;
  4         20  
  4         20  
248             };
249              
250 17         51 my %res = (
251             url_params => \%ranges,
252             host => $host,
253             scheme => $scheme,
254             port => $port,
255             path => $path,
256             query_params => _extract_enum_query( $query ),
257             raw_params => 1,
258             );
259 17         161 %res
260             }
261              
262 27     27   46 sub _generate_requests_iter(%options) {
  27         56  
  27         38  
263 27 100 50 17766   181 my $wrapper = delete $options{ wrap } || sub { wantarray ? @_ : $_[0]};
  17766         59203  
264 27         180 my @keys = sort keys %defaults;
265              
266 27 100       88 if( my $pattern = delete $options{ pattern }) {
267 17         53 %options = (%options, expand_curl_pattern( $pattern ));
268             };
269              
270 27   100     96 my $query_params = $options{ query_params } || {};
271 27   100     93 my $body_params = $options{ body_params } || {};
272 27   100     72 my $url_params = $options{ url_params } || {};
273              
274             $options{ "fixed_$_" } ||= {}
275 27   50     401 for @keys;
276              
277             # Now only iterate over the non-empty lists
278 27         65 my %args = map { my @v = unwrap($options{ $_ }, [@{$defaults{ $_ }}]);
  162         355  
  162         362  
279 162 50       484 @v ? ($_ => @v) : () }
280             @keys;
281 27         156 @keys = sort keys %args; # somewhat predictable
282             $args{ $_ } ||= {}
283 27   50     210 for qw(query_params body_params url_params);
284 27         86 my @loops = _makeref @args{ @keys };
285             #use Data::Dumper; warn Dumper \@loops, \@keys, \%options;
286              
287             # Turn all query_params into additional loops for each entry in keys %$query_params
288             # Turn all body_params into additional loops over keys %$body_params
289 27         65 my @query_params = keys %$query_params;
290 27         58 push @loops, _makeref values %$query_params;
291 27         53 my @body_params = keys %$body_params;
292 27         53 push @loops, _makeref values %$body_params;
293 27         62 my @url_params = keys %$url_params;
294 27         55 push @loops, _makeref values %$url_params;
295              
296             #use Data::Dumper; warn "Looping over " . Dumper \@loops;
297              
298 27         121 my $iter = NestedLoops(\@loops,{});
299              
300             # Set up the fixed parts
301 27         1689 my %template;
302              
303 27         66 for(qw(query_params body_params headers)) {
304 81   100     313 $template{ $_ } = $options{ "fixed_$_" } || {};
305             };
306              
307             return sub {
308 17792     17792   91708 my @v = $iter->();
309 17792 100       375266 return unless @v;
310             #use Data::Dumper; warn Dumper \@v;
311              
312             # Patch in the new values
313 17766         53812 my %values = %template;
314 17766         42623 my @vv = splice @v, 0, 0+@keys;
315 17766         67342 @values{ @keys } = @vv;
316              
317             # Now add the query_params, if any
318 17766 100       36741 if(@query_params) {
319 122         242 my @get_values = splice @v, 0, 0+@query_params;
320 122         182 $values{ query_params } = { (%{ $values{ query_params } }, zip( @query_params, @get_values )) };
  122         790  
321             };
322             # Now add the body_params, if any
323 17766 100       32556 if(@body_params) {
324 8         14 my @values = splice @v, 0, 0+@body_params;
325 8         12 $values{ body_params } = { %{ $values{ body_params } }, zip @body_params, @values };
  8         34  
326             };
327              
328             # Recreate the URL with the substituted values
329 17766 100       33515 if( @url_params ) {
330 17636         24017 my %v;
331 17636         40585 @v{ @url_params } = splice @v, 0, 0+@url_params;
332              
333 17636         34808 for my $key (qw(scheme port path host)) {
334 70544 100 100     177492 if( $key ne 'host'
335             or $values{ $key } !~ /^\[.*\]$/ ) {
336 70538         135336 $values{ $key } = fill_url($values{ $key }, \%v, $options{ raw_params });
337             };
338             };
339             };
340              
341 17766         38597 $values{ url } = _build_uri( \%values );
342              
343             # Merge the headers as well
344             #warn "Merging headers: " . Dumper($values{headers}). " + " . (Dumper $template{headers});
345 17766 50       27063 %{$values{headers}} = (%{$template{headers}}, %{$values{headers} || {}});
  17766         29564  
  17766         31948  
  17766         39639  
346 17766         37294 return $wrapper->(\%values);
347 27         219 };
348             }
349              
350 17766     17766   26184 sub _build_uri( $req ) {
  17766         24830  
  17766         21671  
351 17766         49414 my $uri = URI->new( '', $req->{scheme} );
352 17766 100       885271 if( $req->{host}) {
353 17634         47967 $uri->host( $req->{host});
354 17634         1092944 $uri->scheme( $req->{scheme});
355 17634 100 100     1335593 $uri->port( $req->{port}) if( $req->{port} and $req->{port} != $uri->default_port );
356             };
357 17766         52455 $uri->path( $req->{path});
358             # We want predictable URIs, so we sort the keys here instead of
359             # just passing the hash reference
360 17766         458494 $uri->query_form( map { $_ => $req->{query_params}->{$_} } sort keys %{ $req->{query_params} });
  456         1056  
  17766         67458  
361             #$uri->query_form( $req->{query_params});
362 17766         266585 $uri
363             }
364              
365             =head2 C<< generate_requests( %options ) >>
366              
367             my $g = generate_requests(
368             url => '/profiles/:name',
369             url_params => ['Mark','John'],
370             wrap => sub {
371             my( $req ) = @_;
372             # Fix up some values
373             $req->{headers}->{'Content-Length'} = 666;
374             return $req;
375             },
376             );
377             while( my $r = $g->()) {
378             send_request( $r );
379             };
380              
381             This function creates data structures that are suitable for sending off
382             a mass of similar but different HTTP requests. All array references are expanded
383             into the cartesian product of their contents. The above example would create
384             two requests:
385              
386             url => '/profiles/Mark,
387             url => '/profiles/John',
388              
389             C returns an iterator in scalar context. In list context, it
390             returns the complete list of requests:
391              
392             my @requests = generate_requests(
393             url => '/profiles/:name',
394             url_params => ['Mark','John'],
395             wrap => sub {
396             my( $req ) = @_;
397             # Fix up some values
398             $req->{headers}->{'Content-Length'} = 666;
399             return $req;
400             },
401             );
402             for my $r (@requests) {
403             send_request( $r );
404             };
405              
406             Note that returning a list instead of the iterator will use up quite some memory
407             quickly, as the list will be the cartesian product of the input parameters.
408              
409             There are helper functions
410             that will turn that data into a data structure suitable for your HTTP framework
411             of choice.
412              
413             {
414             method => 'GET',
415             url => '/profiles/Mark',
416             scheme => 'http',
417             port => 80,
418             headers => {},
419             body_params => {},
420             query_params => {},
421             }
422              
423             As a shorthand for creating lists, you can use the C option, which
424             will expand a string into a set of requests. C<{}> will expand into alternatives
425             while C<[xx..yy]> will expand into the range C to C. Note that these
426             lists will be expanded in memory.
427              
428             =head3 Options
429              
430             =over 4
431              
432             =item B
433              
434             pattern => 'https://example.{com,org,net}/page_[00..99].html',
435              
436             Generate URLs from this pattern instead of C, C
437             and C.
438              
439             =item B
440              
441             URL template to use.
442              
443             =item B
444              
445             Parameters to replace in the C template.
446              
447             =item B
448              
449             Parameters to replace in the POST body.
450              
451             =item B
452              
453             Parameters to replace in the GET request.
454              
455             =item B
456              
457             Hostname(s) to use.
458              
459             =item B
460              
461             Port(s) to use.
462              
463             =item B
464              
465             Headers to use. Currently, no templates are generated for the headers. You have
466             to specify complete sets of headers for each alternative.
467              
468             =item B
469              
470             Limit the number of requests generated.
471              
472             =back
473              
474             =cut
475              
476 27     27 1 32195 sub generate_requests(%options) {
  27         81  
  27         42  
477             croak "Option 'protocol' is now named 'scheme'."
478 27 50       101 if $options{ protocol };
479              
480 27         88 my $i = _generate_requests_iter(%options);
481 27 100       73 if( wantarray ) {
482 26         124 return fetch_all($i, $options{ limit });
483             } else {
484 1         5 return $i
485             }
486             }
487              
488             =head2 C<< as_http_request >>
489              
490             generate_requests(
491             method => 'POST',
492             url => '/feedback/:item',
493             wrap => \&HTTP::Request::Generator::as_http_request,
494             )
495              
496             Converts the request data to a L object.
497              
498             =cut
499              
500 0     0 1   sub as_http_request($req) {
  0            
  0            
501 0           require HTTP::Request;
502 0           HTTP::Request->VERSION(6); # ->flatten()
503 0           require URI;
504 0           require URI::QueryParam;
505              
506 0           my $body = '';
507 0           my $headers;
508             my $form_ct;
509 0 0         if( keys %{$req->{body_params}}) {
  0            
510 0           require HTTP::Request::Common;
511             my $r = HTTP::Request::Common::POST( $req->{url},
512 0           [ %{ $req->{body_params} }],
  0            
513             );
514 0           $headers = HTTP::Headers->new( %{ $req->{headers} }, $r->headers->flatten );
  0            
515 0           $body = $r->content;
516 0           $form_ct = $r->content_type;
517             } else {
518 0           $headers = HTTP::Headers->new( %$headers );
519             };
520              
521             # Store metadata / generate "signature" for later inspection/isolation?
522 0           my $uri = _build_uri( $req );
523 0 0         $uri->query_param( %{ $req->{query_params} || {} });
  0            
524             my $res = HTTP::Request->new(
525 0           $req->{method} => $uri,
526             $headers,
527             $body,
528             );
529 0           $res
530             }
531              
532             =head2 C<< as_dancer >>
533              
534             generate_requests(
535             method => 'POST',
536             url => '/feedback/:item',
537             wrap => \&HTTP::Request::Generator::as_dancer,
538             )
539              
540             Converts the request data to a L object.
541              
542             During the creation of Dancer::Request objects, C<< %ENV >> will be empty except
543             for C<< $ENV{TMP} >> and C<< $ENV{TEMP} >>.
544              
545              
546             This function needs and dynamically loads the following modules:
547              
548             L
549              
550             L
551              
552             =cut
553              
554 0     0 1   sub as_dancer($req) {
  0            
  0            
555 0           require Dancer::Request;
556 0           require HTTP::Request;
557 0           HTTP::Request->VERSION(6); # ->flatten()
558              
559 0           my $body = '';
560 0           my $headers;
561             my $form_ct;
562 0 0         if( keys %{$req->{body_params}}) {
  0            
563 0           require HTTP::Request::Common;
564             my $r = HTTP::Request::Common::POST( $req->{url},
565 0           [ %{ $req->{body_params} }],
  0            
566             );
567 0           $headers = HTTP::Headers->new( %{ $req->{headers} }, $r->headers->flatten );
  0            
568 0           $body = $r->content;
569 0           $form_ct = $r->content_type;
570             } else {
571 0           $headers = HTTP::Headers->new( %$headers );
572             };
573              
574 0           my $uri = _build_uri( $req );
575              
576             # Store metadata / generate "signature" for later inspection/isolation?
577 0           my %old_ENV = %ENV;
578 0           local %ENV; # wipe out non-overridable default variables of Dancer::Request
579 0           my @keep = (qw(TMP TEMP));
580 0           @ENV{ @keep } = @old_ENV{ @keep };
581             my $res = Dancer::Request->new_for_request(
582             $req->{method} => $uri->path,
583             $req->{query_params},
584             $body,
585             $headers,
586             { CONTENT_LENGTH => length($body),
587             CONTENT_TYPE => $form_ct,
588             HTTP_HOST => (join ":", $req->{host}, $req->{port}),
589             SERVER_NAME => $req->{host},
590             SERVER_PORT => $req->{port},
591             REQUEST_METHOD => $req->{ method },
592 0           REQUEST_URI => $uri,
593             SCRIPT_NAME => $uri->path,
594              
595             },
596             );
597 0           $res->{_http_body}->add($body);
598             #use Data::Dumper; warn Dumper $res;
599 0           $res
600             }
601              
602             =head2 C<< as_plack >>
603              
604             generate_requests(
605             method => 'POST',
606             url => '/feedback/:item',
607             wrap => \&HTTP::Request::Generator::as_plack,
608             )
609              
610             Converts the request data to a L object.
611              
612             During the creation of Plack::Request objects, C<< %ENV >> will be empty except
613             for C<< $ENV{TMP} >> and C<< $ENV{TEMP} >>.
614              
615             This function needs and dynamically loads the following modules:
616              
617             L
618              
619             L
620              
621             L
622              
623             =cut
624              
625 0     0 1   sub as_plack($req) {
  0            
  0            
626 0           require Plack::Request;
627 0           Plack::Request->VERSION(1.0047);
628 0           require HTTP::Headers;
629 0           require Hash::MultiValue;
630              
631 0           my %env = %$req;
632 0           $env{ 'psgi.version' } = '1.0';
633 0           $env{ 'psgi.url_scheme' } = delete $env{ scheme };
634 0 0         $env{ 'plack.request.query_parameters' } = [%{delete $env{ query_params }||{}} ];
  0            
635 0 0         $env{ 'plack.request.body_parameters' } = [%{delete $env{ body_params }||{}} ];
  0            
636 0           $env{ 'plack.request.headers' } = HTTP::Headers->new( %{ delete $req->{headers} });
  0            
637 0           $env{ REQUEST_METHOD } = delete $env{ method };
638 0           $env{ REQUEST_URI } = _build_uri( $req );
639 0           $env{ SCRIPT_NAME } = $env{ REQUEST_URI }->path;
640 0           delete $env{ url };
641 0           $env{ QUERY_STRING } = ''; # not correct, but...
642 0           $env{ SERVER_NAME } = delete $env{ host };
643 0           $env{ SERVER_PORT } = delete $env{ port };
644             # need to convert the headers into %env HTTP_ keys here
645 0           $env{ CONTENT_TYPE } = undef;
646              
647             # Store metadata / generate "signature" for later inspection/isolation?
648 0           my %old_ENV = %ENV;
649 0           local %ENV; # wipe out non-overridable default variables of Dancer::Request
650 0           my @keep = (qw(TMP TEMP));
651 0           @ENV{ @keep } = @old_ENV{ @keep };
652 0           my $res = Plack::Request->new(\%env);
653 0           $res
654             }
655              
656             1;
657              
658             =head1 SEE ALSO
659              
660             L for the pattern syntax
661              
662             =head1 REPOSITORY
663              
664             The public repository of this module is
665             L.
666              
667             =head1 SUPPORT
668              
669             The public support forum of this module is L.
670              
671             =head1 BUG TRACKER
672              
673             Please report bugs in this module via the RT CPAN bug queue at
674             L
675             or via mail to L.
676              
677             =head1 AUTHOR
678              
679             Max Maischein C
680              
681             =head1 COPYRIGHT (c)
682              
683             Copyright 2017-2019 by Max Maischein C.
684              
685             =head1 LICENSE
686              
687             This module is released under the same terms as Perl itself.
688              
689             =cut