File Coverage

blib/lib/ASP4/Response.pm
Criterion Covered Total %
statement 72 96 75.0
branch 6 18 33.3
condition 0 8 0.0
subroutine 22 25 88.0
pod 13 18 72.2
total 113 165 68.4


line stmt bran cond sub pod time code
1              
2             package ASP4::Response;
3              
4 9     9   704 use strict;
  9         10  
  9         232  
5 9     9   28 use warnings 'all';
  9         12  
  9         239  
6 9     9   446 use HTTP::Date qw( time2str );
  9         2849  
  9         439  
7 9     9   435 use ASP4::HTTPContext;
  9         16  
  9         162  
8 9     9   3179 use ASP4::Mock::RequestRec;
  9         13  
  9         8831  
9              
10              
11             sub new
12             {
13 5122     5122 0 11905 my $s = bless {
14             _status => 200,
15             _expires => 0,
16             _content_type => 'text/html',
17             _expires_absolute => time2str( time() ),
18             }, shift;
19 5122         68362 $s->Status( $s->Status );
20 5122         7033 $s->Expires( $s->Expires );
21 5122         6501 $s->ContentType( $s->ContentType );
22            
23 5122         12645 return $s;
24             }# end new()
25              
26 76687     76687 0 99706 sub context { ASP4::HTTPContext->current }
27              
28              
29             sub ContentType
30             {
31 10244     10244 1 7150 my $s = shift;
32            
33 10244 100       11649 if( @_ )
34             {
35 5122         3972 my $type = shift;
36 5122         4850 $s->{_content_type} = $type;
37 5122         5487 $s->context->r->content_type( $type );
38             }
39             else
40             {
41 5122         7798 return $s->{_content_type};
42             }# end if()
43             }# end ContentType()
44              
45              
46             sub Expires
47             {
48 11245     11245 0 9990 my $s = shift;
49 11245 100       15459 if( @_ )
50             {
51 6123         4790 $s->{_expires} = shift;
52 6123         11261 $s->{_expires_absolute} = time2str( time() + ( $s->{_expires} * 60 ) );
53 6123         51793 $s->AddHeader( expires => $s->ExpiresAbsolute );
54             }# end if()
55            
56 11245         171064 return $s->{_expires};
57             }# end Expires()
58              
59              
60 6123     6123 0 9551 sub ExpiresAbsolute { shift->{_expires_absolute} }
61              
62              
63             sub Status
64             {
65 30751     30751 1 21837 my $s = shift;
66            
67 30751 100       99534 @_ ? $s->context->r->status( $s->{_status} = +shift ) : $s->{_status};
68             }# end Status()
69              
70              
71             sub End
72             {
73 5130     5130 1 4299 my $s = shift;
74 5130         5711 $s->Flush;
75            
76             # Would be nice to somehow stop all execution:
77 5130         5907 $s->context->did_end( 1 );
78             }# end End()
79              
80              
81             sub Flush
82             {
83 10268     10268 1 6897 my $s = shift;
84 10268         10212 $s->context->rflush;
85             }# end Flush()
86              
87              
88             sub Clear
89             {
90             shift->context->rclear
91 0     0 0 0 }# end Clear()
92              
93              
94             sub IsClientConnected
95             {
96 1103     1103 1 1418 ! shift->context->r->connection->aborted();
97             }# end IsClientConnected()
98              
99              
100             sub Write
101             {
102 38639     38639 1 34731 my $s = shift;
103 38639         36901 $s->context->rprint( shift(@_) )
104             }# end Write()
105              
106              
107             sub SetCookie
108             {
109 0     0 1 0 my ($s, %args) = @_;
110            
111 0   0     0 $args{domain} ||= eval { $s->context->config->data_connections->session->cookie_domain } || $ENV{HTTP_HOST};
      0        
112 0   0     0 $args{path} ||= '/';
113 0         0 my @parts = ( );
114 0         0 push @parts, $s->context->server->URLEncode($args{name}) . '=' . $s->context->server->URLEncode($args{value});
115 0         0 push @parts, 'domain=' . $s->context->server->URLEncode($args{domain});
116 0         0 push @parts, 'path=' . $args{path};
117 0 0       0 if( $args{expires} )
118             {
119 0 0       0 if( my ($num,$type) = $args{expires} =~ m/^(\-?\d+)([MHD])$/ )
120             {
121 0         0 my $expires;
122 0 0       0 if( $type eq 'M' ) {
    0          
    0          
123             # Minutes:
124 0         0 $expires = time() + ( $num * 60 );
125             }
126             elsif( $type eq 'H' ) {
127             # Hours:
128 0         0 $expires = time() + ( $num * 60 * 60 );
129             }
130             elsif( $type eq 'D' ) {
131             # Days:
132 0         0 $expires = time() + ( $num * 60 * 60 * 24 );
133             }# end if()
134 0         0 push @parts, 'expires=' . time2str( $expires );
135             }
136             else
137             {
138 0         0 push @parts, 'expires=' . time2str( $args{expires} );
139             }# end if()
140             }# end if()
141 0         0 $s->AddHeader( 'Set-Cookie' => join('; ', @parts) . ';' );
142             }# end SetCookie()
143              
144              
145             sub AddHeader
146             {
147 6123     6123 1 5483 my ($s, $name, $value) = @_;
148            
149 6123         6721 $s->context->headers_out->header( $name => $value );
150             }# end AddHeader()
151              
152              
153             sub Headers
154             {
155 5116     5116 1 3627 my $s = shift;
156            
157 5116         6662 my $out = $s->context->headers_out;
158 5116         8726 map {{
159 5116         17435 $_ => $out->{$_}
160             }} keys %$out;
161             }# end Headers()
162              
163              
164             sub Redirect
165             {
166 0     0 1 0 my ($s, $url) = @_;
167            
168 0 0       0 return if $s->context->did_send_headers;
169            
170 0         0 $s->Clear;
171 0         0 $s->Status( 301 );
172 0         0 $s->AddHeader( Location => $url );
173 0         0 $s->End;
174             }# end Redirect()
175              
176              
177 5122     5122 1 17428 sub Declined { -1 }
178              
179              
180             sub Include
181             {
182 7     7 1 13 my ($s, $file, $args) = @_;
183            
184 7         25 $s->Write( $s->_subrequest( $file, $args ) );
185             }# end Include()
186              
187              
188             sub TrapInclude
189             {
190 1     1 1 3 my ($s, $file, $args) = @_;
191            
192 1         2 return $s->_subrequest( $file, $args );
193             }# end TrapInclude()
194              
195              
196             sub _subrequest
197             {
198 8     8   9 my ($s, $file, $args) = @_;
199            
200 8         20 $s->context->add_buffer();
201 8         16 my $original_r = $s->context->r;
202 8         14 my $root = $s->context->config->web->www_root;
203 8         87 (my $uri = $file) =~ s/^\Q$root\E//;
204 8         35 my $r = ASP4::Mock::RequestRec->new(
205             uri => $uri,
206             args => $original_r->args,
207             );
208 8         62 local $ENV{SCRIPT_NAME} = $uri;
209 8         28 local $ENV{REQUEST_URI} = $uri;
210 8         31 local $ENV{SCRIPT_FILENAME} = $file;
211 8         31 $s->context->setup_request( $r, $s->context->cgi );
212 8         68 $s->context->execute( $args, 1 );
213 8         25 $s->Flush;
214 8         20 my $buffer = $s->context->purge_buffer();
215 8         18 $s->context->{r} = $original_r;
216 8         31 return $r->buffer;
217             }# end _subrequest()
218              
219              
220             sub DESTROY
221             {
222 5104     5104   3396 my $s = shift;
223 5104         10240 undef(%$s);
224             }# end DESTROY()
225              
226             1;# return true:
227              
228             =pod
229              
230             =head1 NAME
231              
232             ASP4::Response - Interface to the outgoing HTTP response
233              
234             =head1 SYNOPSIS
235              
236             $Response->ContentType("text/html");
237            
238             $Response->Status( 200 );
239            
240             $Response->Clear();
241            
242             $Response->Flush();
243              
244             $Response->Write("Hello, World!");
245            
246             $Response->AddHeader( 'x-awesomeness' => '100%' );
247            
248             $Response->SetCookie(
249             # Required parameters:
250             name => "customer-email",
251             value => $Form->{email},
252            
253             # The rest are optional:
254             expires => '30D', # 30 days
255             path => '/',
256             domain => '.mysite.com',
257             );
258            
259             $Response->Redirect( "/path/to/page.asp" );
260            
261             $Response->Include( $Server->MapPath("/my/include.asp") );
262             $Response->Include( $Server->MapPath("/my/include.asp"), \%args );
263            
264             my $string = $Response->TrapInclude( $Server->MapPath("/my/widget.asp") );
265             my $string = $Response->TrapInclude( $Server->MapPath("/my/widget.asp"), \%args );
266            
267             return $Response->Declined;
268            
269             $Response->End;
270            
271             while( 1 ) {
272             last unless $Response->IsClientConnected();
273             $Response->Write("Still Here!
");
274             sleep(1);
275             }
276            
277             my HTTP::Headers $headers = $Response->Headers;
278            
279             # Read-only:
280             my $expires_on = $Response->ExpiresAbsolute;
281              
282             =head1 DESCRIPTION
283              
284             The C<$Response> object offers a unified interface to send content back to the client.
285              
286             =head1 PROPERTIES
287              
288             =head2 ContentType( [$type] )
289              
290             Sets or gets the C header for the response. Examples are C, C, C, etc.
291              
292             =head2 Status( [$status] )
293              
294             Sets or gets the C header for the response. See L for details.
295              
296             B Only the numeric part is necessary - eg: 200, 301, 404, etc.
297              
298             =head2 Headers()
299              
300             Returns the L object that will be used for the outgoing response.
301              
302             If necessary, you can manipulate this object in any way you see fit.
303              
304             =head2 Declined
305              
306             For use within a L subclass, like this:
307              
308             sub run {
309             # Permit requests only every other second:
310             if( time() % 2 ) {
311             return $Response->Declined;
312             }
313             else {
314             $Response->Write("Try again");
315             return $Response->End;
316             }
317             }
318              
319             =head2 IsClientConnected
320              
321             In a ModPerl environment, this can be used to determine whether the client has
322             closed the connection (hit the "Stop" button or closed their browser). Useful within
323             a long-running loop.
324              
325             =head1 METHODS
326              
327             =head2 Write( $str )
328              
329             Adds C<$str> to the output buffer.
330              
331             =head2 Flush( )
332              
333             Causes the output buffer to be flushed to the client.
334              
335             =head2 End( )
336              
337             Aborts the current request.
338              
339             Example:
340              
341             # Good:
342             return $Response->End;
343              
344             Simply calling...
345              
346             # Bad!
347             $Response->End;
348              
349             ...will not work as intended.
350              
351             =head2 AddHeader( $name => $value )
352              
353             Adds a new header to the outgoing HTTP headers collection.
354              
355             =head2 SetCookie( %args )
356              
357             Adds a new cookie to the response.
358              
359             C<%args> B contain the following:
360              
361             =over 4
362              
363             =item * name
364              
365             A string - the name of the cookie.
366              
367             =item * value
368              
369             The value of the cookie.
370              
371             =back
372              
373             Other parameters are:
374              
375             =over 4
376              
377             =item * expires
378              
379             Can be in one of the following formats:
380              
381             =over 8
382              
383             =item * 30B
384              
385             Minutes - how many minutes from "now" calculated as C
386              
387             Example:
388              
389             expires => '30M'
390             expires => '-5M' # 5 minutes ago
391              
392             =item * 2B
393              
394             Hours - how many hours from "now" calculated as C
395              
396             Example:
397              
398             expires => '2H' # 2 hours
399             expires => '12H' # 12 Hours
400              
401             =item * 7B
402              
403             Days - how many days from "now" calculated as C
404              
405             Example:
406              
407             expires => '7D' # A week
408             expires => '30D' # A month
409              
410             =back
411              
412             =item * path
413              
414             Defaults to "C" - you can restrict the "path" that the cookie will apply to.
415              
416             =item * domain
417              
418             Defaults to whatever you set your config->data_connections->session->cookie_domain to
419             in your asp4-config.json. Otherwise defaults to C<$ENV{HTTP_HOST}>.
420              
421             You can override the defaults by passing in a domain, but the browser may not accept
422             other domains. See L for details.
423              
424             =back
425              
426             =head2 Redirect( $url )
427              
428             Causes the following HTTP header to be sent:
429              
430             Status: 301 Moved
431             Location: $url
432              
433             =head2 Include( $path [, \%args ] )
434              
435             Executes the ASP script at C<$path> and includes its output. Additional C<\%args>
436             may be passed along to the include.
437              
438             The passed-in args are accessible to the include like this:
439              
440             <%
441             my ($self, $context, $args) = @_;
442            
443             # Args is a hashref:
444             %>
445              
446             =head2 TrapInclude( $path [, \%args ] )
447              
448             Executes the ASP script at C<$path> and returns its output. Additional C<\%args>
449             may be passed along to the include.
450              
451             The passed-in args are accessible to the include like this:
452              
453             <%
454             my ($self, $context, $args) = @_;
455            
456             # Args is a hashref:
457             %>
458              
459             =cut
460