File Coverage

blib/lib/HTML/MasonX/ApacheLikePlackHandler.pm
Criterion Covered Total %
statement 101 388 26.0
branch 0 168 0.0
condition 1 80 1.2
subroutine 34 71 47.8
pod 1 11 9.0
total 137 718 19.0


line stmt bran cond sub pod time code
1             # -*- cperl-indent-level: 4; cperl-continued-brace-offset: -4; cperl-continued-statement-offset: 4 -*-
2              
3             # Copyright (c) 1998-2005 by Jonathan Swartz. All rights reserved.
4             # This program is free software; you can redistribute it and/or modify it
5             # under the same terms as Perl itself.
6              
7 1     1   873 use strict;
  1         2  
  1         40  
8 1     1   6 use warnings;
  1         2  
  1         68  
9              
10             package HTML::MasonX::ApacheLikePlackHandler;
11             $HTML::MasonX::ApacheLikePlackHandler::VERSION = '0.02';
12             #----------------------------------------------------------------------
13             #
14             # APACHE-SPECIFIC REQUEST OBJECT
15             #
16             package HTML::MasonX::Request::ApacheLikePlackHandler;
17             $HTML::MasonX::Request::ApacheLikePlackHandler::VERSION = '0.02';
18 1     1   1300 use HTML::Mason::Request;
  1         141898  
  1         45  
19 1     1   13 use Class::Container;
  1         3  
  1         21  
20 1     1   5 use Params::Validate qw(BOOLEAN);
  1         3  
  1         101  
21             Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } );
22              
23 1     1   5 use base qw(HTML::Mason::Request);
  1         2  
  1         95  
24              
25 1     1   5 use HTML::Mason::Exceptions( abbr => [qw(param_error error)] );
  1         1  
  1         9  
26              
27 1     1   59 use constant OK => 0;
  1         2  
  1         56  
28 1     1   6 use constant HTTP_OK => 200;
  1         1  
  1         39  
29 1     1   5 use constant DECLINED => -1;
  1         2  
  1         35  
30 1     1   4 use constant NOT_FOUND => 404;
  1         2  
  1         32  
31 1     1   5 use constant REDIRECT => 302;
  1         1  
  1         140  
32              
33             my $APACHE2_REQUEST_CLASS;
34             my $APACHE2_REQUEST_INSTANCE_CLASS;
35             my $APACHE2_STATUS_CLASS;
36             my $APACHE2_SERVERUTIL_CLASS;
37             BEGIN {
38 1     1   5 my %_name_to_var = (
39             APACHE2_REQUEST => \$APACHE2_REQUEST_CLASS,
40             APACHE2_REQUEST_INSTANCE => \$APACHE2_REQUEST_INSTANCE_CLASS,
41             APACHE2_STATUS => \$APACHE2_STATUS_CLASS,
42             APACHE2_SERVERUTIL => \$APACHE2_SERVERUTIL_CLASS,
43             );
44              
45 1         3 for my $key (keys %_name_to_var) {
46 4         20 my $env_name = sprintf 'HTML_MASONX_APACHELIKEPLACKHANDLER_MOCK_%s_CLASS', $key;
47 4   50     15 ${$_name_to_var{$key}} = $ENV{$env_name} || die "You need to set \$ENV{$env_name} to a mock class";
  4         80  
48             }
49             }
50              
51             BEGIN
52             {
53 1     1   19 __PACKAGE__->valid_params
54             ( ah => { isa => 'HTML::MasonX::ApacheLikePlackHandler',
55             descr => 'An ApacheHandler to handle web requests',
56             public => 0 },
57              
58             apache_req => { isa => $APACHE2_REQUEST_INSTANCE_CLASS, default => undef,
59             descr => "An Apache request object",
60             public => 0 },
61              
62             cgi_object => { isa => 'CGI', default => undef,
63             descr => "A CGI.pm request object",
64             public => 0 },
65              
66             auto_send_headers => { parse => 'boolean', type => BOOLEAN, default => 1,
67             descr => "Whether HTTP headers should be auto-generated" },
68             );
69             }
70              
71             use HTML::Mason::MethodMaker
72 1         2 ( read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  3         120  
73 1     1   73 qw( ah apache_req auto_send_headers ) ] );
  1         2  
