File Coverage

lib/Egg/Response.pm
Criterion Covered Total %
statement 13 16 81.2
branch n/a
condition 0 3 0.0
subroutine 5 6 83.3
pod n/a
total 18 25 72.0


line stmt bran cond sub pod time code
1             package Egg::Response;
2             #
3             # Masatoshi Mizuno E<lt>lusheE<64>cpan.orgE<gt>
4             #
5             # $Id: Response.pm 338 2008-05-19 11:22:55Z lushe $
6             #
7 1     1   508 use strict;
  1         2  
  1         39  
8 1     1   6 use warnings;
  1         2  
  1         178  
9              
10             our $VERSION = '3.01';
11              
12             our $CRLF= "\015\012";
13              
14             our %Status= (
15             200 => 'OK',
16             301 => 'Moved Permanently',
17             302 => 'Moved Temporarily',
18             303 => 'See Other',
19             304 => 'Not Modified',
20             307 => 'Temporarily Redirect',
21             400 => 'Bad Request',
22             401 => 'Unauthorized',
23             403 => 'Forbidden',
24             404 => 'Not Found',
25             405 => 'Method Not Allowed',
26             500 => 'Internal Server Error',
27             );
28              
29 0   0 0     sub response { $_[0]->{response} ||= Egg::Response::handler->new(@_) }
30              
31             *res= \&response;
32              
33             package Egg::Response::handler;
34 1     1   6 use strict;
  1         3  
  1         32  
35 1     1   5 use warnings;
  1         1  
  1         24  
36 1     1   451 use Egg::Response::Headers;
  0            
  0            
