home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / ApacheHandler.pm < prev    next >
Encoding:
Text File  |  2003-12-12  |  32.0 KB  |  1,161 lines

  1. # Copyright (c) 1998-2003 by Jonathan Swartz. All rights reserved.
  2. # This program is free software; you can redistribute it and/or modify it
  3. # under the same terms as Perl itself.
  4.  
  5. use strict;
  6.  
  7. #----------------------------------------------------------------------
  8. #
  9. # APACHE-SPECIFIC REQUEST OBJECT
  10. #
  11. package HTML::Mason::Request::ApacheHandler;
  12.  
  13. use Apache::Constants qw( REDIRECT );
  14.  
  15. use HTML::Mason::Request;
  16. use Class::Container;
  17. use Params::Validate qw(BOOLEAN);
  18. Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } );
  19.  
  20. use base qw(HTML::Mason::Request);
  21.  
  22. use HTML::Mason::Exceptions( abbr => [qw(param_error error)] );
  23.  
  24. use constant OK         => 0;
  25. use constant DECLINED   => -1;
  26. use constant NOT_FOUND  => 404;
  27.  
  28. BEGIN
  29. {
  30.     my $ap_req_class = $mod_perl::VERSION < 1.99 ? 'Apache' : 'Apache::RequestRec';
  31.  
  32.     __PACKAGE__->valid_params
  33.     ( ah         => { isa => 'HTML::Mason::ApacheHandler',
  34.               descr => 'An ApacheHandler to handle web requests',
  35.               public => 0 },
  36.  
  37.       apache_req => { isa => $ap_req_class, default => undef,
  38.               descr => "An Apache request object",
  39.               public => 0 },
  40.  
  41.       cgi_object => { isa => 'CGI',    default => undef,
  42.               descr => "A CGI.pm request object",
  43.               public => 0 },
  44.  
  45.       auto_send_headers => { parse => 'boolean', type => BOOLEAN, default => 1,
  46.                  descr => "Whether HTTP headers should be auto-generated" },
  47.     );
  48. }
  49.  
  50. use HTML::Mason::MethodMaker
  51.     ( read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  52.               qw( ah apache_req auto_send_headers ) ] );
  53.  
  54. # A hack for subrequests
  55. sub _properties { qw(ah apache_req), shift->SUPER::_properties }
  56.  
  57. sub new
  58. {
  59.     my $class = shift;
  60.     my $self = $class->SUPER::new(@_);  # Magic!
  61.  
  62.     unless ($self->apache_req or $self->cgi_object)
  63.     {
  64.     param_error __PACKAGE__ . "->new: must specify 'apache_req' or 'cgi_object' parameter";
  65.     }
  66.  
  67.     return $self;
  68. }
  69.  
  70. # Override flush_buffer to also call $r->rflush
  71. sub flush_buffer
  72. {
  73.     my ($self) = @_;
  74.     $self->SUPER::flush_buffer;
  75.     $self->apache_req->rflush;
  76. }
  77.  
  78. sub cgi_object
  79. {
  80.     my ($self) = @_;
  81.  
  82.     error "Can't call cgi_object() unless 'args_method' is set to CGI.\n"
  83.     unless $self->ah->args_method eq 'CGI';
  84.  
  85.     if (defined($_[1])) {
  86.     $self->{cgi_object} = $_[1];
  87.     } else {
  88.     # We may not have created a CGI object if, say, request was a
  89.     # GET with no query string. Create one on the fly if necessary.
  90.     $self->{cgi_object} ||= CGI->new('');
  91.     }
  92.  
  93.     return $self->{cgi_object};
  94. }
  95.  
  96. #
  97. # Override this method to return NOT_FOUND when we get a
  98. # TopLevelNotFound exception. In case of POST we must trick
  99. # Apache into not reading POST content again. Wish there were
  100. # a more standardized way to do this...
  101. #
  102. sub exec
  103. {
  104.     my $self = shift;
  105.     my $r = $self->apache_req;
  106.     my $retval;
  107.  
  108.     if ( $self->is_subrequest )
  109.     {
  110.         # no need to go through all the rigamorale below for
  111.         # subrequests, and it may even break things to do so, since
  112.         # $r's print should only be redefined once.
  113.     eval { $retval = $self->SUPER::exec(@_) };
  114.     }
  115.     else
  116.     {
  117.         # ack, this has to be done at runtime to account for the fact
  118.         # that Apache::Filter changes $r's class and implements its
  119.         # own print() method.
  120.         my $real_apache_print = $r->can('print');
  121.  
  122.     # Remap $r->print to Mason's $m->print while executing
  123.     # request, but just for this $r, in case user does an internal
  124.     # redirect or apache subrequest.
  125.     local $^W = 0;
  126.     no strict 'refs';
  127.  
  128.         my $req_class = ref $r;
  129.     local *{"$req_class\::print"} = sub {
  130.         my $local_r = shift;
  131.         return $self->print(@_) if $local_r eq $r;
  132.         return $local_r->$real_apache_print(@_);
  133.     };
  134.     eval { $retval = $self->SUPER::exec(@_) };
  135.     }
  136.  
  137.     if ($@) {
  138.     if (isa_mason_exception($@, 'TopLevelNotFound')) {
  139.         # Log the error the same way that Apache does (taken from default_handler in http_core.c)
  140.         $r->log_error("[Mason] File does not exist: ", $r->filename . ($r->path_info ? $r->path_info : ""));
  141.         return $self->ah->return_not_found($r);
  142.     } else {
  143.         rethrow_exception $@;
  144.     }
  145.     }
  146.  
  147.     # On a success code, send headers if they have not been sent and
  148.     # if we are the top-level request. Since the out_method sends
  149.     # headers, this will typically only apply after $m->abort.
  150.     # On an error code, leave it to Apache to send the headers.
  151.     if (!$self->is_subrequest
  152.     and $self->auto_send_headers
  153.     and !HTML::Mason::ApacheHandler::http_header_sent($r)
  154.     and (!$retval or $retval==200)) {
  155.     $r->send_http_header();
  156.     }
  157.  
  158.     return defined($retval) ? $retval : OK;
  159. }
  160.  
  161. #
  162. # Override this method to always die when top level component is not found,
  163. # so we can return NOT_FOUND.
  164. #
  165. sub _handle_error
  166. {
  167.     my ($self, $err) = @_;
  168.  
  169.     if (isa_mason_exception($err, 'TopLevelNotFound')) {
  170.     rethrow_exception $err;
  171.     } else {
  172.         if ( $self->error_format eq 'html' ) {
  173.             $self->apache_req->content_type('text/html');
  174.         }
  175.     $self->SUPER::_handle_error($err);
  176.     }
  177. }
  178.  
  179. sub redirect
  180. {
  181.     my ($self, $url, $status) = @_;
  182.     my $r = $self->apache_req;
  183.  
  184.     $self->clear_buffer;
  185.     $r->method('GET');
  186.     $r->headers_in->unset('Content-length');
  187.     $r->err_header_out( Location => $url );
  188.     $self->abort($status || REDIRECT);
  189. }
  190.  
  191. #----------------------------------------------------------------------
  192. #
  193. # APACHE-SPECIFIC FILE RESOLVER OBJECT
  194. #
  195. package HTML::Mason::Resolver::File::ApacheHandler;
  196.  
  197. use strict;
  198.  
  199. use HTML::Mason::Tools qw(paths_eq);
  200.  
  201. use HTML::Mason::Resolver::File;
  202. use base qw(HTML::Mason::Resolver::File);
  203. use Params::Validate qw(SCALAR ARRAYREF);
  204.  
  205. BEGIN
  206. {
  207.     __PACKAGE__->valid_params
  208.     (
  209.      comp_root =>   # This is optional in superclass, but required for us.
  210.      { parse => 'list',
  211.        type => SCALAR|ARRAYREF,
  212.        descr => "A string or array of arrays indicating the search path for component calls" },
  213.     );
  214. }
  215.  
  216. #
  217. # Given an apache request object, return the associated component
  218. # path or undef if none exists. This is called for top-level web
  219. # requests that resolve to a particular file.
  220. #
  221. sub apache_request_to_comp_path {
  222.     my ($self, $r) = @_;
  223.  
  224.     my $file = $r->filename;
  225.     $file .= $r->path_info unless -f $file;
  226.  
  227.     # Clear up any weirdness here so that paths_eq compares two
  228.     # 'canonical' paths (canonpath is called on comp roots when
  229.     # resolver object is created.  Seems to be needed on Win32 (see
  230.     # bug #356).
  231.     $file = File::Spec->canonpath($file);
  232.  
  233.     foreach my $root (map $_->[1], $self->comp_root_array) {
  234.     if (paths_eq($root, substr($file, 0, length($root)))) {
  235.         my $path = substr($file, length $root);
  236.             $path = length $path ? join '/', File::Spec->splitdir($path) : '/';
  237.             chop $path if $path ne '/' && substr($path, -1) eq '/';
  238.  
  239.             return $path;
  240.     }
  241.     }
  242.     return undef;
  243. }
  244.  
  245.  
  246. #----------------------------------------------------------------------
  247. #
  248. # APACHEHANDLER OBJECT
  249. #
  250. package HTML::Mason::ApacheHandler;
  251.  
  252. use File::Path;
  253. use File::Spec;
  254. use HTML::Mason::Exceptions( abbr => [qw(param_error system_error error)] );
  255. use HTML::Mason::Interp;
  256. use HTML::Mason::Tools qw( load_pkg );
  257. use HTML::Mason::Utils;
  258. use Params::Validate qw(:all);
  259. Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } );
  260.  
  261. use Apache;
  262. use Apache::Constants qw( OK DECLINED NOT_FOUND );
  263.  
  264. # Require a reasonably modern mod_perl - should probably be later
  265. use mod_perl 1.22;
  266.  
  267. if ( $mod_perl::VERSION < 1.99 )
  268. {
  269.     error "mod_perl must be compiled with PERL_METHOD_HANDLERS=1 (or EVERYTHING=1) to use ", __PACKAGE__, "\n"
  270.     unless Apache::perl_hook('MethodHandlers');
  271. }
  272.  
  273. use vars qw($VERSION);
  274.  
  275. $VERSION = 1.69;
  276.  
  277. use Class::Container;
  278. use base qw(Class::Container);
  279.  
  280. BEGIN
  281. {
  282.     __PACKAGE__->valid_params
  283.     (
  284.      apache_status_title =>
  285.          { parse => 'string', type => SCALAR, default => 'HTML::Mason status',
  286.            descr => "The title of the Apache::Status page" },
  287.  
  288.      args_method =>
  289.          { parse => 'string',  type => SCALAR,       default => 'mod_perl',
  290.            regex => qr/^(?:CGI|mod_perl)$/,
  291.            descr => "Whether to use CGI.pm or Apache::Request for parsing the incoming HTTP request",
  292.          },
  293.  
  294.      decline_dirs =>
  295.          { parse => 'boolean', type => BOOLEAN, default => 1,
  296.            descr => "Whether Mason should decline to handle requests for directories" },
  297.  
  298.      # the only required param
  299.      interp =>
  300.          { isa => 'HTML::Mason::Interp',
  301.            descr => "A Mason interpreter for processing components" },
  302.     );
  303.  
  304.     __PACKAGE__->contained_objects
  305.     (
  306.      interp =>
  307.          { class => 'HTML::Mason::Interp',
  308.            descr => 'The interp class coordinates multiple objects to handle request execution'
  309.          },
  310.     );
  311. }
  312.  
  313. use HTML::Mason::MethodMaker
  314.     ( read_only  => [ 'args_method' ],
  315.       read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  316.               qw( apache_status_title
  317.               decline_dirs
  318.               interp ) ]
  319.     );
  320.  
  321. my ($STARTED);
  322.  
  323. # hack to let the make_params_pod.pl script work
  324. __PACKAGE__->_startup() if Apache->server;
  325. sub _startup
  326. {
  327.     my $pack = shift;
  328.     return if $STARTED++; # Allows a subclass to call us, without running twice
  329.  
  330.     if ( my $args_method = $pack->_get_string_param('MasonArgsMethod') )
  331.     {
  332.     if ($args_method eq 'CGI')
  333.     {
  334.         require CGI unless defined $CGI::VERSION;
  335.     }
  336.     elsif ($args_method eq 'mod_perl')
  337.     {
  338.         require Apache::Request unless defined $Apache::Request::VERSION;
  339.     }
  340.     }
  341. }
  342.  
  343. use constant
  344.     HAS_TABLE_API => $mod_perl::VERSION >= 1.99 || Apache::perl_hook('TableApi');
  345.  
  346. my %AH_BY_CONFIG;
  347. sub make_ah
  348. {
  349.     my ($package, $r) = @_;
  350.  
  351.     my $config = $r->dir_config;
  352.  
  353.     #
  354.     # If the user has virtual hosts, each with a different document
  355.     # root, then we will have to be called from the handler method.
  356.     # This means we have an active request.  In order to distinguish
  357.     # between virtual hosts with identical config directives that have
  358.     # no comp root defined (meaning they expect to use the default
  359.     # comp root), we append the document root for the current request
  360.     # to the key.
  361.     #
  362.     my $key =
  363.         ( join $;,
  364.           $r->document_root,
  365.           map { $_, HAS_TABLE_API ? sort $config->get($_) : $config->{$_} }
  366.           grep { /^Mason/ }
  367.           keys %$config
  368.         );
  369.  
  370.     return $AH_BY_CONFIG{$key} if exists $AH_BY_CONFIG{$key};
  371.  
  372.     my %p = $package->_get_mason_params($r);
  373.  
  374.     # can't use hash_list for this one because it's _either_ a string
  375.     # or a hash_list
  376.     if (exists $p{comp_root}) {
  377.     if (@{$p{comp_root}} == 1 && $p{comp_root}->[0] !~ /=>/) {
  378.         $p{comp_root} = $p{comp_root}[0];  # Convert to a simple string
  379.     } else {
  380.             my @roots;
  381.         foreach my $root (@{$p{comp_root}}) {
  382.         $root = [ split /\s*=>\s*/, $root, 2 ];
  383.         param_error "Configuration parameter MasonCompRoot must be either ".
  384.                             "a single string value or multiple key/value pairs ".
  385.                             "like 'foo => /home/mason/foo'.  Invalid parameter:\n$root"
  386.             unless defined $root->[1];
  387.  
  388.                 push @roots, $root;
  389.         }
  390.  
  391.             $p{comp_root} = \@roots;
  392.     }
  393.     }
  394.  
  395.     my $ah = $package->new(%p, $r);
  396.     $AH_BY_CONFIG{$key} = $ah if $key;
  397.  
  398.     return $ah;
  399. }
  400.  
  401. # The following routines handle getting information from $r->dir_config
  402.  
  403. sub calm_form {
  404.     # Transform from StudlyCaps to name_like_this
  405.     my ($self, $string) = @_;
  406.     $string =~ s/^Mason//;
  407.     $string =~ s/(^|.)([A-Z])/$1 ? "$1\L_$2" : "\L$2"/ge;
  408.     return $string;
  409. }
  410.  
  411. sub studly_form {
  412.     # Transform from name_like_this to StudlyCaps
  413.     my ($self, $string) = @_;
  414.     $string =~ s/(?:^|_)(\w)/\U$1/g;
  415.     return $string;
  416. }
  417.  
  418. sub _get_mason_params
  419. {
  420.     my $self = shift;
  421.     my $r = shift;
  422.  
  423.     my $config = $r ? $r->dir_config : Apache->server->dir_config;
  424.  
  425.     # Get all params starting with 'Mason'
  426.     my %candidates;
  427.  
  428.     foreach my $studly ( keys %$config )
  429.     {
  430.     (my $calm = $studly) =~ s/^Mason// or next;
  431.     $calm = $self->calm_form($calm);
  432.  
  433.     $candidates{$calm} = $config->{$studly};
  434.     }
  435.  
  436.     return unless %candidates;
  437.  
  438.     #
  439.     # We will accumulate all the string versions of the keys and
  440.     # values here for later use.
  441.     #
  442.     return ( map { $_ =>
  443.                    scalar $self->get_param( $_, \%candidates, $config, $r )
  444.                  }
  445.              keys %candidates );
  446. }
  447.  
  448. sub get_param {
  449.     # Gets a single config item from dir_config.
  450.  
  451.     my ($self, $key, $candidates, $config, $r) = @_;
  452.  
  453.     $key = $self->calm_form($key);
  454.  
  455.     my $spec = $self->allowed_params( $candidates || {} )->{$key}
  456.         or error "Unknown config item '$key'";
  457.  
  458.     # Guess the default parse type from the Params::Validate validation spec
  459.     my $type = ($spec->{parse} or
  460.         $spec->{type} & ARRAYREF ? 'list' :
  461.         $spec->{type} & SCALAR   ? 'string' :
  462.         $spec->{type} & CODEREF  ? 'code' :
  463.         undef)
  464.         or error "Unknown parse type for config item '$key'";
  465.  
  466.     my $method = "_get_${type}_param";
  467.     return $self->$method('Mason'.$self->studly_form($key), $config, $r);
  468. }
  469.  
  470. sub _get_string_param
  471. {
  472.     my $self = shift;
  473.     return scalar $self->_get_val(@_);
  474. }
  475.  
  476. sub _get_boolean_param
  477. {
  478.     my $self = shift;
  479.     return scalar $self->_get_val(@_);
  480. }
  481.  
  482. sub _get_code_param
  483. {
  484.     my $self = shift;
  485.     my $p = $_[0];
  486.     my $val = $self->_get_val(@_);
  487.  
  488.     return unless $val;
  489.  
  490.     my $sub_ref = eval $val;
  491.  
  492.     param_error "Configuration parameter '$p' is not valid perl:\n$@\n"
  493.     if $@;
  494.  
  495.     return $sub_ref;
  496. }
  497.  
  498. sub _get_list_param
  499. {
  500.     my $self = shift;
  501.     my @val = $self->_get_val(@_);
  502.     if (@val == 1 && ! defined $val[0])
  503.     {
  504.     @val = ();
  505.     }
  506.  
  507.     return \@val;
  508. }
  509.  
  510. sub _get_hash_list_param
  511. {
  512.     my $self = shift;
  513.     my @val = $self->_get_val(@_);
  514.     if (@val == 1 && ! defined $val[0])
  515.     {
  516.         return {};
  517.     }
  518.  
  519.     my %hash;
  520.     foreach my $pair (@val)
  521.     {
  522.         my ($key, $val) = split /\s*=>\s*/, $pair, 2;
  523.         param_error "Configuration parameter $_[0] must be a key/value pair ".
  524.                     qq|like "foo => 'bar'".  Invalid parameter:\n$pair|
  525.                 unless defined $key && defined $val;
  526.  
  527.         $hash{$key} = $val;
  528.     }
  529.  
  530.     return \%hash;
  531. }
  532.  
  533. sub _get_val
  534. {
  535.     my ($self, $p, $config, $r) = @_;
  536.  
  537.     my @val;
  538.     if (wantarray || !$config)
  539.     {
  540.         if ($config)
  541.         {
  542.             my $c = $r ? $r : Apache->server;
  543.             @val = HAS_TABLE_API ? $config->get($p) : $config->{$p};
  544.         }
  545.         else
  546.         {
  547.             my $c = $r ? $r : Apache->server;
  548.             @val = HAS_TABLE_API ? $c->dir_config->get($p) : $c->dir_config($p);
  549.         }
  550.     }
  551.     else
  552.     {
  553.         @val = exists $config->{$p} ? $config->{$p} : ();
  554.     }
  555.  
  556.     param_error "Only a single value is allowed for configuration parameter '$p'\n"
  557.     if @val > 1 && ! wantarray;
  558.  
  559.     return wantarray ? @val : $val[0];
  560. }
  561.  
  562. sub new
  563. {
  564.     my $class = shift;
  565.  
  566.     # Get $r off end of params if its there
  567.     my $r;
  568.     $r = pop() if @_ % 2;
  569.     my %params = @_;
  570.  
  571.     my %defaults;
  572.     $defaults{request_class}  = 'HTML::Mason::Request::ApacheHandler'
  573.         unless exists $params{request};
  574.     $defaults{resolver_class} = 'HTML::Mason::Resolver::File::ApacheHandler'
  575.         unless exists $params{resolver};
  576.  
  577.     my $allowed_params = $class->allowed_params(%defaults, %params);
  578.  
  579.     if ( exists $allowed_params->{comp_root} and
  580.      my $req = $r || Apache->request )  # DocumentRoot is only available inside requests
  581.     {
  582.     $defaults{comp_root} = $req->document_root;
  583.     }
  584.  
  585.     if (exists $allowed_params->{data_dir} and not exists $params{data_dir})
  586.     {
  587.     # constructs path to <server root>/mason
  588.     my $def = $defaults{data_dir} = Apache->server_root_relative('mason');
  589.     param_error "Default data_dir (MasonDataDir) '$def' must be an absolute path"
  590.         unless File::Spec->file_name_is_absolute($def);
  591.       
  592.     my @levels = File::Spec->splitdir($def);
  593.     param_error "Default data_dir (MasonDataDir) '$def' must be more than two levels deep (or must be set explicitly)"
  594.         if @levels <= 3;
  595.     }
  596.  
  597.     # Set default error_format based on error_mode
  598.     if (exists($params{error_mode}) and $params{error_mode} eq 'fatal') {
  599.     $defaults{error_format} = 'line';
  600.     } else {
  601.     $defaults{error_mode} = 'output';
  602.     $defaults{error_format} = 'html';
  603.     }
  604.  
  605.     # Push $r onto default allow_globals
  606.     if (exists $allowed_params->{allow_globals}) {
  607.     if ( $params{allow_globals} ) {
  608.         push @{ $params{allow_globals} }, '$r';
  609.     } else {
  610.         $defaults{allow_globals} = ['$r'];
  611.     }
  612.     }
  613.  
  614.     my $self = eval { $class->SUPER::new(%defaults, %params) };
  615.  
  616.     # We catch & throw this exception just to provide a better error message
  617.     if ( $@ && isa_mason_exception( $@, 'Params' ) && $@->message =~ /comp_root/ )
  618.     {
  619.     param_error "No comp_root specified and cannot determine DocumentRoot." .
  620.                     " Please provide comp_root explicitly.";
  621.     }
  622.     rethrow_exception $@;
  623.  
  624.     unless ( $self->interp->resolver->can('apache_request_to_comp_path') )
  625.     {
  626.     error "The resolver class your Interp object uses does not implement " .
  627.               "the 'apache_request_to_comp_path' method.  This means that ApacheHandler " .
  628.               "cannot resolve requests.  Are you using a handler.pl file created ".
  629.           "before version 1.10?  Please see the handler.pl sample " .
  630.               "that comes with the latest version of Mason.";
  631.     }
  632.  
  633.     # If we're running as superuser, change file ownership to http user & group
  634.     if (!($> || $<) && $self->interp->files_written)
  635.     {
  636.     chown Apache->server->uid, Apache->server->gid, $self->interp->files_written
  637.         or system_error( "Can't change ownership of files written by interp object: $!\n" );
  638.     }
  639.  
  640.     $self->_initialize;
  641.     return $self;
  642. }
  643.  
  644. # Register with Apache::Status at module startup.  Will get replaced
  645. # with a more informative status once an interpreter has been created.
  646. my $status_name = 'mason0001';
  647. if ( load_pkg('Apache::Status') )
  648. {
  649.     Apache::Status->menu_item
  650.     ($status_name => __PACKAGE__->allowed_params->{apache_status_title}{default},
  651.          sub { ["<b>(no interpreters created in this child yet)</b>"] });
  652. }
  653.  
  654.  
  655. sub _initialize {
  656.     my ($self) = @_;
  657.  
  658.     if ($self->args_method eq 'mod_perl') {
  659.     unless (defined $Apache::Request::VERSION) {
  660.         warn "Loading Apache::Request at runtime.  You could " .
  661.                  "increase shared memory between Apache processes by ".
  662.                  "preloading it in your httpd.conf or handler.pl file\n";
  663.         require Apache::Request;
  664.     }
  665.     } else {
  666.     unless (defined $CGI::VERSION) {
  667.         warn "Loading CGI at runtime.  You could increase shared ".
  668.                  "memory between Apache processes by preloading it in ".
  669.                  "your httpd.conf or handler.pl file\n";
  670.  
  671.         require CGI;
  672.     }
  673.     }
  674.  
  675.     # Add an HTML::Mason menu item to the /perl-status page.
  676.     if (defined $Apache::Status::VERSION) {
  677.     # A closure, carries a reference to $self
  678.     my $statsub = sub {
  679.         my ($r,$q) = @_; # request and CGI objects
  680.         return [] if !defined($r);
  681.  
  682.         if ($r->path_info and $r->path_info =~ /expire_code_cache=(.*)/) {
  683.         $self->interp->delete_from_code_cache($1);
  684.         }
  685.  
  686.         return ["<center><h2>" . $self->apache_status_title . "</h2></center>" ,
  687.             $self->status_as_html(apache_req => $r),
  688.             $self->interp->status_as_html(ah => $self, apache_req => $r)];
  689.     };
  690.     local $^W = 0; # to avoid subroutine redefined warnings
  691.     Apache::Status->menu_item($status_name, $self->apache_status_title, $statsub);
  692.     }
  693.  
  694.     my $interp = $self->interp;
  695.  
  696.     #
  697.     # Allow global $r in components
  698.     #
  699.     $interp->compiler->add_allowed_globals('$r')
  700.     if $interp->compiler->can('add_allowed_globals');
  701. }
  702.  
  703. # Generate HTML that describes ApacheHandler's current status.
  704. # This is used in things like Apache::Status reports.
  705.  
  706. sub status_as_html {
  707.     my ($self, %p) = @_;
  708.  
  709.     # Should I be scared about this?  =)
  710.  
  711.     my $comp_source = <<'EOF';
  712. <h3>ApacheHandler properties:</h3>
  713. <blockquote>
  714.  <tt>
  715. <table width="75%">
  716. <%perl>
  717. foreach my $property (sort keys %$ah) {
  718.     my $val = $ah->{$property};
  719.     my $default = ( defined $val && defined $valid{$property}{default} && $val eq $valid{$property}{default} ) || ( ! defined $val && exists $valid{$property}{default} && ! defined $valid{$property}{default} );
  720.  
  721.     my $display = $val;
  722.     if (ref $val) {
  723.         $display = '<font color="darkred">';
  724.         # only object can ->can, others die
  725.         my $is_object = eval { $val->can('anything'); 1 };
  726.         if ($is_object) {
  727.             $display .= ref $val . ' object';
  728.         } else {
  729.             if (UNIVERSAL::isa($val, 'ARRAY')) {
  730.                 $display .= 'ARRAY reference - [ ';
  731.                 $display .= join ', ', @$val;
  732.                 $display .= '] ';
  733.             } elsif (UNIVERSAL::isa($val, 'HASH')) {
  734.                 $display .= 'HASH reference - { ';
  735.                 my @pairs;
  736.                 while (my ($k, $v) = each %$val) {
  737.                    push @pairs, "$k => $v";
  738.                 }
  739.                 $display .= join ', ', @pairs;
  740.                 $display .= ' }';
  741.             } else {
  742.                 $display = ref $val . ' reference';
  743.             }
  744.         }
  745.         $display .= '</font>';
  746.     }
  747.  
  748.     defined $display && $display =~ s,([\x00-\x1F]),'<font color="purple">control-' . chr( ord('A') + ord($1) - 1 ) . '</font>',eg; # does this work for non-ASCII?
  749. </%perl>
  750.  <tr valign="top" cellspacing="10">
  751.   <td>
  752.     <% $property | h %>
  753.   </td>
  754.   <td>
  755.    <% defined $display ? $display : '<i>undef</i>' %>
  756.    <% $default ? '<font color=green>(default)</font>' : '' %>
  757.   </td>
  758.  </tr>
  759. % }
  760. </table>
  761.   </tt>
  762. </blockquote>
  763.  
  764. <%args>
  765.  $ah       # The ApacheHandler we'll elucidate
  766.  %valid    # Contains default values for member data
  767. </%args>
  768. EOF
  769.  
  770.     my $interp = $self->interp;
  771.     my $comp = $interp->make_component(comp_source => $comp_source);
  772.     my $out;
  773.  
  774.     $self->interp->make_request
  775.     ( comp => $comp,
  776.       args => [ah => $self, valid => $interp->allowed_params],
  777.       ah => $self,
  778.       apache_req => $p{apache_req},
  779.       out_method => \$out,
  780.     )->exec;
  781.  
  782.     return $out;
  783. }
  784.  
  785. sub handle_request
  786. {
  787.     my ($self, $r) = @_;
  788.  
  789.     my $req = $self->prepare_request($r);
  790.     return $req unless ref($req);
  791.  
  792.     return $req->exec;
  793. }
  794.  
  795. my $do_filter = sub { $_[0]->filter_register };
  796. my $no_filter = sub { $_[0] };
  797. sub prepare_request
  798. {
  799.     my $self = shift;
  800.  
  801.     my $r_sub = lc $_[0]->dir_config('Filter') eq 'on' ? $do_filter : $no_filter;
  802.  
  803.     # This gets the proper request object all in one fell swoop.  We
  804.     # don't want to copy it because if we do something like assign an
  805.     # Apache::Request object to a variable currently containing a
  806.     # plain Apache object, we leak memory.  This means we'd have to
  807.     # use multiple variables to avoid this, which is annoying.
  808.     my $r =
  809.         $r_sub->( $self->args_method eq 'mod_perl' ?
  810.                   Apache::Request->instance( $_[0] ) :
  811.                   $_[0]
  812.                 );
  813.  
  814.     my $interp = $self->interp;
  815.  
  816.     #
  817.     # If filename is a directory, then either decline or simply reset
  818.     # the content type, depending on the value of decline_dirs.
  819.     #
  820.     # ** We should be able to use $r->finfo here, but finfo is broken
  821.     # in some versions of mod_perl (e.g. see Shane Adams message on
  822.     # mod_perl list on 9/10/00)
  823.     #
  824.     my $is_dir = -d $r->filename;
  825.     my $is_file = -f _;
  826.  
  827.     if ($is_dir) {
  828.     if ($self->decline_dirs) {
  829.         return DECLINED;
  830.     } else {
  831.         $r->content_type(undef);
  832.     }
  833.     }
  834.  
  835.     #
  836.     # Compute the component path via the resolver. Return NOT_FOUND on failure.
  837.     #
  838.     my $comp_path = $interp->resolver->apache_request_to_comp_path($r);
  839.     unless ($comp_path) {
  840.     #
  841.     # Append path_info if filename does not represent an existing file
  842.     # (mainly for dhandlers).
  843.     #
  844.     my $pathname = $r->filename;
  845.     $pathname .= $r->path_info unless $is_file;
  846.  
  847.     warn "[Mason] Cannot resolve file to component: " .
  848.              "$pathname (is file outside component root?)";
  849.     return $self->return_not_found($r);
  850.     }
  851.  
  852.     my ($args, undef, $cgi_object) = $self->request_args($r);
  853.  
  854.     #
  855.     # Set up interpreter global variables.
  856.     #
  857.     $interp->set_global( r => $r );
  858.  
  859.     # If someone is using a custom request class that doesn't accept
  860.     # 'ah' and 'apache_req' that's their problem.
  861.     #
  862.     my $request = eval {
  863.         $interp->make_request( comp => $comp_path,
  864.                                args => [%$args],
  865.                                ah => $self,
  866.                                apache_req => $r,
  867.                              );
  868.     };
  869.     if (my $err = $@) {
  870.         # Mason doesn't currently throw any exceptions in the above, but some
  871.         # subclasses might. So be sure to handle them appropriately. We
  872.         # rethrow everything but TopLevelNotFound, Abort, and Decline errors.
  873.     if ( isa_mason_exception($err, 'TopLevelNotFound') ) {
  874.             # Return a 404.
  875.         $r->log_error("[Mason] File does not exist: ", $r->filename .
  876.                           ($r->path_info || ""));
  877.         return $self->return_not_found($r);
  878.     }
  879.         # Abort or decline.
  880.     my $retval = isa_mason_exception($err, 'Abort')   ? $err->aborted_value  :
  881.              isa_mason_exception($err, 'Decline') ? $err->declined_value :
  882.              rethrow_exception $err;
  883.         $r->send_http_header unless $retval and $retval != 200;
  884.     return $retval;
  885.     }
  886.  
  887.     my $final_output_method = ($r->method eq 'HEAD' ?
  888.                    sub {} :
  889.                    $r->can('print'));
  890.  
  891.     # Craft the request's out method to handle http headers, content
  892.     # length, and HEAD requests.
  893.     my $sent_headers = 0;
  894.     my $out_method = sub {
  895.  
  896.     # Send headers if they have not been sent by us or by user.
  897.         # We use instance here because if we store $request we get a
  898.         # circular reference and a big memory leak.
  899.     if (!$sent_headers and HTML::Mason::Request->instance->auto_send_headers) {
  900.         unless (http_header_sent($r)) {
  901.         $r->send_http_header();
  902.         }
  903.         $sent_headers = 1;
  904.     }
  905.  
  906.     # We could perhaps install a new, faster out_method here that
  907.     # wouldn't have to keep checking whether headers have been
  908.     # sent and what the $r->method is.  That would require
  909.     # additions to the Request interface, though.
  910.  
  911.     
  912.     # Call $r->print (using the real Apache method, not our
  913.     # overriden method).
  914.     $r->$final_output_method(grep {defined} @_);
  915.     };
  916.  
  917.     $request->out_method($out_method);
  918.  
  919.     $request->cgi_object($cgi_object) if $cgi_object;
  920.  
  921.     return $request;
  922. }
  923.  
  924. sub request_args
  925. {
  926.     my ($self, $r) = @_;
  927.  
  928.     #
  929.     # Get arguments from Apache::Request or CGI.
  930.     #
  931.     my ($args, $cgi_object);
  932.     if ($self->args_method eq 'mod_perl') {
  933.     $args = $self->_mod_perl_args($r);
  934.     } else {
  935.     $cgi_object = CGI->new;
  936.     $args = $self->_cgi_args($r, $cgi_object);
  937.     }
  938.  
  939.     # we return $r solely for backwards compatibility
  940.     return ($args, $r, $cgi_object);
  941. }
  942.  
  943. #
  944. # Get $args hashref via CGI package
  945. #
  946. sub _cgi_args
  947. {
  948.     my ($self, $r, $q) = @_;
  949.  
  950.     # For optimization, don't bother creating a CGI object if request
  951.     # is a GET with no query string
  952.     return {} if $r->method eq 'GET' && !scalar($r->args);
  953.  
  954.     return HTML::Mason::Utils::cgi_request_args($q, $r->method);
  955. }
  956.  
  957. #
  958. # Get $args hashref via Apache::Request package.
  959. #
  960. sub _mod_perl_args
  961. {
  962.     my ($self, $apr, $request) = @_;
  963.  
  964.     my %args;
  965.     foreach my $key ( $apr->param ) {
  966.     my @values = $apr->param($key);
  967.     $args{$key} = @values == 1 ? $values[0] : \@values;
  968.     }
  969.  
  970.     return \%args;
  971. }
  972.  
  973. #
  974. # Determines whether the http header has been sent.
  975. #
  976. sub http_header_sent { shift->header_out("Content-type") }
  977.  
  978. # Utility function to prepare $r before returning NOT_FOUND.
  979. sub return_not_found
  980. {
  981.     my ($self, $r) = @_;
  982.  
  983.     if ($r->method eq 'POST') {
  984.     $r->method('GET');
  985.     $r->headers_in->unset('Content-length');
  986.     }
  987.     return NOT_FOUND;
  988. }
  989.  
  990. #
  991. # PerlHandler HTML::Mason::ApacheHandler
  992. #
  993. BEGIN
  994. {
  995.     # A method handler is prototyped differently in mod_perl 1.x than in 2.x
  996.     my $handler_code = sprintf <<'EOF', $mod_perl::VERSION >= 1.99 ? ': method' : '($$)';
  997. sub handler %s
  998. {
  999.     my ($package, $r) = @_;
  1000.  
  1001.     my $ah;
  1002.     $ah ||= $package->make_ah($r);
  1003.  
  1004.     return $ah->handle_request($r);
  1005. }
  1006. EOF
  1007.     eval $handler_code;
  1008.     rethrow_exception $@;
  1009. }
  1010.  
  1011. 1;
  1012.  
  1013. __END__
  1014.  
  1015. =head1 NAME
  1016.  
  1017. HTML::Mason::ApacheHandler - Mason/mod_perl interface
  1018.  
  1019. =head1 SYNOPSIS
  1020.  
  1021.     use HTML::Mason::ApacheHandler;
  1022.  
  1023.     my $ah = HTML::Mason::ApacheHandler->new (..name/value params..);
  1024.     ...
  1025.     sub handler {
  1026.         my $r = shift;
  1027.         $ah->handle_request($r);
  1028.     }
  1029.  
  1030. =head1 DESCRIPTION
  1031.  
  1032. The ApacheHandler object links Mason to mod_perl, running components in
  1033. response to HTTP requests. It is controlled primarily through
  1034. parameters to the new() constructor.
  1035.  
  1036. handle_request() is not a user method, but rather is called from the
  1037. HTML::Mason::handler() routine in handler.pl.
  1038.  
  1039. =head1 PARAMETERS TO THE new() CONSTRUCTOR
  1040.  
  1041. =over
  1042.  
  1043. =item apache_status_title
  1044.  
  1045. Title that you want this ApacheHandler to appear as under
  1046. Apache::Status.  Default is "HTML::Mason status".  This is useful if
  1047. you create more than one ApacheHandler object and want them all
  1048. visible via Apache::Status.
  1049.  
  1050. =item args_method
  1051.  
  1052. Method to use for unpacking GET and POST arguments. The valid options
  1053. are 'CGI' and 'mod_perl'; these indicate that a C<CGI.pm> or
  1054. C<Apache::Request> object (respectively) will be created for the
  1055. purposes of argument handling.
  1056.  
  1057. 'mod_perl' is the default and requires that you have installed the
  1058. C<Apache::Request> package.
  1059.  
  1060. If args_method is 'mod_perl', the C<$r> global is upgraded to an
  1061. Apache::Request object. This object inherits all Apache methods and
  1062. adds a few of its own, dealing with parameters and file uploads.  See
  1063. C<Apache::Request> for more information.
  1064.  
  1065. If the args_method is 'CGI', the Mason request object (C<$m>) will have a
  1066. method called C<cgi_object> available.  This method returns the CGI
  1067. object used for argument processing.
  1068.  
  1069. While Mason will load C<Apache::Request> or C<CGI> as needed at runtime, it
  1070. is recommended that you preload the relevant module either in your
  1071. F<httpd.conf> or F<handler.pl> file, as this will save some memory.
  1072.  
  1073. =item decline_dirs
  1074.  
  1075. True or false, default is true. Indicates whether Mason should decline
  1076. directory requests, leaving Apache to serve up a directory index or a
  1077. C<FORBIDDEN> error as appropriate. See the L<allowing directory requests|HTML::Mason::Admin/allowing directory requests> section of the administrator's manual
  1078. for more information about handling directories with Mason.
  1079.  
  1080. =item interp
  1081.  
  1082. The interpreter object to associate with this compiler. By default a
  1083. new object of the specified L<interp_class|HTML::Mason::Params/interp_class> will be created.
  1084.  
  1085. =item interp_class
  1086.  
  1087. The class to use when creating a interpreter. Defaults to
  1088. L<HTML::Mason::Interp|HTML::Mason::Interp>.
  1089.  
  1090. =back
  1091.  
  1092. =head1 ACCESSOR METHODS
  1093.  
  1094. All of the above properties, except interp_class, have standard accessor
  1095. methods of the same name: no arguments retrieves the value, and one
  1096. argument sets it, except for args_method, which is not settable.  For
  1097. example:
  1098.  
  1099.     my $ah = HTML::Mason::ApacheHandler->new;
  1100.     my $decline_dirs = $ah->decline_dirs;
  1101.     $ah->decline_dirs(1);
  1102.  
  1103. =head1 OTHER METHODS
  1104.  
  1105. The ApacheHandler object has a few other publically accessible methods
  1106. that may be of interest to end users.
  1107.  
  1108. =over 4
  1109.  
  1110. =item handle_request ($r)
  1111.  
  1112. This method takes an Apache object representing a request and
  1113. translates that request into a form Mason can understand.  It's return
  1114. value is an Apache status code.
  1115.  
  1116. =item prepare_request ($r)
  1117.  
  1118. This method takes an Apache object representing a request and returns
  1119. a new Mason request object or an Apache status code.  If it is a
  1120. request object you can manipulate that object as you like, and then
  1121. call the request object's C<exec> method to have it generate output.
  1122.  
  1123. If this method returns an Apache status code, that means that it could
  1124. not create a Mason request object.
  1125.  
  1126. This method is useful if you would like to have a chance to decline a
  1127. request based on properties of the Mason request object or a component
  1128. object.  For example:
  1129.  
  1130.     my $req = $ah->prepare_request($r);
  1131.     # $req must be an Apache status code if it's not an object
  1132.     return $req unless ref($req);
  1133.  
  1134.     return DECLINED
  1135.         unless $req->request_comp->source_file =~ /\.html$/;
  1136.  
  1137.     $req->exec;
  1138.  
  1139. =item request_args ($r)
  1140.  
  1141. Given an Apache request object, this method returns a three item list.
  1142. The first item is a hash reference containing the arguments passed by
  1143. the client's request.
  1144.  
  1145. The second is an Apache request object.  This is returned for
  1146. backwards compatibility from when this method was responsible for
  1147. turning a plain Apache object into an Apache::Request object.
  1148.  
  1149. The third item may be a CGI.pm object or C<undef>, depending on the
  1150. value of the L<args_method|HTML::Mason::Params/args_method> parameter.
  1151.  
  1152. =back
  1153.  
  1154. =head1 SEE ALSO
  1155.  
  1156. L<HTML::Mason|HTML::Mason>,
  1157. L<HTML::Mason::Admin|HTML::Mason::Admin>,
  1158. L<HTML::Mason::Interp|HTML::Mason::Interp>
  1159.  
  1160. =cut
  1161.