74              
75             # A hack for subrequests
76 0     0   0 sub _properties { qw(ah apache_req), shift->SUPER::_properties }
77              
78             sub new
79             {
80 0     0   0 my $class = shift;
81 0         0 my $self = $class->SUPER::new(@_); # Magic!
82              
83 0 0 0     0 unless ($self->apache_req or $self->cgi_object)
84             {
85 0         0 param_error __PACKAGE__ . "->new: must specify 'apache_req' or 'cgi_object' parameter";
86             }
87              
88             # Record a flag indicating whether the user passed a custom out_method
89 0         0 my %params = @_;
90 0         0 $self->ah->{has_custom_out_method} = exists $params{out_method};
91              
92 0         0 return $self;
93             }
94              
95             sub cgi_object
96             {
97 0     0   0 my ($self) = @_;
98              
99 0 0       0 error "Can't call cgi_object() unless 'args_method' is set to CGI.\n"
100             unless $self->ah->args_method eq 'CGI';
101              
102 0 0       0 if (defined($_[1])) {
103 0         0 $self->{cgi_object} = $_[1];
104             } else {
105             # We may not have created a CGI object if, say, request was a
106             # GET with no query string. Create one on the fly if necessary.
107 0   0     0 $self->{cgi_object} ||= CGI->new('');
108             }
109              
110 0         0 return $self->{cgi_object};
111             }
112              
113             #
114             # Override this method to return NOT_FOUND when we get a
115             # TopLevelNotFound exception. In case of POST we must trick
116             # Apache into not reading POST content again. Wish there were
117             # a more standardized way to do this...
118             #
119             sub exec
120             {
121 0     0   0 my $self = shift;
122 0         0 my $r = $self->apache_req;
123 0         0 my $retval;
124              
125 0 0       0 if ( $self->is_subrequest )
126             {
127             # no need to go through all the rigamorale below for
128             # subrequests, and it may even break things to do so, since
129             # $r's print should only be redefined once.
130 0         0 $retval = $self->SUPER::exec(@_);
131             }
132             else
133             {
134             # ack, this has to be done at runtime to account for the fact
135             # that Apache::Filter changes $r's class and implements its
136             # own print() method.
137 0         0 my $real_apache_print = $r->can('print');
138              
139             # Remap $r->print to Mason's $m->print while executing
140             # request, but just for this $r, in case user does an internal
141             # redirect or apache subrequest.
142 0         0 local $^W = 0;
143 1     1   369 no strict 'refs';
  1         2  
  1         44  
144              
145 0         0 my $req_class = ref $r;
146 1     1   10 no warnings 'redefine';
  1         2  
  1         310  
147 0         0 local *{"$req_class\::print"} = sub {
148 0     0   0 my $local_r = shift;
149 0 0       0 return $self->print(@_) if $local_r eq $r;
150 0         0 return $local_r->$real_apache_print(@_);
151 0         0 };
152 0         0 $retval = $self->SUPER::exec(@_);
153             }
154              
155             # mod_perl 1 treats HTTP_OK and OK the same, but mod_perl-2 does not.
156 0 0 0     0 return defined $retval && $retval ne HTTP_OK ? $retval : OK;
157             }
158              
159             #
160             # Override this method to always die when top level component is not found,
161             # so we can return NOT_FOUND.
162             #
163             sub _handle_error
164             {
165 0     0   0 my ($self, $err) = @_;
166              
167 0 0       0 if (isa_mason_exception($err, 'TopLevelNotFound')) {
168 0         0 rethrow_exception $err;
169             } else {
170 0 0       0 if ( $self->error_format eq 'html' ) {
171 0         0 $self->apache_req->content_type('text/html');
172             }
173 0         0 $self->SUPER::_handle_error($err);
174             }
175             }
176              
177             sub redirect
178             {
179 0     0   0 my ($self, $url, $status) = @_;
180 0         0 my $r = $self->apache_req;
181              
182 0         0 $r->method('GET');
183 0         0 $r->headers_in->unset('Content-length');
184 0         0 $r->err_headers_out->{Location} = $url;
185 0   0     0 $self->clear_and_abort($status || REDIRECT);
186             }
187              
188             #----------------------------------------------------------------------
189             #
190             # APACHEHANDLER OBJECT
191             #
192             package HTML::MasonX::ApacheLikePlackHandler;
193              
194 1     1   5 use File::Path;
  1         2  
  1         65  
195 1     1   6 use File::Spec;
  1         1  
  1         29  
196 1     1   4 use HTML::Mason::Exceptions( abbr => [qw(param_error system_error error)] );
  1         2  
  1         5  
197 1     1   1322 use HTML::Mason::Interp;
  1         82874  
  1         95  
198 1     1   25 use HTML::Mason::Tools qw( load_pkg );
  1         2  
  1         114  
199 1     1   6 use HTML::Mason::Utils;
  1         3  
  1         48  
