File Coverage

blib/lib/HTTP/Server/Simple/Mason.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package HTTP::Server::Simple::Mason;
2 4     4   860420 use base qw/HTTP::Server::Simple::CGI/;
  4         11  
  4         4337  
3 4     4   88233 use strict;
  4         13  
  4         195  
4             our $VERSION = '0.14';
5              
6             =head1 NAME
7              
8             HTTP::Server::Simple::Mason - An abstract baseclass for a standalone mason server
9              
10              
11             =head1 SYNOPSIS
12              
13              
14             my $server = MyApp::Server->new();
15            
16             $server->run;
17            
18             package MyApp::Server;
19             use base qw/HTTP::Server::Simple::Mason/;
20            
21             sub mason_config {
22             return ( comp_root => '/tmp/mason-pages' );
23             }
24              
25             =head1 DESCRIPTION
26              
27              
28             =head1 INTERFACE
29              
30             See L and the documentation below.
31              
32             =cut
33              
34              
35              
36 4     4   6866 use HTML::Mason::CGIHandler;
  0            
  0            
37             use HTML::Mason::FakeApache;
38              
39             use Hook::LexWrap;
40              
41             our $http_header_sent = 0;
42              
43             wrap 'HTML::Mason::FakeApache::send_http_header', pre => sub {
44             my $r = shift;
45              
46             $http_header_sent = 1;
47             return if $r->http_header_sent;
48              
49             my $status = $r->header_out('Status') || '200 H::S::Mason OK';
50             print STDOUT "HTTP/1.0 $status\n";
51             };
52              
53             =head2 mason_handler
54              
55             Returns the server's C object. The first time
56             this method is called, it creates a new handler by calling C.
57              
58             =cut
59              
60             sub mason_handler {
61             my $self = shift;
62             $self->{'mason_handler'} ||= $self->new_handler;
63             return $self->{'mason_handler'};
64             }
65              
66             =head2 handle_request CGI
67              
68             Called with a CGI object. Invokes mason and runs the request
69              
70             =cut
71              
72             my %status_phrase = (
73             '100' => 'Continue',
74             '101' => 'Switching Protocols',
75             '200' => 'OK',
76             '201' => 'Created',
77             '202' => 'Accepted',
78             '203' => 'Non-Authoritative Information',
79             '204' => 'No Content',
80             '205' => 'Reset Content',
81             '206' => 'Partial Content',
82             '300' => 'Multiple Choices',
83             '301' => 'Moved Permanently',
84             '302' => 'Found',
85             '303' => 'See Other',
86             '304' => 'Not Modified',
87             '305' => 'Use Proxy',
88             '307' => 'Temporary Redirect',
89             '400' => 'Bad Request',
90             '401' => 'Unauthorized',
91             '402' => 'Payment Required',
92             '403' => 'Forbidden',
93             '404' => 'Not Found',
94             '405' => 'Method Not Allowed',
95             '406' => 'Not Acceptable',
96             '407' => 'Proxy Authentication Required',
97             '408' => 'Request Time-out',
98             '409' => 'Conflict',
99             '410' => 'Gone',
100             '411' => 'Length Required',
101             '412' => 'Precondition Failed',
102             '413' => 'Request Entity Too Large',
103             '414' => 'Request-URI Too Large',
104             '415' => 'Unsupported Media Type',
105             '416' => 'Requested range not satisfiable',
106             '417' => 'Expectation Failed',
107             '500' => 'Internal Server Error',
108             '501' => 'Not Implemented',
109             '502' => 'Bad Gateway',
110             '503' => 'Service Unavailable',
111             '504' => 'Gateway Time-out',
112             '505' => 'HTTP Version not supported',
113             );
114              
115             sub handle_request {
116             my $self = shift;
117             my $cgi = shift;
118              
119             local $http_header_sent = 0;
120              
121             my $m = $self->mason_handler;
122             unless ( $m->interp->comp_exists( $cgi->path_info ) ) {
123             my $path = $cgi->path_info;
124             $path .= '/' unless $path =~ m{/$};
125             $path .= 'index.html';
126             $cgi->path_info( $path )
127             if $m->interp->comp_exists( $path );
128             }
129              
130             local $@;
131             my $status = eval { $m->handle_cgi_object($cgi) };
132             if ( my $error = $@ ) {
133             return $self->handle_error($error);
134             }
135              
136             if ( $status && $http_header_sent ) {
137             warn "Request has been aborted or declined with status '$status'"
138             .", but it's too late as HTTP headers has been sent already"
139             unless $status =~ /^200(?:\s|$)/;
140             } elsif ( !$http_header_sent ) {
141             # we didn't send anything
142             # at this moment we can not use $m->cgi_request->send_headers
143              
144             $status ||= 204; # No Content
145             my ($code, $reason) = split /\s/, $status, 2;
146             $reason ||= $status_phrase{ $status } || 'No reason';
147             print STDOUT "HTTP/1.0 $status $reason\r\n";
148             print STDOUT "Content-Type: text/html; charset='UTF-8'\r\n";
149             print STDOUT "\r\n";
150             print STDOUT "$code: $reason\n";
151             }
152             return;
153             }
154              
155             =head2 handle_error ERROR
156              
157             If the call to C dies, C is called with the
158             exception (that is, C<$@>). By default, it does nothing; it can be overriden
159             by your subclass.
160              
161             =cut
162              
163             sub handle_error {
164             my $self = shift;
165              
166             return;
167             }
168              
169             =head2 new_handler
170              
171             Creates and returns a new C, with configuration
172             specified by the C and C methods.
173             You don't need to call this method yourself; C will automatically
174             call it the first time it is called.
175              
176             =cut
177              
178             sub new_handler {
179             my $self = shift;
180              
181             my $handler_class = $self->handler_class;
182              
183             my $handler = $handler_class->new(
184             $self->default_mason_config,
185             $self->mason_config,
186             # Override mason's default output method so
187             # we can change the binmode to our encoding if
188             # we happen to be handed character data instead
189             # of binary data.
190             #
191             # Cloned from HTML::Mason::CGIHandler
192             out_method => sub {
193             # We use instance here because if we store $request we get a
194             # circular reference and a big memory leak.
195             my $m = HTML::Mason::Request->instance;
196             my $r = $m->cgi_request;
197              
198             # Send headers if they have not been sent by us or by user.
199             $r->send_http_header unless $r->http_header_sent;
200              
201             # Set up a default
202             $r->content_type('text/html; charset=utf-8')
203             unless $r->content_type;
204              
205             if ( $r->content_type =~ /charset=([\w-]+)$/ ) {
206             my $enc = $1;
207             if ( lc $enc !~ /utf-?8$/ ) {
208             for my $str (@_) {
209             next unless $str;
210              
211             # only encode perl internal strings
212             next unless utf8::is_utf8($str);
213             $str = Encode::encode( $enc, $str );
214             }
215             }
216             }
217              
218             # default to utf8 encoding
219             for my $str (@_) {
220             next unless $str;
221             next unless utf8::is_utf8($str);
222             $str = Encode::encode( 'utf8', $str );
223             }
224              
225             # We could perhaps install a new, faster out_method here that
226             # wouldn't have to keep checking whether headers have been
227             # sent and what the $r->method is. That would require
228             # additions to the Request interface, though.
229             print STDOUT grep {defined} @_;
230             },
231             @_,
232             );
233              
234             $self->setup_escapes($handler);
235              
236             return ($handler);
237             }
238              
239             =head2 handler_class
240              
241             Returns the name of the Mason handler class invoked in C. Defaults
242             to L, but in your subclass you may wish to change it to a
243             subclass of L.
244              
245             =cut
246              
247             sub handler_class { "HTML::Mason::CGIHandler" }
248              
249             =head2 setup_escapes $handler
250              
251             Sets up the Mason escapes for the handler C<$handler>. For example, the C in
252              
253             <% $name | h %>
254              
255             By default, sets C to C
256             and C to C, but you can override this in your subclass.
257              
258             =cut
259              
260             sub setup_escapes {
261             my $self = shift;
262             my $handler = shift;
263              
264             $handler->interp->set_escape(
265             h => \&HTTP::Server::Simple::Mason::escape_utf8 );
266             $handler->interp->set_escape(
267             u => \&HTTP::Server::Simple::Mason::escape_uri );
268             return;
269             }
270              
271             =head2 mason_config
272              
273             Returns a subclass-defined mason handler configuration; you almost certainly want to override it
274             and specify at least C.
275              
276             =cut
277              
278             sub mason_config {
279             (); # user-defined
280             }
281              
282             =head2 default_mason_config
283              
284             Returns the default mason handler configuration (which can be overridden by entries in C).
285              
286             =cut
287              
288             sub default_mason_config {
289             (
290             default_escape_flags => 'h',
291              
292             # Turn off static source if we're in developer mode.
293             autoflush => 0
294             );
295             }
296              
297             # {{{ escape_utf8
298              
299             =head2 escape_utf8 SCALARREF
300              
301             does a css-busting but minimalist escaping of whatever html you're passing in.
302              
303             =cut
304              
305             sub escape_utf8 {
306             my $ref = shift;
307             my $val = $$ref;
308             use bytes;
309             $val =~ s/&/&/g;
310             $val =~ s/
311             $val =~ s/>/>/g;
312             $val =~ s/\(/(/g;
313             $val =~ s/\)/)/g;
314             $val =~ s/"/"/g;
315             $val =~ s/'/'/g;
316             $$ref = $val;
317             Encode::_utf8_on($$ref);
318              
319             }
320              
321             # }}}
322              
323             # {{{ escape_uri
324              
325             =head2 escape_uri SCALARREF
326              
327             Escapes URI component according to RFC2396
328              
329             =cut
330              
331             use Encode qw();
332              
333             sub escape_uri {
334             my $ref = shift;
335             $$ref = Encode::encode_utf8($$ref);
336             $$ref =~ s/([^a-zA-Z0-9_.!~*'()-])/uc sprintf("%%%02X", ord($1))/eg;
337             Encode::_utf8_on($$ref);
338             }
339              
340             # }}}
341              
342              
343              
344             =head1 CONFIGURATION AND ENVIRONMENT
345              
346             For most configuration, see L.
347              
348             You can (and must) configure your mason CGI handler by subclassing this module and overriding
349             the subroutine C. It's most important that you set a component root (where your pages live)
350             by adding
351              
352             comp_root => '/some/absolute/path'
353              
354             See the Synopsis section or C in the distribution for a complete example.
355              
356              
357             =head1 DEPENDENCIES
358              
359              
360             L
361             L
362              
363             =head1 INCOMPATIBILITIES
364              
365             None reported.
366              
367              
368             =head1 BUGS AND LIMITATIONS
369              
370              
371             Please report any bugs or feature requests to
372             C, or through the web interface at
373             L.
374              
375              
376             =head1 AUTHOR
377              
378             Jesse Vincent C<< >>
379              
380              
381             =head1 LICENCE AND COPYRIGHT
382              
383             Copyright (c) 2001-2005, Jesse Vincent C<< >>. All rights reserved.
384              
385             This module is free software; you can redistribute it and/or
386             modify it under the same terms as Perl itself.
387              
388              
389             =head1 DISCLAIMER OF WARRANTY
390              
391             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
392             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
393             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
394             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
395             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
396             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
397             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
398             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
399             NECESSARY SERVICING, REPAIR, OR CORRECTION.
400              
401             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
402             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
403             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
404             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
405             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
406             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
407             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
408             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
409             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
410             SUCH DAMAGES.
411              
412             =cut
413              
414              
415             1;