37             use Egg::Response::TieCookie;
38             use CGI::Cookie;
39             use CGI::Util qw/ expires /;
40             use Carp qw/ croak /;
41             use base qw/ Egg::Base /;
42              
43             {
44             no strict 'refs'; ## no critic.
45             no warnings 'redefine';
46             for (['Window-Target'], ['Content-Encoding'],
47             ['Content-Disposition', sub { qq{attachment; filename=$_[0]} }],
48             ['P3P', sub { qq{policyref="/w3c/p3p.xml", CP="$_[0]"} }, sub {
49             $_[1] ? join(' ', @_)
50             : ref($_[0]) eq 'ARRAY' ? join(' ', @{$_[0]}) : ($_[0] || "");
51             } ] ) {
52             my $name = $_->[0];
53             my $tcode = $_->[1] || sub { $_[0] };
54             my $acode = $_->[2] || sub { $_[0] };
55             my $lcname= lc($name); $lcname=~s{\-} [_]g;
56             *{__PACKAGE__."::$lcname"}= sub {
57             my $head= shift->headers;
58             if (@_) {
59             my $value= $acode->(@_);
60             delete($head->{$name}) if $head->{$name};
61             return $_[0] ? $head->{$name}= $tcode->($value): "";
62             }
63             my $a= $head->{$name} || return "";
64             $a->[1];
65             };
66             }
67             *attachment= \&content_disposition;
68             };
69              
70             __PACKAGE__->mk_accessors(qw/ nph no_content_length
71             is_expires last_modified content_type content_language location /);
72              
73             sub new {
74             my($class, $e)= @_;
75             bless {
76             e => $e,
77             body=> undef,
78             status=> 0,
79             location => "",
80             parameters=> {},
81             content_type => "",
82             content_language => "",
83             no_content_length => 0,
84             set_modified => ($e->config->{set_modified_constant} || 0),
85             }, $class;
86             }
87             sub body {
88             my $res= shift;
89             return ($res->{body} || undef) unless @_;
90             $res->{body}= $_[0] ? (ref($_[0]) ? $_[0]: \$_[0]): undef;
91             }
92             sub headers {
93             $_[0]->{__headers} ||= Egg::Response::Headers->new($_[0]);
94             }
95             sub header {
96             my $res = shift;
97             my $body= shift || $res->body;
98             my $e = $res->e;
99             my $header;
100             my $headers= $res->{__headers} || {};
101             my($status, $content_type);
102             if ($res->nph) {
103             $header.= ($e->request->protocol || 'HTTP/1.0')
104             . ' '. ($res->status || '200 OK'). $CRLF
105             . 'Server: '. $e->request->server_software
106             . $CRLF;
107             }
108             if ($status= $res->status) {
109             $header = "Status: ${status}". $res->status_string. $CRLF;
110             $header.= 'Location: '
111             . $res->location. $CRLF if $status=~/^30[1237]/;
112             if ($content_type= $res->content_type || "") {
113             $header.= "Content-Type: "
114             . "@{[ $res->_ctype_check($content_type) ]}$CRLF";
115             }
116             } else {
117             $content_type= $res->_ctype_check( $res->content_type
118             || $res->content_type($e->config->{content_type} || 'text/html') );
119             $header.= "Content-Type: ${content_type}$CRLF";
120             }
121             my $regext= $e->config->{no_content_length_regex}
122             || qr{(?:^text/|/(?:rss\+)?xml)};
123             if ($content_type=~m{$regext}i) {
124             if (my $language= $res->content_language) {
125             $header.= "Content-Language: ${language}$CRLF";
126             }
127             } elsif ($body and
128             ! $e->request->is_head and ! $res->no_content_length) {
129             $header.= "Content-Length: ". length($$body). $CRLF;
130             }
131             my $cookie_ok;
132             if (my $cookies= $res->{Cookies}) {
133             while (my($name, $hash)= each %$cookies) {
134             if (ref($hash) eq 'ARRAY') {
135             for (@$hash) {
136             my $obj= $_->{obj} || next;
137             $header.= "Set-Cookie: ". $obj->as_string. $CRLF;
138             }
139             } else {
140             my $cookie= $hash->{obj} || CGI::Cookie->new(
141             '-name' => $name,
142             '-value' => $hash->{value},
143             '-expires' => $hash->{expires},
144             '-domain' => $hash->{domain},
145             '-path' => $hash->{path},
146             '-secure' => $hash->{secure},
147             '-max-age' => $hash->{max_age},
148             '-httponly'=> $hash->{httponly},
149             ) || next;
150             $header.= "Set-Cookie: ". $cookie->as_string. $CRLF;
151             }
152             ++$cookie_ok;
153             }
154             if ($cookie_ok and ! $headers->{P3P}
155             and my $p3p= $e->config->{p3p_policy}) {
156             $res->p3p($p3p);
157             }
158             }
159             $header.= 'Date: '. expires(0,'http'). $CRLF
160             if ($cookie_ok or $res->is_expires or $res->nph);
161             $header.= 'Expires: '. expires($res->is_expires). $CRLF
162             if $res->is_expires;
163             $header.= 'Last-Modified: '. expires($res->last_modified). $CRLF
164             if $res->last_modified;
165             $header.= "Pragma: no-cache$CRLF"
166             . "Cache-Control: no-cache, no-store, must-revalidate$CRLF"
167             if $res->no_cache;
168              
169             for my $h (values %$headers) {
170             $header.= "$h->[0]\: $_$CRLF"
171             for (ref($h->[1]) eq 'ARRAY' ? @{$h->[1]}: $h->[1]);
172             }
173             $res->{header}= $header
174             . 'X-Egg-'. $e->namespace. ': '. $e->VERSION. $CRLF. $CRLF;
175             \$res->{header};
176             }
177             sub _ctype_check {
178             return $_[1] unless $_[1]=~m{^text/};
179             return $_[1] if $_[1]=~m{\;\s+charset=}i;
180             my $charset= $_[0]->charset || return $_[1];
181             qq{$_[1]; charset="${charset}"};
182             }
183             sub charset {
184             my $e= $_[0]->e;
185             $e->stash->{charset_out} || $e->config->{charset_out} || (undef);
186             }
187             sub cookies {
188             my($res)= @_;
189             $res->{Cookies} ||= do {
190             ## $res->{cookies_ok}= 1;
191             my $p3p;
192             if (! $res->p3p and $p3p= $res->e->config->{p3p_policy}) {
193             $res->p3p($p3p);
194             }
195             my %cookies;
196             tie %cookies, 'Egg::Response::TieCookie', $res->e;
197             \%cookies;
198             };
199             }
200             sub cookie {
201             my $res= shift;
202             return keys %{$res->cookies} if @_< 1;
203             my $key= shift || return 0;
204             if (@_) {
205             if (scalar(@_)== 1) {
206             $res->cookies->{$key}= shift;
207             } else {
208             my $hash= { $key, @_ };
209             $key= $hash->{name} || croak q{I want param name.};
210             $res->cookies->{$key}= $hash;
211             }
212             } else {
213             $res->cookies->{$key};
214             }
215             }
216             sub no_cache {
217             my $res= shift;
218             return $res->{no_cache} || 0 unless @_;
219             if ($_[0]) {
220             $_[1] ? $res->is_expires($_[1])
221             : ($res->is_expires || $res->is_expires('-1d'));
222             $_[2] ? $res->last_modified($_[1])
223             : ($res->last_modified || $res->last_modified('-1d'));
224             $res->{no_cache}= 1;
225             } else {
226             $res->is_expires(0);
227             $res->last_modified(0);
228             $res->{no_cache}= 0;
229             }
230             }
231             sub status {
232             my $res= shift;
233             return $res->{status} unless @_;
234             if (my $status= shift) {
235             my($state, $string)=
236             $status=~/^(\d+)(?: +(.+))?/ ? ($1, ($2 || 0)): (200, 0);
237             $res->{status_string}= $string || $Status{$state} || "";
238             return $res->{status}= $state;
239             } else {
240             $res->{status}= $res->{status_string}= "";
241             return 0;
242             }
243             }
244             sub status_string {
245             $_[0]->{status_string} ? " $_[0]->{status_string}": "";
246             }
247             sub redirect {
248             my $res= shift;
249             return ($res->location || undef) unless @_;
250             return $_[0] ? do {
251             $res->location( shift || '/' );
252             my $status= shift || 302;
253             my $o= $_[1] ? {@_}: $_[0];
254             $res->window_target($o->{target}) if $o->{target};
255             $res->e->finished($status);
256             }: do {
257             $res->status(0);
258             $res->window_target(0);
259             $res->location("");
260             $res->e->finished(0);
261             };
262             }
263             sub clear_body {
264             my($res)= @_;
265             $res->{body}= undef if $res->{body};
266             }
267             sub clear_cookies {
268             return 0 unless $_[0]->{Cookies};
269             my($res)= @_;
270             tied(%{$res->{Cookies}})->_clear;
271             delete($res->headers->{P3P}) if $res->headers->{P3P};
272             1;
273             }
274             sub clear {
275             my($res)= @_;
276             $res->$_(0) for (qw/ redirect
277             no_cache no_content_length content_type content_language nph /);
278             $res->headers->clear if $res->{__headers};
279             undef($res->{header});
280             $res->clear_cookies;
281             1;
282             }
283             sub DESTROY {
284             my($res)= @_;
285             untie %{$res->{Cookies}} if $res->{Cookies};
286             }
287              
288             1;
289              
290             __END__
291              
292             =head1 NAME
293              
294             Egg::Response - WEB response processing for Egg.
295              
296             =head1 SYNOPSIS
297              
298             # The object is acquired.
299             my $res= $e->response;
300            
301             # The contents type is set.
302             $res->content_type('text/plain');
303            
304             # The cache control is set.
305             $res->no_cache(1);
306            
307             # The output contents are set.
308             $res->body('Hell world !!');
309            
310             # The enhancing header is set.
311             $res->headers->{'My-Header'}= 'OK';
312            
313             # Cookie is set.
314             $res->cookie( hoge => 'boo' );
315            
316             # It redirects it.
317             $res->redirect('http://ho.com/hellow.html', '302');
318            
319             # The response header is generated.
320             my $scalar_ref= $res->header;
321              
322             =head1 DESCRIPTION
323              
324             The WEB response processing for the Egg framework is done.
325              
326             =head1 METHODS
327              
328             The main body of this module is built into the component of the project.
329              
330             =head2 response
331              
332             The handler object of this module is returned.
333              
334             =over 4
335              
336             =item * res
337              
338             =back
339              
340             =head1 HANDLER METHODS
341              
342             =head2 new
343              
344             Constructor. It is not necessary to call from the application.
345              
346             my $res= $e->response;
347              
348             =head2 body ([BODY_STRING])
349              
350             Output contents are maintained.
351              
352             The maintained data is always done by the SCALAR reference.
353              
354             Undef is set when 0 is given to BODY_STRING and it initializes it.
355              
356             my $scalar_ref= $res->body(<<END_BODY);
357             Hellow world !!
358             END_BODY
359              
360             =head2 headers
361              
362             L<Egg::Response::Headers> object is returned.
363              
364             The response header not supported by this module can be set by this.
365              
366             $res->headers->{'X-MyHader'}= 'hoge';
367              
368             =head2 header ([BODY_SCALAR_REF])
369              
370             It returns it making the response header.
371             Egg calls this by a series of processing. It is not necessary to call it from
372             the project.
373              
374             To measure Content-Length, BODY_SCALAR_REF is passed.
375             $res-E<gt>body is used when omitted.
376              
377             The returned value is SCALAR always reference.
378              
379             =head2 content_type ([STRING])
380              
381             To generate the Content-Type header by the header method, it sets it.
382              
383             It can be overwrited that content_type is set to the configuration though default
384             is 'text/html'.
385              
386             Moreover, when the contents type of default is output, it is not necessary to
387             call 'content_type'.
388              
389             $res->content_type('text/javascript');
390              
391             =head2 content_language ([STRING])
392              
393             To generate the Content-Language header by the header method, it sets it.
394              
395             The Content-Language header is not usually output because there is no default.
396              
397             $res->content_language('ja');
398              
399             =head2 no_cache ([BOOL])
400              
401             It is a flag to generate the header for the cash control by the header method.
402              
403             $res->no_cache(1);
404              
405             =head2 nph ([BOOL])
406              
407             It is a flag to generate the header of NPH scripting by the header method.
408              
409             $res->nph(1);
410              
411             * However, please note the thing not behaving like the NPH script in usual
412             processing about Egg.
413              
414             =head2 no_content_length ([BOOL])
415              
416             It is a flag so as not to output the Content-Length header.
417              
418             $e->no_content_length(1);
419              
420             The Content-Length header is not output at the following time.
421              
422             =over 4
423              
424             =item * It is possible to overwrite in 'no_content_length_regex' of the
425             configuration though default is qr{(?:^text/|/(?:rss\+)?xml)} when matching
426             it to the putter of $res-E<gt>{no_content_length_regex}.
427              
428             =item * When $e-E<gt>request-E<gt>is_head returns ture.
429              
430             =item * When $res-E<gt>body returns undefined.
431              
432             =back
433              
434             =head2 is_expires ([ARGS])
435              
436             To generate the Expires header by the header method, it sets it.
437              
438             ARGS is a value passed to the expires function of L<CGI::Util>.
439              
440             $res->is_expires('+1D');
441              
442             =head2 last_modified ([ARGS])
443              
444             To generate the Last-Modified header by the header method, it sets it.
445              
446             ARGS is a value passed to the expires function of L<CGI::Util>.
447              
448             $res->last_modified('+1D');
449              
450             =head2 cookies
451              
452             The HASH reference to set Cookie is returned.
453              
454             Returned HASH is the one having made it by L<Egg::Response::TieCookie>.
455              
456             $res->cookies->{'Hoo'}= '123';
457              
458             When 'p3p_policy' of the configuration is defined, the value is set if the p3p
459             method is still undefined.
460             Please set 'p3p_policy' to the configuration when you want to transmit the P3P
461             header when Cookie is output.
462              
463             package MyApp::config;
464             sub out { {
465             .............
466             ........
467             p3p_policy => 'UNI CUR OUR',
468             ........
469             } }
470              
471             =head2 cookie ([KEY], [VALUE])
472              
473             The list of the key to the data that has been set to omit the argument is
474             returned.
475              
476             The content of Cookie that corresponds more than the data set to give KEY is
477             returned.
478              
479             When VALUE is given, Cookie is set.
480              
481             my @key_list= $res->cookie;
482            
483             my $hoo= $res->cookie('Hoo');
484            
485             $res->cookie( Boo => '456' );
486              
487             =head2 status ([STATUS])
488              
489             The response status is set.
490              
491             The following forms are accepted.
492              
493             $res->status(403);
494             $res->status('403 Forbiden');
495             $res->status(403, 'Forbiden');
496              
497             When the text part is omitted, the value corresponding to status is set from
498             %Egg::Response::Status. The response code not supported by this module can be
499             customized by adding it to %Egg::Response::Status.
500              
501             0 Is initialized when giving it.
502              
503             =head2 status_string
504              
505             The text defined by the status method is returned. When the returned value exists,
506             half angle space is sure to be included in the head.
507              
508             my $status= 'Status: '. $res->status. $res->status_string;
509              
510             =head2 redirect ([URI], [STATUS_CODE], [OPTION_HASH])
511              
512             It is prepared to generate Ridairectoheddar.
513              
514             URL is concretely set in the location method, and STATUS_CODE is set in the
515             status method. And, the result of $e->finished is returned at the end.
516              
517             There is no default of URL. Please specify it.
518              
519             The default of STATUS_CODE is 302.
520              
521             The window target can be specified with OPTION_HASH.
522              
523             $res->redirect('/hoge', 302, target=> '_top' );
524              
525             0 Is canceled when giving it.
526              
527             =head2 location ([URI])
528              
529             To generate the Location header with the header, it sets it.
530              
531             This is usually called in the redirect method and set.
532              
533             Please note no desire as redirecting even if only this method is set.
534              
535             The value that has already been set is returned at the URI unspecification.
536              
537             =head2 window_target ([TARGET_STRING])
538              
539             Window-Target is set.
540              
541             $res->window_target('foo');
542              
543             * Because it is the one set in the response header, this is evaluated depending
544             on a browser of the client. Especially, this header might been deleted as for
545             the client that is via the proxy. I do not think that it is in certain target
546             specification.
547              
548             =head2 content_encoding ([ENCODING_STRING])
549              
550             Content-Encoding is set.
551              
552             $res->content_encoding('identity');
553              
554             =head2 content_disposition ([FILE_NAME])
555              
556             Content-Disposition is set.
557              
558             This is used to specify the file name when it is made to download.
559              
560             $res->content_disposition('myname.txt');
561            
562             # It is output to the header as follows.
563             Content-Disposition: attachment; filename=myname.txt
564              
565             =over 4
566              
567             =item * Alias = attachment
568              
569             =back
570              
571             =head2 p3p ([SIMPLE_POLICY])
572              
573             P3P is set.
574              
575             SIMPLE_POLICY gives the character string of the ARRAY reference or the half angle
576             space district switching off.
577              
578             $res->p3p('UNI CUR OUR');
579            
580             # It is output to the header as follows.
581             P3P: policyref="/w3c/p3p.xml", CP="UNI CUR OUR"
582              
583             =head2 clear_body
584              
585             Undef is set in the value of body and it initializes it.
586              
587             =head2 clear_cookies
588              
589             The set Cookie data is annulled. And, if the P3P header is set, it also initializes it.
590              
591             =head2 clear
592              
593             no_cache, no_content_length, content_type, content_language, nph, headers, and
594             clear_cookies are done in this method.
595              
596             Please note that body is not cleared.
597              
598             =head1 SEE ALSO
599              
600             L<Egg::Release>
601             L<Egg::Response::Headers>,
602             L<Egg::Response::TieCookie>,
603             L<CGI::Cookie>,
604             L<CGI::Util>,
605             L<Egg::Base>,
606              
607             =head1 AUTHOR
608              
609             Masatoshi Mizuno E<lt>lusheE<64>cpan.orgE<gt>
610              
611             =head1 COPYRIGHT AND LICENSE
612              
613             Copyright (C) 2008 Bee Flag, Corp. E<lt>L<http://egg.bomcity.com/>E<gt>.
614              
615             This library is free software; you can redistribute it and/or modify
616             it under the same terms as Perl itself, either Perl version 5.8.6 or,
617             at your option, any later version of Perl 5 you may have available.
618              
619             =cut
620