200 1     1   41 use Params::Validate qw(:all);
  1         2  
  1         428  
201             Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } );
202              
203 1     1   6 use constant OK => 0;
  1         2  
  1         99  
204 1     1   5 use constant HTTP_OK => 200;
  1         2  
  1         51  
205 1     1   6 use constant DECLINED => -1;
  1         3  
  1         42  
206 1     1   5 use constant NOT_FOUND => 404;
  1         1  
  1         56  
207 1     1   5 use constant REDIRECT => 302;
  1         2  
  1         40  
208              
209 1     1   6 use base qw(HTML::Mason::Handler);
  1         2  
  1         1335  
210              
211             BEGIN
212             {
213 1     1   691 __PACKAGE__->valid_params
214             (
215             apache_status_title =>
216             { parse => 'string', type => SCALAR, default => 'HTML::Mason status',
217             descr => "The title of the Apache::Status page" },
218              
219             args_method =>
220             { parse => 'string', type => SCALAR,
221             default => 'CGI',
222             regex => qr/^(?:CGI|mod_perl)$/,
223             descr => "Whether to use CGI.pm or Apache::Request for parsing the incoming HTTP request",
224             },
225              
226             decline_dirs =>
227             { parse => 'boolean', type => BOOLEAN, default => 1,
228             descr => "Whether Mason should decline to handle requests for directories" },
229              
230             # the only required param
231             interp =>
232             { isa => 'HTML::Mason::Interp',
233             descr => "A Mason interpreter for processing components" },
234             );
235              
236 1         50 __PACKAGE__->contained_objects
237             (
238             interp =>
239             { class => 'HTML::Mason::Interp',
240             descr => 'The interp class coordinates multiple objects to handle request execution'
241             },
242             );
243             }
244              
245             use HTML::Mason::MethodMaker
246 3         127 ( read_only => [ 'args_method' ],
247 1         4 read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
248             qw( apache_status_title
249             decline_dirs
250             interp ) ]
251 1     1   75 );
  1         2  
