File Coverage

blib/lib/CGI/Easy/Request.pm
Criterion Covered Total %
statement 139 139 100.0
branch 56 68 82.3
condition 12 14 85.7
subroutine 15 15 100.0
pod 2 2 100.0
total 224 238 94.1


line stmt bran cond sub pod time code
1             package CGI::Easy::Request;
2              
3 4     4   77867 use warnings;
  4         10  
  4         124  
4 4     4   22 use strict;
  4         7  
  4         115  
5 4     4   20 use Carp;
  4         151  
  4         350  
6              
7 4     4   1737 use version; our $VERSION = qv('1.0.0'); # REMINDER: update Changes
  4         4400  
  4         31  
8              
9             # REMINDER: update dependencies in Makefile.PL
10 4     4   2056 use CGI::Easy::Util qw( uri_unescape_plus burst_urlencoded burst_multipart );
  4         10  
  4         30  
11 4     4   5399 use URI::Escape qw( uri_unescape );
  4         6  
  4         186  
12 4     4   3536 use MIME::Base64;
  4         3220  
  4         274  
13              
14 4     4   27 use constant MiB => 1024*1024; ## no critic(Capitalization)
  4         8  
  4         5916  
15              
16             my $MAX_POST = MiB;
17              
18             sub new {
19 40     40 1 96791 my ($class, $opt) = @_;
20 40 100       1301 my $self = {
21             scheme => $ENV{HTTPS} ? 'https' : 'http',
22             host => undef,
23             port => $ENV{SERVER_PORT},
24             path => undef,
25             GET => {}, # for GET, HEAD, DELETE, …
26             POST => {}, # for POST, PUT
27             filename => {},
28             mimetype => {},
29             cookie => {},
30             REMOTE_ADDR => $ENV{REMOTE_ADDR},
31             REMOTE_PORT => $ENV{REMOTE_PORT},
32             AUTH_TYPE => $ENV{AUTH_TYPE},
33             REMOTE_USER => $ENV{REMOTE_USER},
34             REMOTE_PASS => undef,
35             ENV => { %ENV },
36             STDIN => q{},
37             error => q{},
38             };
39 40         147 bless $self, $class;
40              
41 40         66 my $pre = $opt->{frontend_prefix};
42 40 100       147 if (defined $pre) {
43 2         4 $pre = uc $pre;
44 2         6 $pre =~ s/-/_/xmsg;
45 2 100       8 if (defined $ENV{"HTTP_${pre}REMOTE_ADDR"}) {
46 1         3 $self->{REMOTE_ADDR} = $ENV{"HTTP_${pre}REMOTE_ADDR"};
47 1         3 $self->{REMOTE_PORT} = $ENV{"HTTP_${pre}REMOTE_PORT"};
48 1 50       7 $self->{scheme} = $ENV{"HTTP_${pre}HTTPS"} ? 'https' : 'http';
49             }
50             }
51              
52 40         76 my $host = $ENV{HTTP_HOST};
53 40         76 my $path = $ENV{REQUEST_URI};
54 40 100       126 if ($path =~ s{\A\w+://(?:[^/@]*@)?([^/]+)}{}xms) {
55 2         7 $host = $1;
56             }
57 40         73 $host =~ s{:\d+\z}{}xms;
58 40         126 $path =~ s{[?].*}{}xms;
59 40         166 $path = uri_unescape($path); # WARNING nginx allow %2F, apache didn't
60 40 100       361 if (!length $path) {
61 1         3 $path = q{/};
62             }
63 40         102 $self->{host} = $host;
64 40         69 $self->{path} = $path;
65              
66 40 100       104 if ($ENV{HTTP_AUTHORIZATION}) {
67 3 100       18 if ($ENV{HTTP_AUTHORIZATION} =~ /\ABasic\s+(\S+)\z/xms) {
68 2         17 my ($user, $pass) = split /:/xms, decode_base64($1), 2;
69 2 50       11 if (defined $pass) {
70 2         7 $self->{AUTH_TYPE} = 'Basic';
71 2         6 $self->{REMOTE_USER} = $user;
72 2         5 $self->{REMOTE_PASS} = $pass;
73             }
74             }
75 3 100       15 if (!defined $self->{REMOTE_PASS}) {
76 1         4 $self->{error} = 'failed to parse HTTP_AUTHORIZATION';
77             }
78             }
79              
80 40         112 $self->_read_cookie();
81              
82 40 100 100     195 if ($ENV{REQUEST_METHOD} eq 'POST' || $ENV{REQUEST_METHOD} eq 'PUT') {
83 14         75 $self->_read_post($opt->{max_post});
84 14 100       59 if ($opt->{post_with_get}) {
85 2         10 $self->_read_get();
86             }
87             } else {
88 26         72 $self->_read_get();
89             }
90              
91 40 100       109 if (!$opt->{keep_all_values}) {
92 39         114 $self->_force_scalar_params();
93             }
94              
95 40 100       110 if (!$opt->{raw}) {
96 37         89 $self->_decode_utf8();
97             }
98              
99 40         1016 return $self;
100             }
101              
102             sub param {
103 14     14 1 42 my ($self, $name) = @_;
104 14 100       31 if (defined $name) {
105 12         14 my @result;
106 12         23 for my $method (qw( POST GET )) {
107 24 100       74 if (exists $self->{$method}{$name}) {
108 16         29 my $value = $self->{$method}{$name};
109 16 100       42 push @result, ref $value ? @{$value} : $value;
  6         20  
110             }
111             }
112 12 100       79 return wantarray ? @result : $result[0];
113             }
114             else {
115 2 50       4 my %p = map { $_ => 1 } keys %{$self->{POST} || {}}, keys %{$self->{GET} || {}};
  8 50       26  
  2         9  
  2         8  
116 2         23 return keys %p;
117             }
118             }
119              
120             sub _force_scalar_params {
121 39     39   58 my ($self) = @_;
122 39         123 for my $p ($self->{GET}, $self->{POST}, $self->{filename}, $self->{mimetype}) {
123 156 50       167 for my $name (keys %{ $p || {} }) {
  156         587  
124 54 100       3134 if ($name !~ /\[\]\z/xms) {
125 44         156 $p->{ $name } = $p->{ $name }[0];
126             }
127             }
128             }
129 39         79 return;
130             }
131              
132             sub _decode_utf8 {
133 37     37   47 my ($self) = @_;
134 37         116 utf8::decode($self->{path});
135 37         68 for my $key (qw( GET POST filename mimetype cookie )) {
136 185         199 my %tmp;
137 185 50       196 for my $name (keys %{ $self->{$key} || {} }) {
  185         626  
138 63 100       164 if (ref $self->{$key}{$name}) {
139 16         22 for my $i (0 .. $#{ $self->{$key}{$name} }) {
  16         49  
140 22 50 66     81 if (!($key eq 'POST' && defined $self->{mimetype}{$name}[$i])) {
141 22         71 utf8::decode($self->{$key}{$name}[$i]);
142             }
143             }
144             }
145             else {
146 47 100 100     159 if (!($key eq 'POST' && defined $self->{mimetype}{$name})) {
147 45         631 utf8::decode($self->{$key}{$name});
148             }
149             }
150 63         302 my $namestr = $name; utf8::decode($namestr);
  63         7101  
151 63         9870 $tmp{ $namestr } = $self->{$key}{$name};
152             }
153 185         609 $self->{$key} = \%tmp;
154             }
155 37         77 return;
156             }
157              
158             sub _read_cookie {
159 40     40   59 my ($self) = @_;
160 40   100     266 foreach (split /;\s?/xms, $self->{ENV}{HTTP_COOKIE} || q{}) {
161 9         49 s/\s*(.*?)\s*/$1/xms;
162 9         28 my ($key, $value) = split /=/xms, $_, 2;
163             # Some foreign cookies are not in name=value format, so ignore them.
164 9 50       31 next if !defined $value;
165 9         36 $key = uri_unescape_plus($key);
166 9         197 $value = uri_unescape_plus($value);
167             # A bug in Netscape can cause several cookies with same name to
168             # appear. The FIRST one in HTTP_COOKIE is the most recent version.
169 9 50       436 next if exists $self->{cookie}{$key};
170 9         104 $self->{cookie}{$key} = $value;
171             }
172 40         76 return;
173             }
174              
175             sub _read_get {
176 28     28   41 my $self = shift;
177 28         108 $self->{GET} = burst_urlencoded($self->{ENV}{QUERY_STRING});
178 28         62 return;
179             }
180              
181             sub _read_post {
182 14     14   33 my ($self, $max_post) = @_;
183              
184 14   66     57 $max_post ||= $MAX_POST;
185 14 100       69 if ($self->{ENV}{CONTENT_LENGTH} > $max_post) {
186 1         2 $self->{error} = 'POST body too large';
187 1         3 return;
188             }
189              
190 13         23 my $buffer = q{};
191 13 50       46 if ($self->{ENV}{CONTENT_LENGTH} > 0) {
192 13         29 binmode STDIN;
193 13         1911 my $n = read STDIN, $buffer, $self->{ENV}{CONTENT_LENGTH}, 0;
194 13         27 $self->{STDIN} = $buffer;
195 13 100       47 if ($n != $self->{ENV}{CONTENT_LENGTH}) {
196 1         1 $self->{error} = 'POST body incomplete';
197 1         2 return;
198             }
199             }
200              
201             # Boundaries are supposed to consist of only the following
202             # (1-70 of them, not ending in ' ') A-Za-z0-9 '()+,_-./:=?
203 12         66 my $multipart = qr{\Amultipart/form-data;\s+boundary=(.*)\z}xmsi;
204 12 50       41 if ($self->{ENV}{CONTENT_TYPE}) {
205 12 100       119 if ($self->{ENV}{CONTENT_TYPE} =~ m/$multipart/xms) {
    50          
206 2         10 my $boundary = $1;
207 2         43 @{$self}{'POST','filename','mimetype'}
  2         7  
208             = burst_multipart($buffer, $boundary);
209             }
210             elsif ($self->{ENV}{CONTENT_TYPE} eq 'application/x-www-form-urlencoded') {
211 10         50 $self->{POST} = burst_urlencoded($buffer);
212             }
213             }
214 12         53 return;
215             }
216              
217              
218             1; # Magic true value required at end of module
219             __END__
220              
221             =encoding utf8
222              
223             =head1 NAME
224              
225             CGI::Easy::Request - parse CGI params
226              
227              
228             =head1 SYNOPSIS
229              
230             use CGI::Easy::Request;
231              
232             my $r = CGI::Easy::Request->new();
233             my $r = CGI::Easy::Request->new({
234             frontend_prefix => 'X-Real-',
235             max_post => 10*1024*1024,
236             post_with_get => 1,
237             raw => 1,
238             keep_all_values => 1,
239             });
240              
241             if ($r->{error}) { # incorrect HTTP request
242             print "417 Expectation Failed\r\n\r\n";
243             print $r->{error};
244             exit;
245             }
246              
247             my @all_param_names = $r->param();
248             my $myparam = $r->param('myparam'); # first 'myparam' value
249             my @myparam = $r->param('myparam'); # all 'myparam' values
250              
251             print $r->{GET}{'myarray[]'}[0];
252             print $r->{GET}{myparam}; # without keep_all_values=>1
253             print $r->{GET}{myparam}[0]; # with keep_all_values=>1
254              
255             $uploaded_file = $r->{POST}{myfile};
256             $uploaded_filename = $r->{filename}{myfile};
257              
258             print $r->{cookie}{mycookie};
259              
260             print $r->{ENV}{HTTP_USER_AGENT};
261              
262              
263             =head1 DESCRIPTION
264              
265             Parse CGI params (from %ENV and STDIN) and provide user with ease to use
266             hash (object) with all interesting data.
267              
268             =head2 FEATURES
269              
270             =over
271              
272             =item * DoS protection
273              
274             Maximum size of content in STDIN is B<always> limited.
275              
276             =item * HTTP Basic authorization support
277              
278             Provide CGI with remote user name and password.
279              
280             =item * UTF8 support
281              
282             Decode path, GET/POST/cookie names and values (except uploaded files content)
283             and uploaded file names from UTF8 to Unicode.
284              
285             =item * Frontend web server support
286              
287             Can take REMOTE_ADDR/REMOTE_PORT and "https" scheme from non-standard HTTP
288             headers (like X-Real-REMOTE-ADDR) which is usually set by nginx/lighttpd
289             frontends.
290              
291             =item * HEAD/GET/POST/PUT/DELETE/… support
292              
293             Params sent with POST or PUT method will be placed in C<< {POST} >>,
294             params for all other methods (including unknown) will be placed in C<< {GET} >>.
295              
296             =back
297              
298              
299             =head1 INTERFACE
300              
301             =over
302              
303             =item new( [\%opt] )
304              
305             Parse CGI request from %ENV and STDIN.
306              
307             Create new object, which contain all parsed data in public fields.
308             You can access/modify all fields of this object in any way.
309              
310             If given, %opt may contain these fields:
311              
312             =over
313              
314             =item {frontend_prefix}
315              
316             If there frontend web server used, then CGI executed on backend web server
317             will not be able to detect user's IP/port and is HTTPS used in usual way,
318             because CGI "user" now isn't real user, but frontend web server instead.
319              
320             In this case usual environment variables REMOTE_ADDR and REMOTE_PORT will
321             contain frontend web server's address, and variable HTTPS will not exists
322             (because frontend will not use https to connect to backend even if user
323             connects to frontend using https).
324              
325             Frontend can be configured to send real user's IP/port/https in custom
326             HTTP headers (like X-Real-REMOTE_ADDR, X-Real-REMOTE_PORT, X-Real-HTTPS).
327             For example, nginx configuration may looks like:
328              
329             server {
330             listen *:80;
331             ...
332             proxy_set_header X-Real-REMOTE_ADDR $remote_addr;
333             proxy_set_header X-Real-REMOTE_PORT $remote_port;
334             proxy_set_header X-Real-HTTPS "";
335             }
336             server {
337             listen *:443;
338             ...
339             proxy_set_header X-Real-REMOTE_ADDR $remote_addr;
340             proxy_set_header X-Real-REMOTE_PORT $remote_port;
341             proxy_set_header X-Real-HTTPS on;
342             }
343              
344             If you can guarantee only frontend is able to connect to backend, then you can
345             safely trust these X-Real-* headers. In this case you can set
346             C<< frontend_prefix => 'X-Real-' >> and new() will parse headers with this
347             prefix instead of standard REMOTE_ADDR, REMOTE_PORT and HTTPS variables.
348              
349             =item {max_post}
350              
351             To protect against DoS attack, size of POST/PUT request is B<always> limited.
352             Default limit is 1 MB. You can change it using C<< {max_post} >> option
353             (value in bytes).
354              
355             =item {post_with_get}
356              
357             Sometimes POST/PUT request sent to url which also contain some parameters
358             (after '?'). By default these parameters will be ignored, and only
359             parameters sent in HTTP request body (STDIN) will be parsed (to C<< {POST} >>).
360             If you want to additionally parse parameters from url you should set
361             C<< post_with_get => 1 >> option (these parameters will be parsed to
362             C<< {GET} >> and not mixed with parameters in C<< {POST} >>).
363              
364             =item {keep_all_values}
365              
366             By default only parameters which names ending with '[]' are allowed to have
367             more than one value. These parameters stored in fields
368             C<< {GET}, {POST}, {filename} and {mimetype} >> as ARRAYREF, while all other
369             parameters stored as SCALAR (only first value for these parameters is stored).
370             If you want to allow more than one value in all parameters you should set
371             C<< keep_all_values => 1 >> option, and all parameters will be stored as ARRAYREF.
372              
373             =item {raw}
374              
375             By default we suppose request send either in UTF8 (or ASCII) encoding.
376             Request path, GET/POST/cookie names and values (except uploaded files content)
377             and uploaded file names will be decoded from UTF8 to Unicode.
378              
379             If you need to handle requests in other encodings, you should disable
380             automatic decoding from UTF8 using C<< raw => 1 >> option and decode
381             all these things manually.
382              
383             =back
384              
385             Created object will contain these fields:
386              
387             =over
388              
389             =item {scheme}
390              
391             'http' or 'https'.
392              
393             You may need to use C<< frontend_prefix >> option if you've frontend and
394             backend web servers to reliably detect 'https' scheme.
395              
396             =item {host}
397              
398             =item {port}
399              
400             Host name and port for requested url.
401              
402             =item {path}
403              
404             Path from url, always begin with '/'.
405              
406             Will be decoded from UTF8 to Unicode unless new() called with option
407             C<< raw=>1 >>.
408              
409             =item {GET}
410              
411             =item {POST}
412              
413             Will contain request parameters. For request methods POST and PUT
414             parameters will be stored in C<< {POST} >> (if option C<< post_with_get => 1 >>
415             used then parameters from url will be additionally stored in C<< {GET >>),
416             for all other methods (HEAD/GET/DELETE/etc.) parameters will be stored
417             in C<< {GET} >>.
418              
419             These fields will contain HASHREF with parameter names, which value will depend
420             on C<< keep_all_values >> option. By default, value for parameters which
421             names ending with '[]' will be ARRAYREF, and for all other SCALAR (only first
422             value for these parameters will be stored if more than one available).
423              
424             Example: request "GET http://example.com/some.cgi?a=5&a=6&b[]=7&b[]=8&c=9"
425             will be parsed to
426              
427             # by default:
428             GET => {
429             'a' => 5,
430             'b[]' => [ 7, 8 ],
431             'c' => 9,
432             },
433             POST => {}
434              
435             # with option keep_all_values=>1:
436             GET => {
437             'a' => [ 5, 6 ],
438             'b[]' => [ 7, 8 ],
439             'c' => [ 9 ],
440             },
441             POST => {}
442              
443             Parameter names and values (except file content) be decoded from UTF8 to
444             Unicode unless new() called with option C<< raw=>1 >>.
445              
446             =item {filename}
447              
448             =item {mimetype}
449              
450             When C<< <INPUT TYPE="FILE"> >> used to upload files, browser will send
451             uploaded file name and MIME type in addition to file contents.
452             These values will be available in fields C<< {filename} >> and C<< {mimetype} >>,
453             which have same format as C<< {POST} >> field.
454              
455             Example: submitted form contain parameter "a" with value "5" and parameter
456             "image" with value of file "C:\Images\some.gif" will be parsed to:
457              
458             GET => {},
459             POST => {
460             a => 5,
461             image => '...binary image data...',
462             },
463             filename => {
464             a => undef,
465             image => 'C:\Images\some.gif',
466             }
467             mimetype => {
468             a => undef,
469             image => 'image/gif',
470             }
471              
472             Parameter names and file names will be decoded from UTF8 to Unicode unless
473             new() called with option C<< raw=>1 >>.
474              
475             =item {cookie}
476              
477             Will contain hash with cookie names and values. Example:
478              
479             cookie => {
480             some_cookie => 'some value',
481             other_cookie => 'other value',
482             }
483              
484             Cookie names and values will be decoded from UTF8 to Unicode unless
485             new() called with option C<< raw=>1 >>.
486              
487             =item {REMOTE_ADDR}
488              
489             =item {REMOTE_PORT}
490              
491             User's IP and port.
492              
493             You may need to use C<< frontend_prefix >> option if you've frontend and
494             backend web servers.
495              
496             =item {AUTH_TYPE}
497              
498             =item {REMOTE_USER}
499              
500             =item {REMOTE_PASS}
501              
502             There two ways to use HTTP authentication:
503              
504             1) Web server will check user login/pass, and will provide values for
505             C<< {AUTH_TYPE} >> and C<< {REMOTE_USER} >>. In this case C<< {REMOTE_PASS} >>
506             will contain undef().
507              
508             2) Your CGI will manually check authentication. Only 'Basic' type of HTTP
509             authentication supported by this module. In this case C<< {AUTH_TYPE} >> will be
510             set to 'Basic', and C<< {REMOTE_USER} >> and C<< {REMOTE_PASS} >> will contain
511             login/pass sent by user, and your CGI should check is they correct.
512             To allow this type of manual authentication you may need to configure
513             C<.htaccess> to force Apache to send HTTP_AUTHORIZATION environment to your
514             CGI/FastCGI script:
515              
516             <Files "myscript.cgi">
517             RewriteEngine On
518             RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
519             </Files>
520              
521             =item {ENV}
522              
523             =item {STDIN}
524              
525             These fields will contain copy of %ENV and STDIN contents as they was seen
526             by new(). This is useful to access values in %ENV which doesn't included in
527             other fields of this object, and to manually parse non-standard data in STDIN.
528              
529             =item {error}
530              
531             This field will contain empty string if HTTP request was formed correctly, or
532             error message if HTTP request was formed incorrectly. Possible errors are:
533              
534             failed to parse HTTP_AUTHORIZATION
535             POST body too large
536             POST body incomplete
537              
538             =back
539              
540             Return created CGI::Easy::Request object.
541              
542              
543             =item param( )
544              
545             =item param( $name )
546              
547             This method shouldn't be called if you modified format of C<< {GET} >> or
548             C<< {POST} >> fields.
549              
550             When called without parameter will return ARRAY with all CGI parameter
551             names, both GET and POST parameter names will be joined.
552              
553             When called with parameter name will return value of this parameter (from
554             POST parameter if it exists, or from GET if it doesn't exist in POST parameters).
555             All stored values (see C<< keep_all_values >> option) for this parameter
556             will be returned in ARRAY context, and only first value will be returned
557             in SCALAR context.
558              
559              
560             =back
561              
562              
563             =head1 BUGS AND LIMITATIONS
564              
565             No bugs have been reported.
566              
567             Receiving (from FORM upload) large files will use a lot of memory - this
568             module doesn't use temporary files and keep everything in memory.
569              
570              
571             =head1 SUPPORT
572              
573             Please report any bugs or feature requests through the web interface at
574             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CGI-Easy>.
575             I will be notified, and then you'll automatically be notified of progress
576             on your bug as I make changes.
577              
578             You can also look for information at:
579              
580             =over
581              
582             =item * RT: CPAN's request tracker
583              
584             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=CGI-Easy>
585              
586             =item * AnnoCPAN: Annotated CPAN documentation
587              
588             L<http://annocpan.org/dist/CGI-Easy>
589              
590             =item * CPAN Ratings
591              
592             L<http://cpanratings.perl.org/d/CGI-Easy>
593              
594             =item * Search CPAN
595              
596             L<http://search.cpan.org/dist/CGI-Easy/>
597              
598             =back
599              
600              
601             =head1 AUTHOR
602              
603             Alex Efros C<< <powerman-asdf@ya.ru> >>
604              
605              
606             =head1 LICENSE AND COPYRIGHT
607              
608             Copyright 2009-2010 Alex Efros <powerman-asdf@ya.ru>.
609              
610             This program is distributed under the MIT (X11) License:
611             L<http://www.opensource.org/licenses/mit-license.php>
612              
613             Permission is hereby granted, free of charge, to any person
614             obtaining a copy of this software and associated documentation
615             files (the "Software"), to deal in the Software without
616             restriction, including without limitation the rights to use,
617             copy, modify, merge, publish, distribute, sublicense, and/or sell
618             copies of the Software, and to permit persons to whom the
619             Software is furnished to do so, subject to the following
620             conditions:
621              
622             The above copyright notice and this permission notice shall be
623             included in all copies or substantial portions of the Software.
624              
625             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
626             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
627             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
628             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
629             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
630             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
631             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
632             OTHER DEALINGS IN THE SOFTWARE.
633              
634