252              
253             sub _get_apache_server
254             {
255 1     1   23 return $APACHE2_SERVERUTIL_CLASS->server();
256             }
257              
258             my ($STARTED);
259              
260             # The "if _get_apache_server" bit is a hack to let this module load
261             # when not under mod_perl, which is needed to generate Params.pod
262             __PACKAGE__->_startup() if eval { _get_apache_server };
263             sub _startup
264             {
265 0     0     my $pack = shift;
266 0 0         return if $STARTED++; # Allows a subclass to call this method without running it twice
267              
268 0 0         if ( my $args_method = $pack->_get_string_param('MasonArgsMethod') )
269             {
270 0 0         if ($args_method eq 'CGI')
    0          
271             {
272 0 0         eval { require CGI unless defined CGI->VERSION; };
  0            
273             # mod_perl2 does not warn about this, so somebody should
274 0 0         if (CGI->VERSION < 3.08) {
275 0           die "CGI version 3.08 is required to support mod_perl2 API";
276             }
277 0 0         die $@ if $@;
278             }
279             elsif ( $args_method eq 'mod_perl' )
280             {
281 0 0         eval "require $APACHE2_REQUEST_CLASS" unless defined $APACHE2_REQUEST_CLASS->VERSION;
282             }
283             }
284             }
285              
286             # Register with Apache::Status at module startup. Will get replaced
287             # with a more informative status once an interpreter has been created.
288             my $status_name = 'mason0001';
289             if ( load_pkg($APACHE2_STATUS_CLASS) )
290             {
291             $APACHE2_STATUS_CLASS->menu_item
292             ($status_name => __PACKAGE__->allowed_params->{apache_status_title}{default},
293             sub { ["(no interpreters created in this child yet)"] });
294             }
295              
296              
297             my %AH_BY_CONFIG;
298             sub make_ah
299             {
300 0     0 0   my ($package, $r) = @_;
301              
302 0           my $config = $r->dir_config;
303              
304             #
305             # If the user has virtual hosts, each with a different document
306             # root, then we will have to be called from the handler method.
307             # This means we have an active request. In order to distinguish
308             # between virtual hosts with identical config directives that have
309             # no comp root defined (meaning they expect to use the default
310             # comp root), we append the document root for the current request
311             # to the key.
312             #
313 0           my $key =
314             ( join $;,
315             $r->document_root,
316 0           map { $_, sort $config->get($_) }
317 0           grep { /^Mason/ }
318             keys %$config
319             );
320              
321 0 0         return $AH_BY_CONFIG{$key} if exists $AH_BY_CONFIG{$key};
322              
323 0           my %p = $package->_get_mason_params($r);
324              
325             # can't use hash_list for this one because it's _either_ a string
326             # or a hash_list
327 0 0         if (exists $p{comp_root}) {
328 0 0 0       if (@{$p{comp_root}} == 1 && $p{comp_root}->[0] !~ /=>/) {
  0            
329 0           $p{comp_root} = $p{comp_root}[0]; # Convert to a simple string
330             } else {
331 0           my @roots;
332 0           foreach my $root (@{$p{comp_root}}) {
  0            
333 0           $root = [ split /\s*=>\s*/, $root, 2 ];
334 0 0         param_error "Configuration parameter MasonCompRoot must be either ".
335             "a single string value or multiple key/value pairs ".
336             "like 'foo => /home/mason/foo'. Invalid parameter:\n$root"
337             unless defined $root->[1];
338              
339 0           push @roots, $root;
340             }
341              
342 0           $p{comp_root} = \@roots;
343             }
344             }
345              
346 0           my $ah = $package->new(%p, $r);
347 0 0         $AH_BY_CONFIG{$key} = $ah if $key;
348              
349 0           return $ah;
350             }
351              
352             # The following routines handle getting information from $r->dir_config
353              
354             sub calm_form {
355             # Transform from StudlyCaps to name_like_this
356 0     0 0   my ($self, $string) = @_;
357 0           $string =~ s/^Mason//;
358 0 0         $string =~ s/(^|.)([A-Z])/$1 ? "$1\L_$2" : "\L$2"/ge;
  0            
359 0           return $string;
360             }
361              
362             sub studly_form {
363             # Transform from name_like_this to StudlyCaps
364 0     0 0   my ($self, $string) = @_;
365 0           $string =~ s/(?:^|_)(\w)/\U$1/g;
366 0           return $string;
367             }
368              
369             sub _get_mason_params
370             {
371 0     0     my $self = shift;
372 0           my $r = shift;
373              
374 0 0         my $config = $r ? $r->dir_config : _get_apache_server->dir_config;
375              
376             # Get all params starting with 'Mason'
377 0           my %candidates;
378              
379 0           foreach my $studly ( keys %$config )
380             {
381 0 0         (my $calm = $studly) =~ s/^Mason// or next;
382 0           $calm = $self->calm_form($calm);
383              
384 0           $candidates{$calm} = $config->{$studly};
385             }
386              
387 0 0         return unless %candidates;
388              
389             #
390             # We will accumulate all the string versions of the keys and
391             # values here for later use.
392             #
393 0           return ( map { $_ =>
  0            
394             scalar $self->_get_param( $_, \%candidates, $config, $r )
395             }
396             keys %candidates );
397             }
398              
399             sub _get_param {
400             # Gets a single config item from dir_config.
401              
402 0     0     my ($self, $key, $candidates, $config, $r) = @_;
403              
404 0           $key = $self->calm_form($key);
405              
406 0 0 0       my $spec = $self->allowed_params( $candidates || {} )->{$key}
407             or error "Unknown config item '$key'";
408              
409             # Guess the default parse type from the Params::Validate validation spec
410 0 0 0       my $type = ($spec->{parse} or
411             $spec->{type} & ARRAYREF ? 'list' :
412             $spec->{type} & SCALAR ? 'string' :
413             $spec->{type} & CODEREF ? 'code' :
414             undef)
415             or error "Unknown parse type for config item '$key'";
416              
417 0           my $method = "_get_${type}_param";
418 0           return $self->$method('Mason'.$self->studly_form($key), $config, $r);
419             }
420              
421             sub _get_string_param
422             {
423 0     0     my $self = shift;
424 0           return scalar $self->_get_val(@_);
425             }
426              
427             sub _get_boolean_param
428             {
429 0     0     my $self = shift;
430 0           return scalar $self->_get_val(@_);
431             }
432              
433             sub _get_code_param
434             {
435 0     0     my $self = shift;
436 0           my $p = $_[0];
437 0           my $val = $self->_get_val(@_);
438              
439 0 0         return unless $val;
440              
441 0           my $sub_ref = eval $val;
442              
443 0 0         param_error "Configuration parameter '$p' is not valid perl:\n$@\n"
444             if $@;
445              
446 0           return $sub_ref;
447             }
448              
449             sub _get_list_param
450             {
451 0     0     my $self = shift;
452 0           my @val = $self->_get_val(@_);
453 0 0 0       if (@val == 1 && ! defined $val[0])
454             {
455 0           @val = ();
456             }
457              
458 0           return \@val;
459             }
460              
461             sub _get_hash_list_param
462             {
463 0     0     my $self = shift;
464 0           my @val = $self->_get_val(@_);
465 0 0 0       if (@val == 1 && ! defined $val[0])
466             {
467 0           return {};
468             }
469              
470 0           my %hash;
471 0           foreach my $pair (@val)
472             {
473 0           my ($key, $val) = split /\s*=>\s*/, $pair, 2;
474 0 0 0       param_error "Configuration parameter $_[0] must be a key/value pair ".
475             qq|like "foo => bar". Invalid parameter:\n$pair|
476             unless defined $key && defined $val;
477              
478 0           $hash{$key} = $val;
479             }
480              
481 0           return \%hash;
482             }
483              
484             sub _get_val
485             {
486 0     0     my ($self, $p, $config, $r) = @_;
487              
488 0           my @val;
489 0 0 0       if (wantarray || !$config)
490             {
491 0 0         if ($config)
492             {
493 0           @val = $config->get($p);
494             }
495             else
496             {
497 0 0         my $c = $r ? $r : _get_apache_server;
498 0           @val = $c->dir_config->get($p);
499             }
500             }
501             else
502             {
503 0 0         @val = exists $config->{$p} ? $config->{$p} : ();
504             }
505              
506 0 0 0       param_error "Only a single value is allowed for configuration parameter '$p'\n"
507             if @val > 1 && ! wantarray;
508              
509 0 0         return wantarray ? @val : $val[0];
510             }
511              
512             sub new
513             {
514 0     0 1   my $class = shift;
515              
516             # Get $r off end of params if its there
517 0           my $r;
518 0 0         $r = pop() if @_ % 2;
519 0           my %params = @_;
520              
521 0           my %defaults;
522 0 0         $defaults{request_class} = 'HTML::MasonX::Request::ApacheLikePlackHandler'
523             unless exists $params{request};
524              
525 0           my $allowed_params = $class->allowed_params(%defaults, %params);
526              
527 0 0 0       if ( exists $allowed_params->{comp_root} and
528             my $req = $r ) # DocumentRoot is only available inside requests
529             {
530 0           $defaults{comp_root} = $req->document_root;
531             }
532              
533 0 0 0       if (exists $allowed_params->{data_dir} and not exists $params{data_dir})
534             {
535             # constructs path to /mason
536 0 0         if (UNIVERSAL::can($APACHE2_SERVERUTIL_CLASS,'server_root')) {
537 1     1   2016 no strict 'refs';
  1         2  
  1         2552  
538 0           $defaults{data_dir} = File::Spec->catdir(&{"$APACHE2_SERVERUTIL_CLASS\::server_root"}(),'mason');
  0            
539             } else {
540 0           $defaults{data_dir} = Apache->server_root_relative('mason');
541             }
542 0           my $def = $defaults{data_dir};
543 0 0         param_error "Default data_dir (MasonDataDir) '$def' must be an absolute path"
544             unless File::Spec->file_name_is_absolute($def);
545            
546 0           my @levels = File::Spec->splitdir($def);
547 0 0         param_error "Default data_dir (MasonDataDir) '$def' must be more than two levels deep (or must be set explicitly)"
548             if @levels <= 3;
549             }
550              
551             # Set default error_format based on error_mode
552 0 0 0       if (exists($params{error_mode}) and $params{error_mode} eq 'fatal') {
553 0           $defaults{error_format} = 'line';
554             } else {
555 0           $defaults{error_mode} = 'output';
556 0           $defaults{error_format} = 'html';
557             }
558              
559             # Push $r onto default allow_globals
560 0 0         if (exists $allowed_params->{allow_globals}) {
561 0 0         if ( $params{allow_globals} ) {
562 0           push @{ $params{allow_globals} }, '$r';
  0            
563             } else {
564 0           $defaults{allow_globals} = ['$r'];
565             }
566             }
567              
568 0           my $self = eval { $class->SUPER::new(%defaults, %params) };
  0            
569              
570             # We catch this exception just to provide a better error message
571 0 0 0       if ( $@ && isa_mason_exception( $@, 'Params' ) && $@->message =~ /comp_root/ )
      0        
572             {
573 0           param_error "No comp_root specified and cannot determine DocumentRoot." .
574             " Please provide comp_root explicitly.";
575             }
576 0           rethrow_exception $@;
577              
578 0 0         unless ( $self->interp->resolver->can('apache_request_to_comp_path') )
579             {
580 0           error "The resolver class your Interp object uses does not implement " .
581             "the 'apache_request_to_comp_path' method. This means that ApacheHandler " .
582             "cannot resolve requests. Are you using a handler.pl file created ".
583             "before version 1.10? Please see the handler.pl sample " .
584             "that comes with the latest version of Mason.";
585             }
586              
587             # If we're running as superuser, change file ownership to http user & group
588 0 0 0       if (!($> || $<) && $self->interp->files_written)
      0        
589             {
590 0 0         chown $self->get_uid_gid, $self->interp->files_written
591             or system_error( "Can't change ownership of files written by interp object: $!\n" );
592             }
593              
594 0           $self->_initialize;
595 0           return $self;
596             }
597              
598             sub get_uid_gid
599             {
600             # Apache2 lacks $s->uid.
601             # Workaround by searching the config tree.
602 0     0 0   die "The wrapper layer using the Apache2::Directive class is unimplemented";
603              
604 0           my $conftree = Apache2::Directive::conftree();
605 0           my $user = $conftree->lookup('User');
606 0           my $group = $conftree->lookup('Group');
607              
608 0           $user =~ s/^["'](.*)["']$/$1/;
609 0           $group =~ s/^["'](.*)["']$/$1/;
610              
611 0 0         my $uid = $user ? getpwnam($user) : $>;
612 0 0         my $gid = $group ? getgrnam($group) : $);
613              
614 0           return ($uid, $gid);
615             }
616              
617             sub _initialize {
618 0     0     my ($self) = @_;
619              
620 0 0         if ($self->args_method eq 'mod_perl') {
621 0 0         unless (defined $APACHE2_REQUEST_CLASS->VERSION) {
622 0           warn "Loading $APACHE2_REQUEST_CLASS at runtime. You could " .
623             "increase shared memory between Apache processes by ".
624             "preloading it in your httpd.conf or handler.pl file\n";
625 0           eval "require $APACHE2_REQUEST_CLASS";
626             }
627             } else {
628 0 0         unless (defined CGI->VERSION) {
629 0           warn "Loading CGI at runtime. You could increase shared ".
630             "memory between Apache processes by preloading it in ".
631             "your httpd.conf or handler.pl file\n";
632              
633 0           require CGI;
634             }
635             }
636              
637             # Add an HTML::Mason menu item to the /perl-status page.
638 0 0         if (defined $APACHE2_STATUS_CLASS->VERSION) {
639             # A closure, carries a reference to $self
640             my $statsub = sub {
641 0     0     my ($r,$q) = @_; # request and CGI objects
642 0 0         return [] if !defined($r);
643              
644 0 0 0       if ($r->path_info and $r->path_info =~ /expire_code_cache=(.*)/) {
645 0           $self->interp->delete_from_code_cache($1);
646             }
647              
648 0           return ["

" . $self->apache_status_title . "

" ,
649             $self->status_as_html(apache_req => $r),
650             $self->interp->status_as_html(ah => $self, apache_req => $r)];
651 0           };
652 0           local $^W = 0; # to avoid subroutine redefined warnings
653 0           $APACHE2_STATUS_CLASS->menu_item($status_name, $self->apache_status_title, $statsub);
654             }
655              
656 0           my $interp = $self->interp;
657              
658             #
659             # Allow global $r in components
660             #
661             # This is somewhat redundant with code in new, but seems to be
662             # needed since the user may simply create their own interp.
663             #
664 0 0         $interp->compiler->add_allowed_globals('$r')
665             if $interp->compiler->can('add_allowed_globals');
666             }
667              
668             # Generate HTML that describes ApacheHandler's current status.
669             # This is used in things like Apache::Status reports.
670              
671             sub status_as_html {
672 0     0 0   my ($self, %p) = @_;
673              
674             # Should I be scared about this? =)
675              
676 0           my $comp_source = <<'EOF';
677            

ApacheHandler properties:

678            
679            
680            
681             <%perl>
682             foreach my $property (sort keys %$ah) {
683             my $val = $ah->{$property};
684             my $default = ( defined $val && defined $valid{$property}{default} && $val eq $valid{$property}{default} ) || ( ! defined $val && exists $valid{$property}{default} && ! defined $valid{$property}{default} );
685              
686             my $display = $val;
687             if (ref $val) {
688             $display = '';
689             # only object can ->can, others die
690             my $is_object = eval { $val->can('anything'); 1 };
691             if ($is_object) {
692             $display .= ref $val . ' object';
693             } else {
694             if (UNIVERSAL::isa($val, 'ARRAY')) {
695             $display .= 'ARRAY reference - [ ';
696             $display .= join ', ', @$val;
697             $display .= '] ';
698             } elsif (UNIVERSAL::isa($val, 'HASH')) {
699             $display .= 'HASH reference - { ';
700             my @pairs;
701             while (my ($k, $v) = each %$val) {
702             push @pairs, "$k => $v";
703             }
704             $display .= join ', ', @pairs;
705             $display .= ' }';
706             } else {
707             $display = ref $val . ' reference';
708             }
709             }
710             $display .= '';
711             }
712              
713             defined $display && $display =~ s,([\x00-\x1F]),'control-' . chr( ord('A') + ord($1) - 1 ) . '',eg; # does this work for non-ASCII?
714            
715            
716            
717             <% $property | h %>
718            
719            
720             <% defined $display ? $display : 'undef' %>
721             <% $default ? '(default)' : '' %>
722            
723            
724             % }
725            
726            
727            
728              
729             <%args>
730             $ah # The ApacheHandler we'll elucidate
731             %valid # Contains default values for member data
732            
733             EOF
734              
735 0           my $interp = $self->interp;
736 0           my $comp = $interp->make_component(comp_source => $comp_source);
737 0           my $out;
738              
739 0           $self->interp->make_request
740             ( comp => $comp,
741             args => [ah => $self, valid => $interp->allowed_params],
742             ah => $self,
743             apache_req => $p{apache_req},
744             out_method => \$out,
745             )->exec;
746              
747 0           return $out;
748             }
749              
750             sub handle_request
751             {
752 0     0 0   my ($self, $r) = @_;
753              
754 0           my $req = $self->prepare_request($r);
755 0 0         return $req unless ref($req);
756              
757 0           return $req->exec;
758             }
759              
760             sub prepare_request
761             {
762 0     0 0   my $self = shift;
763              
764 0           my $r = $self->_apache_request_object(@_);
765              
766 0           my $interp = $self->interp;
767              
768 0           my $fs_type = $self->_request_fs_type($r);
769              
770 0 0 0       return DECLINED if $fs_type eq 'dir' && $self->decline_dirs;
771              
772             #
773             # Compute the component path via the resolver. Return NOT_FOUND on failure.
774             #
775 0           my $comp_path = $interp->resolver->apache_request_to_comp_path($r, $interp->comp_root_array);
776 0 0         unless ($comp_path) {
777             #
778             # Append path_info if filename does not represent an existing file
779             # (mainly for dhandlers).
780             #
781 0           my $pathname = $r->filename;
782 0 0         $pathname .= $r->path_info unless $fs_type eq 'file';
783              
784 0           warn "[Mason] Cannot resolve file to component: " .
785             "$pathname (is file outside component root?)";
786 0           return $self->return_not_found($r);
787             }
788              
789 0           my ($args, undef, $cgi_object) = $self->request_args($r);
790              
791             #
792             # Set up interpreter global variables.
793             #
794 0           $interp->set_global( r => $r );
795              
796             # If someone is using a custom request class that doesn't accept
797             # 'ah' and 'apache_req' that's their problem.
798             #
799 0           my $m = eval {
800 0           $interp->make_request( comp => $comp_path,
801             args => [%$args],
802             ah => $self,
803             apache_req => $r,
804             );
805             };
806              
807 0 0         if (my $err = $@) {
808             # We rethrow everything but TopLevelNotFound, Abort, and Decline errors.
809            
810 0 0         if ( isa_mason_exception($@, 'TopLevelNotFound') ) {
811 0   0       $r->log_error("[Mason] File does not exist: ", $r->filename . ($r->path_info || ""));
812 0           return $self->return_not_found($r);
813             }
814 0 0         my $retval = ( isa_mason_exception($err, 'Abort') ? $err->aborted_value :
    0          
815             isa_mason_exception($err, 'Decline') ? $err->declined_value :
816             rethrow_exception $err );
817 0 0 0       $retval = OK if defined $retval && $retval eq HTTP_OK;
818 0 0         unless ($retval) {
819 0 0         unless ($r->notes('mason-sent-headers')) {
820 0           $r->send_http_header();
821             }
822             }
823 0           return $retval;
824             }
825              
826 0 0         $self->_set_mason_req_out_method($m, $r) unless $self->{has_custom_out_method};
827              
828 0 0 0       $m->cgi_object($cgi_object) if $m->can('cgi_object') && $cgi_object;
829              
830 0           return $m;
831             }
832              
833             my $do_filter = sub { $_[0]->filter_register };
834             my $no_filter = sub { $_[0] };
835             sub _apache_request_object
836             {
837 0     0     my $self = shift;
838              
839             # We need to be careful to never assign a new apache (subclass)
840             # object to $r or we will leak memory, at least with mp1.
841 0           my $new_r = $_[0];
842              
843 0           my $r_sub;
844 0           my $filter = $_[0]->dir_config('Filter');
845 0 0 0       if ( defined $filter && lc $filter eq 'on' )
846             {
847 0 0         die "To use Apache::Filter with Mason you must have at least version 1.021 of Apache::Filter\n"
848             unless Apache::Filter->VERSION >= 1.021;
849              
850 0           $r_sub = $do_filter;
851             }
852             else
853             {
854 0           $r_sub = $no_filter;
855             }
856              
857             my $apreq_instance =
858 0     0     sub { $APACHE2_REQUEST_CLASS->new( $_[0] ) };
  0            
859              
860             return
861 0 0         $r_sub->( $self->args_method eq 'mod_perl' ?
862             $apreq_instance->( $new_r ) :
863             $new_r
864             );
865             }
866              
867             sub _request_fs_type
868             {
869 0     0     my ($self, $r) = @_;
870              
871             #
872             # If filename is a directory, then either decline or simply reset
873             # the content type, depending on the value of decline_dirs.
874             #
875             # ** We should be able to use $r->finfo here, but finfo is broken
876             # in some versions of mod_perl (e.g. see Shane Adams message on
877             # mod_perl list on 9/10/00)
878             #
879 0           my $is_dir = -d $r->filename;
880              
881 0 0         return $is_dir ? 'dir' : -f _ ? 'file' : 'other';
    0          
882             }
883              
884             sub request_args
885             {
886 0     0 0   my ($self, $r) = @_;
887              
888             #
889             # Get arguments from Apache::Request or CGI.
890             #
891 0           my ($args, $cgi_object);
892 0 0         if ($self->args_method eq 'mod_perl') {
893 0           $args = $self->_mod_perl_args($r);
894             } else {
895 0           $cgi_object = CGI->new;
896 0           $args = $self->_cgi_args($r, $cgi_object);
897             }
898              
899             # we return $r solely for backwards compatibility
900 0           return ($args, $r, $cgi_object);
901             }
902              
903             #
904             # Get $args hashref via CGI package
905             #
906             sub _cgi_args
907             {
908 0     0     my ($self, $r, $q) = @_;
909              
910             # For optimization, don't bother creating a CGI object if request
911             # is a GET with no query string
912 0 0 0       return {} if $r->method eq 'GET' && !scalar($r->args);
913              
914 0           return HTML::Mason::Utils::cgi_request_args($q, $r->method);
915             }
916              
917             #
918             # Get $args hashref via Apache::Request package.
919             #
920             sub _mod_perl_args
921             {
922 0     0     my ($self, $apr) = @_;
923              
924 0           my %args;
925 0           foreach my $key ( $apr->param ) {
926 0           my @values = $apr->param($key);
927 0 0         $args{$key} = @values == 1 ? $values[0] : \@values;
928             }
929              
930 0           return \%args;
931             }
932              
933             sub _set_mason_req_out_method
934             {
935 0     0     my ($self, $m, $r) = @_;
936              
937             my $final_output_method = ($r->method eq 'HEAD' ?
938 0     0     sub {} :
939 0 0         $r->can('print'));
940              
941             # Craft the request's out method to handle http headers, content
942             # length, and HEAD requests.
943 0           my $out_method;
944             {
945             # mod_perl-2 does not need to call $r->send_http_headers
946 0           $out_method = sub {
947 0     0     $r->$final_output_method( grep { defined } @_ );
  0            
948 0           $r->rflush;
949 0           };
950             }
951              
952 0           $m->out_method($out_method);
953             }
954              
955             # Utility function to prepare $r before returning NOT_FOUND.
956             sub return_not_found
957             {
958 0     0 0   my ($self, $r) = @_;
959              
960 0 0         if ($r->method eq 'POST') {
961 0           $r->method('GET');
962 0           $r->headers_in->unset('Content-length');
963             }
964 0           return NOT_FOUND;
965             }
966              
967             #
968             # PerlHandler HTML::MasonX::ApacheLikePlackHandler
969             #
970             sub handler
971             {
972 0     0 0   my ($package, $r) = @_;
973              
974 0           my $ah;
975 0   0       $ah ||= $package->make_ah($r);
976              
977 0           return $ah->handle_request($r);
978             }
979              
980             1;
981              
982             __END__