| MasonX-Apache2Handler documentation | Contained in the MasonX-Apache2Handler distribution. |
MasonX::Apache2Handler - experimental (alpha) Mason/mod_perl2 interface
use MasonX::Apache2Handler;
my $ah = MasonX::Apache2Handler->new (..name/value params..);
...
sub handler {
my $r = shift;
$ah->handle_request($r);
}
MasonX::Apache2Handler is highly experimental ( alpha ) and should only be used in a test environment.
MasonX::Apache2Handler is a clone of HTML::Mason::ApacheHandler changed to work under a pure mod_perl2 environment. The external interface is unchanged, see HTML::Mason::ApacheHandler (ApacheHandler).
The actual changes I made can be found in the distribution in diff/ApacheHandler.diff ( made with 'diff -Naru' ... ).
A HOTWO for MasonX::Apache2Handler may be found at HOWTO Run Mason with mod_perl2 (Mason-with-mod_perl2).
You must have the following packages installed:
mod_perl => 1.9910
HTML::Mason' => 1.25
libapreq2 => 2.02-dev
Please refer to the original packages' documentation for instructions.
My documents, including: HOWTO Run Mason with mod_perl2 (Mason-with-mod_perl2), MasonX::Request::WithApache2Session (WithApache2Session), MasonX::Request::WithMulti2Session (WithMulti2Session),
Original Mason documents, including: HTML::Mason::ApacheHandler (ApacheHandler), MasonX::Request::WithApacheSession (WithApacheSession), MasonX::Request::WithMultiSession (WithMultiSession).
Also see the Mason documentation at http://masonhq.com/docs/manual/.
Beau E. Cox <beau@beaucox.com> http://beaucox.com.
The real authors (I just made mod_perl2 changes) are the Mason crew, including: Jonathan Swartz <swartz@pobox.com>, Dave Rolsky <autarch@urth.org>, Ken Williams <ken@mathforum.org>.
Version 0.05 as of April, 2004.
| MasonX-Apache2Handler documentation | Contained in the MasonX-Apache2Handler distribution. |
#------------------------------------------------------------------------ # Changes to HTML::Mason::ApacheHandler for Apache2/mod_perl 2. # # Beau E. Cox <beau@beaucox.com> # April 2004 # # Changes (C)Copyright 2004 Beau E. Cox. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. #------------------------------------------------------------------------ # Copyright (c) 1998-2003 by Jonathan Swartz. All rights reserved. # This program is free software; you can redistribute it and/or modify it # under the same terms as Perl itself. use strict; #---------------------------------------------------------------------- # # APACHE-SPECIFIC REQUEST OBJECT # package MasonX::Request::Apache2Handler; use Apache::Const -compile => qw( REDIRECT ); use MasonX::Request2; use Class::Container; use Params::Validate qw(BOOLEAN); Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } ); use base qw(MasonX::Request2); use HTML::Mason::Exceptions( abbr => [qw(param_error error)] ); use constant OK => 0; use constant DECLINED => -1; use constant NOT_FOUND => 404; if ( $mod_perl::VERSION < 1.99 ) { error "you must use mod_perl 2 (version >= 1.99)", __PACKAGE__, "\n"; } BEGIN { __PACKAGE__->valid_params ( ah => { isa => 'MasonX::Apache2Handler', descr => 'An Apache2Handler to handle web requests', public => 0 }, apache_req => { isa => 'Apache::RequestRec', default => undef, descr => "An Apache request object", public => 0 }, cgi_object => { isa => 'CGI', default => undef, descr => "A CGI.pm request object", public => 0 }, auto_send_headers => { parse => 'boolean', type => BOOLEAN, default => 1, descr => "Whether HTTP headers should be auto-generated" }, ); } use HTML::Mason::MethodMaker ( read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] } qw( ah apache_req auto_send_headers ) ] ); # A hack for subrequests sub _properties { qw(ah apache_req), shift->SUPER::_properties } sub new { my $class = shift; my $self = $class->SUPER::new(@_); # Magic! unless ($self->apache_req or $self->cgi_object) { param_error __PACKAGE__ . "->new: must specify 'apache_req' or 'cgi_object' parameter"; } return $self; } # Override flush_buffer to also call $r->rflush sub flush_buffer { my ($self) = @_; # Only call rflush if flush_buffer returns a true value. # (return implemented in MasonX::Request2 and MasonX::Buffer2.) $self->SUPER::flush_buffer and $self->apache_req->rflush; } sub cgi_object { my ($self) = @_; error "Can't call cgi_object() unless 'args_method' is set to CGI.\n" unless $self->ah->args_method eq 'CGI'; if (defined($_[1])) { $self->{cgi_object} = $_[1]; } else { # We may not have created a CGI object if, say, request was a # GET with no query string. Create one on the fly if necessary. $self->{cgi_object} ||= CGI->new(''); } return $self->{cgi_object}; } # # Override this method to return NOT_FOUND when we get a # TopLevelNotFound exception. In case of POST we must trick # Apache into not reading POST content again. Wish there were # a more standardized way to do this... # sub exec { my $self = shift; my $r = $self->apache_req; my $retval; if ( $self->is_subrequest ) { # no need to go through all the rigamorale below for # subrequests, and it may even break things to do so, since # $r's print should only be redefined once. eval { $retval = $self->SUPER::exec(@_) }; } else { # ack, this has to be done at runtime to account for the fact # that Apache::Filter changes $r's class and implements its # own print() method. my $real_apache_print = $r->can('print'); # Remap $r->print to Mason's $m->print while executing # request, but just for this $r, in case user does an internal # redirect or apache subrequest. local $^W = 0; no strict 'refs'; my $req_class = ref $r; local *{"$req_class\::print"} = sub { my $local_r = shift; return $self->print(@_) if $local_r eq $r; return $local_r->$real_apache_print(@_); }; eval { $retval = $self->SUPER::exec(@_) }; } if ($@) { if (isa_mason_exception($@, 'TopLevelNotFound')) { # Log the error the same way that Apache does (taken from default_handler in http_core.c) $r->log_error("[Mason] File does not exist: ", $r->filename . ($r->path_info ? $r->path_info : "")); return $self->ah->return_not_found($r); } else { rethrow_exception $@; } } # On a success code, send headers if they have not been sent and # if we are the top-level request. Since the out_method sends # headers, this will typically only apply after $m->abort. # On an error code, leave it to Apache to send the headers. # not needed in mod_per2 (??) if (!$self->is_subrequest and $self->auto_send_headers and !MasonX::Apache2Handler::http_header_sent($r) and (!$retval or $retval==200)) { #$r->send_http_header(); } return defined($retval) ? $retval : OK; } # # Override this method to always die when top level component is not found, # so we can return NOT_FOUND. # sub _handle_error { my ($self, $err) = @_; if (isa_mason_exception($err, 'TopLevelNotFound')) { rethrow_exception $err; } else { if ( $self->error_format eq 'html' ) { $self->apache_req->content_type('text/html'); } $self->SUPER::_handle_error($err); } } sub redirect { my ($self, $url, $status) = @_; my $r = $self->apache_req; $self->clear_buffer; $r->method('GET'); $r->headers_in->unset('Content-length'); $r->err_headers_out->{ Location } = $url; $self->abort($status || Apache::REDIRECT); } #---------------------------------------------------------------------- # # APACHE-SPECIFIC FILE RESOLVER OBJECT # package MasonX::Resolver::File::Apache2Handler; use strict; use HTML::Mason::Tools qw(paths_eq); use HTML::Mason::Resolver::File; use base qw(HTML::Mason::Resolver::File); use Params::Validate qw(SCALAR ARRAYREF); BEGIN { __PACKAGE__->valid_params ( comp_root => # This is optional in superclass, but required for us. { parse => 'list', type => SCALAR|ARRAYREF, descr => "A string or array of arrays indicating the search path for component calls" }, ); } # # Given an apache request object, return the associated component # path or undef if none exists. This is called for top-level web # requests that resolve to a particular file. # sub apache_request_to_comp_path { my ($self, $r) = @_; my $file = $r->filename; $file .= $r->path_info unless -f $file; # Clear up any weirdness here so that paths_eq compares two # 'canonical' paths (canonpath is called on comp roots when # resolver object is created. Seems to be needed on Win32 (see # bug #356). $file = File::Spec->canonpath($file); foreach my $root (map $_->[1], $self->comp_root_array) { if (paths_eq($root, substr($file, 0, length($root)))) { my $path = substr($file, length $root); $path = length $path ? join '/', File::Spec->splitdir($path) : '/'; chop $path if $path ne '/' && substr($path, -1) eq '/'; return $path; } } return undef; } #---------------------------------------------------------------------- # # APACHEHANDLER OBJECT # package MasonX::Apache2Handler; use File::Path; use File::Spec; use HTML::Mason::Exceptions( abbr => [qw(param_error system_error error)] ); use HTML::Mason::Interp; use HTML::Mason::Tools qw( load_pkg ); use HTML::Mason::Utils; use Params::Validate qw(:all); Params::Validate::validation_options( on_fail => sub { param_error( join '', @_ ) } ); use Apache::Const -compile => qw( OK DECLINED NOT_FOUND ); use APR; use Apache::ServerUtil; # Require a mod_perl 2 use mod_perl 1.99; if ( $mod_perl::VERSION < 1.99 ) { error "you must use mod_perl 2 (version >= 1.99)", __PACKAGE__, "\n"; } use vars qw($VERSION); $VERSION = 0.05; use Class::Container; use base qw(Class::Container); BEGIN { __PACKAGE__->valid_params ( apache_status_title => { parse => 'string', type => SCALAR, default => 'HTML::Mason status', descr => "The title of the Apache::Status page" }, args_method => { parse => 'string', type => SCALAR, default => 'mod_perl', regex => qr/^(?:CGI|mod_perl)$/, descr => "Whether to use CGI.pm or Apache::Request for parsing the incoming HTTP request", }, decline_dirs => { parse => 'boolean', type => BOOLEAN, default => 1, descr => "Whether Mason should decline to handle requests for directories" }, # the only required param interp => { isa => 'HTML::Mason::Interp', descr => "A Mason interpreter for processing components" }, ); __PACKAGE__->contained_objects ( interp => { class => 'HTML::Mason::Interp', descr => 'The interp class coordinates multiple objects to handle request execution' }, ); } use HTML::Mason::MethodMaker ( read_only => [ 'args_method' ], read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] } qw( apache_status_title decline_dirs interp ) ] ); my ($STARTED); # hack to let the make_params_pod.pl script work __PACKAGE__->_startup() if Apache->server; sub _startup { my $pack = shift; return if $STARTED++; # Allows a subclass to call us, without running twice if ( my $args_method = $pack->_get_string_param('MasonArgsMethod') ) { if ($args_method eq 'CGI') { require CGI unless defined $CGI::VERSION; } elsif ($args_method eq 'mod_perl') { require Apache::Request unless defined $Apache::Request::VERSION; } } } use constant HAS_TABLE_API => $mod_perl::VERSION >= 1.99; my %AH_BY_CONFIG; sub make_ah { my ($package, $r) = @_; my $config = $r->dir_config; # # If the user has virtual hosts, each with a different document # root, then we will have to be called from the handler method. # This means we have an active request. In order to distinguish # between virtual hosts with identical config directives that have # no comp root defined (meaning they expect to use the default # comp root), we append the document root for the current request # to the key. # my $key = ( join $;, $r->document_root, map { $_, HAS_TABLE_API ? sort $config->get($_) : $config->{$_} } grep { /^Mason/ } keys %$config ); return $AH_BY_CONFIG{$key} if exists $AH_BY_CONFIG{$key}; my %p = $package->_get_mason_params($r); # can't use hash_list for this one because it's _either_ a string # or a hash_list if (exists $p{comp_root}) { if (@{$p{comp_root}} == 1 && $p{comp_root}->[0] !~ /=>/) { $p{comp_root} = $p{comp_root}[0]; # Convert to a simple string } else { my @roots; foreach my $root (@{$p{comp_root}}) { $root = [ split /\s*=>\s*/, $root, 2 ]; param_error "Configuration parameter MasonCompRoot must be either ". "a single string value or multiple key/value pairs ". "like 'foo => /home/mason/foo'. Invalid parameter:\n$root" unless defined $root->[1]; push @roots, $root; } $p{comp_root} = \@roots; } } my $ah = $package->new(%p, $r); $AH_BY_CONFIG{$key} = $ah if $key; return $ah; } # The following routines handle getting information from $r->dir_config sub calm_form { # Transform from StudlyCaps to name_like_this my ($self, $string) = @_; $string =~ s/^Mason//; $string =~ s/(^|.)([A-Z])/$1 ? "$1\L_$2" : "\L$2"/ge; return $string; } sub studly_form { # Transform from name_like_this to StudlyCaps my ($self, $string) = @_; $string =~ s/(?:^|_)(\w)/\U$1/g; return $string; } sub _get_mason_params { my $self = shift; my $r = shift; my $config = $r ? $r->dir_config : Apache->server->dir_config; # Get all params starting with 'Mason' my %candidates; foreach my $studly ( keys %$config ) { (my $calm = $studly) =~ s/^Mason// or next; $calm = $self->calm_form($calm); $candidates{$calm} = $config->{$studly}; } return unless %candidates; # # We will accumulate all the string versions of the keys and # values here for later use. # return ( map { $_ => scalar $self->get_param( $_, \%candidates, $config, $r ) } keys %candidates ); } sub get_param { # Gets a single config item from dir_config. my ($self, $key, $candidates, $config, $r) = @_; $key = $self->calm_form($key); my $spec = $self->allowed_params( $candidates || {} )->{$key} or error "Unknown config item '$key'"; # Guess the default parse type from the Params::Validate validation spec my $type = ($spec->{parse} or $spec->{type} & ARRAYREF ? 'list' : $spec->{type} & SCALAR ? 'string' : $spec->{type} & CODEREF ? 'code' : undef) or error "Unknown parse type for config item '$key'"; my $method = "_get_${type}_param"; return $self->$method('Mason'.$self->studly_form($key), $config, $r); } sub _get_string_param { my $self = shift; return scalar $self->_get_val(@_); } sub _get_boolean_param { my $self = shift; return scalar $self->_get_val(@_); } sub _get_code_param { my $self = shift; my $p = $_[0]; my $val = $self->_get_val(@_); return unless $val; my $sub_ref = eval $val; param_error "Configuration parameter '$p' is not valid perl:\n$@\n" if $@; return $sub_ref; } sub _get_list_param { my $self = shift; my @val = $self->_get_val(@_); if (@val == 1 && ! defined $val[0]) { @val = (); } return \@val; } sub _get_hash_list_param { my $self = shift; my @val = $self->_get_val(@_); if (@val == 1 && ! defined $val[0]) { return {}; } my %hash; foreach my $pair (@val) { my ($key, $val) = split /\s*=>\s*/, $pair, 2; param_error "Configuration parameter $_[0] must be a key/value pair ". qq|like "foo => 'bar'". Invalid parameter:\n$pair| unless defined $key && defined $val; $hash{$key} = $val; } return \%hash; } sub _get_val { my ($self, $p, $config, $r) = @_; my @val; if (wantarray || !$config) { if ($config) { my $c = $r ? $r : Apache->server; @val = HAS_TABLE_API ? $config->get($p) : $config->{$p}; } else { my $c = $r ? $r : Apache->server; @val = HAS_TABLE_API ? $c->dir_config->get($p) : $c->dir_config($p); } } else { @val = exists $config->{$p} ? $config->{$p} : (); } param_error "Only a single value is allowed for configuration parameter '$p'\n" if @val > 1 && ! wantarray; return wantarray ? @val : $val[0]; } sub new { my $class = shift; # Get $r off end of params if its there my $r; $r = pop() if @_ % 2; my %params = @_; my %defaults; $defaults{request_class} = 'MasonX::Request::Apache2Handler' unless exists $params{request}; $defaults{resolver_class} = 'MasonX::Resolver::File::Apache2Handler' unless exists $params{resolver}; my $allowed_params = $class->allowed_params(%defaults, %params); if ( exists $allowed_params->{comp_root} and my $req = $r ) # DocumentRoot is only available inside requests { $defaults{comp_root} = $req->document_root; }
if (exists $allowed_params->{data_dir} and not exists $params{data_dir})
{
# constructs path to <server root>/mason
my $def = $defaults{data_dir} = Apache->server->server_root_relative('mason');
param_error "Default data_dir (MasonDataDir) '$def' must be an absolute path"
unless File::Spec->file_name_is_absolute($def);
my @levels = File::Spec->splitdir($def);
param_error "Default data_dir (MasonDataDir) '$def' must be more than two levels deep (or must be set explicitly)"
if @levels <= 3;
}
# Set default error_format based on error_mode
if (exists($params{error_mode}) and $params{error_mode} eq 'fatal') {
$defaults{error_format} = 'line';
} else {
$defaults{error_mode} = 'output';
$defaults{error_format} = 'html';
}
# Push $r onto default allow_globals
if (exists $allowed_params->{allow_globals}) {
if ( $params{allow_globals} ) {
push @{ $params{allow_globals} }, '$r';
} else {
$defaults{allow_globals} = ['$r'];
}
}
my $self = eval { $class->SUPER::new(%defaults, %params) };
# We catch & throw this exception just to provide a better error message
if ( $@ && isa_mason_exception( $@, 'Params' ) && $@->message =~ /comp_root/ )
{
param_error "No comp_root specified and cannot determine DocumentRoot." .
" Please provide comp_root explicitly.";
}
rethrow_exception $@;
unless ( $self->interp->resolver->can('apache_request_to_comp_path') )
{
error "The resolver class your Interp object uses does not implement " .
"the 'apache_request_to_comp_path' method. This means that Apache2Handler " .
"cannot resolve requests. Are you using a handler.pl file created ".
"before version 1.10? Please see the handler.pl sample " .
"that comes with the latest version of Mason.";
}
# If we're running as superuser, change file ownership to http user & group
if (!($> || $<) && $self->interp->files_written)
{
chown getpwnam( Apache->server->dir_config( '_MasonUser' ) ),
getgrnam( Apache->server->dir_config( '_MasonGroup' ) ),
$self->interp->files_written
or system_error( "Can't change ownership of files written by interp object: $!\n" );
}
$self->_initialize;
return $self;
}
# Register with Apache::Status at module startup. Will get replaced
# with a more informative status once an interpreter has been created.
my $status_name = 'mason0001';
if ( load_pkg('Apache::Status') )
{
Apache::Status->menu_item
($status_name => __PACKAGE__->allowed_params->{apache_status_title}{default},
sub { ["<b>(no interpreters created in this child yet)</b>"] });
}
sub _initialize {
my ($self) = @_;
if ($self->args_method eq 'mod_perl') {
unless (defined $Apache::Request::VERSION) {
warn "Loading Apache::Request at runtime. You could " .
"increase shared memory between Apache processes by ".
"preloading it in your httpd.conf or handler.pl file\n";
require Apache::Request;
}
} else {
unless (defined $CGI::VERSION) {
warn "Loading CGI at runtime. You could increase shared ".
"memory between Apache processes by preloading it in ".
"your httpd.conf or handler.pl file\n";
require CGI;
}
}
# Add an HTML::Mason menu item to the /perl-status page.
if (defined $Apache::Status::VERSION) {
# A closure, carries a reference to $self
my $statsub = sub {
my ($r,$q) = @_; # request and CGI objects
return [] if !defined($r);
if ($r->path_info and $r->path_info =~ /expire_code_cache=(.*)/) {
$self->interp->delete_from_code_cache($1);
}
return ["<center><h2>" . $self->apache_status_title . "</h2></center>" ,
$self->status_as_html
(apache_req => $r),
$self->interp->status_as_html
(ah => $self, $r) ];
};
local $^W = 0; # to avoid subroutine redefined warnings
Apache::Status->menu_item($status_name, $self->apache_status_title, $statsub);
}
my $interp = $self->interp;
#
# Allow global $r in components
#
$interp->compiler->add_allowed_globals('$r')
if $interp->compiler->can('add_allowed_globals');
}
# Generate HTML that describes Apache2andler's current status.
# This is used in things like Apache::Status reports.
sub status_as_html {
my ($self, %p) = @_;
# Should I be scared about this? =)
my $comp_source = <<'EOF';
<h3>Apache2Handler properties:</h3>
<blockquote>
<tt>
<table width="75%">
<%perl>
foreach my $property (sort keys %$ah) {
my $val = $ah->{$property};
my $default = ( defined $val && defined $valid{$property}{default} && $val eq $valid{$property}{default} ) || ( ! defined $val && exists $valid{$property}{default} && ! defined $valid{$property}{default} );
my $display = $val;
if (ref $val) {
$display = '<font color="darkred">';
# only object can ->can, others die
my $is_object = eval { $val->can('anything'); 1 };
if ($is_object) {
$display .= ref $val . ' object';
} else {
if (UNIVERSAL::isa($val, 'ARRAY')) {
$display .= 'ARRAY reference - [ ';
$display .= join ', ', @$val;
$display .= '] ';
} elsif (UNIVERSAL::isa($val, 'HASH')) {
$display .= 'HASH reference - { ';
my @pairs;
while (my ($k, $v) = each %$val) {
push @pairs, "$k => $v";
}
$display .= join ', ', @pairs;
$display .= ' }';
} else {
$display = ref $val . ' reference';
}
}
$display .= '</font>';
}
defined $display && $display =~ s,([\x00-\x1F]),'<font color="purple">control-' . chr( ord('A') + ord($1) - 1 ) . '</font>',eg; # does this work for non-ASCII?
</%perl>
<tr valign="top" cellspacing="10">
<td>
<% $property | h %>
</td>
<td>
<% defined $display ? $display : '<i>undef</i>' %>
<% $default ? '<font color=green>(default)</font>' : '' %>
</td>
</tr>
% }
</table>
</tt>
</blockquote>
<%args>
$ah # The Apache2Handler we'll elucidate
%valid # Contains default values for member data
</%args>
EOF
my $interp = $self->interp;
my $comp = $interp->make_component(comp_source => $comp_source);
my $out;
$self->interp->make_request
( comp => $comp,
args => [ah => $self, valid => $interp->allowed_params],
ah => $self,
apache_req => $p{apache_req},
out_method => \$out,
)->exec;
return $out;
}
sub handle_request
{
my ($self, $r) = @_;
my $req = $self->prepare_request($r);
return $req unless ref($req);
return $req->exec;
}
my $do_filter = sub { $_[0]->filter_register };
my $no_filter = sub { $_[0] };
sub prepare_request
{
my $self = shift;
my $r_sub = lc $_[0]->dir_config('Filter') eq 'on' ? $do_filter : $no_filter;
# This gets the proper request object all in one fell swoop. We
# don't want to copy it because if we do something like assign an
# Apache::Request object to a variable currently containing a
# plain Apache object, we leak memory. This means we'd have to
# use multiple variables to avoid this, which is annoying.
# for mod_perl2 just pickup Apache::RequestRec
my $r = $_[0];
my $interp = $self->interp;
#
# If filename is a directory, then either decline or simply reset
# the content type, depending on the value of decline_dirs.
#
# ** We should be able to use $r->finfo here, but finfo is broken
# in some versions of mod_perl (e.g. see Shane Adams message on
# mod_perl list on 9/10/00)
#
my $is_dir = -d $r->filename;
my $is_file = -f _;
if ($is_dir) {
if ($self->decline_dirs) {
return Apache::DECLINED;
} else {
$r->content_type(undef);
}
}
#
# Compute the component path via the resolver. Return NOT_FOUND on failure.
#
my $comp_path = $interp->resolver->apache_request_to_comp_path($r);
unless ($comp_path) {
#
# Append path_info if filename does not represent an existing file
# (mainly for dhandlers).
#
my $pathname = $r->filename;
$pathname .= $r->path_info unless $is_file;
warn "[Mason] Cannot resolve file to component: " .
"$pathname (is file outside component root?)";
return $self->return_not_found($r);
}
my ($args, undef, $cgi_object) = $self->request_args($r);
#
# Set up interpreter global variables.
#
$interp->set_global( r => $r );
# If someone is using a custom request class that doesn't accept
# 'ah' and 'apache_req' that's their problem.
#
my $request = eval {
$interp->make_request( comp => $comp_path,
args => [%$args],
ah => $self,
apache_req => $r,
);
};
if (my $err = $@) {
# Mason doesn't currently throw any exceptions in the above, but some
# subclasses might. So be sure to handle them appropriately. We
# rethrow everything but TopLevelNotFound, Abort, and Decline errors.
if ( isa_mason_exception($err, 'TopLevelNotFound') ) {
# Return a 404.
$r->log_error("[Mason] File does not exist: ", $r->filename .
($r->path_info || ""));
return $self->return_not_found($r);
}
# Abort or decline.
my $retval = isa_mason_exception($err, 'Abort') ? $err->aborted_value :
isa_mason_exception($err, 'Decline') ? $err->declined_value :
rethrow_exception $err;
# not needed in mod_perl2 (??)
#$r->send_http_header unless $retval and $retval != 200;
return $retval;
}
my $final_output_method = ($r->method eq 'HEAD' ?
sub {} :
$r->can('print'));
# Craft the request's out method to handle http headers, content
# length, and HEAD requests.
my $sent_headers = 0;
my $out_method = sub {
# Send headers if they have not been sent by us or by user.
# We use instance here because if we store $request we get a
# circular reference and a big memory leak.
if (!$sent_headers and MasonX::Request2->instance->auto_send_headers) {
# not needed in mod_perl2 (??) - just set content type
#unless (http_header_sent($r)) {
# $r->send_http_header();
#}
$sent_headers = 1;
}
# We could perhaps install a new, faster out_method here that
# wouldn't have to keep checking whether headers have been
# sent and what the $r->method is. That would require
# additions to the Request interface, though.
# Call $r->print (using the real Apache method, not our
# overriden method).
$r->$final_output_method(grep {defined} @_);
};
$request->out_method($out_method);
$request->cgi_object($cgi_object) if $cgi_object;
return $request;
}
sub request_args
{
my ($self, $r) = @_;
#
# Get arguments from Apache::Request or CGI.
#
my ($args, $cgi_object);
if ($self->args_method eq 'mod_perl') {
$args = $self->_mod_perl_args($r);
} else {
$cgi_object = CGI->new;
$args = $self->_cgi_args($r, $cgi_object);
}
# we return $r solely for backwards compatibility
return ($args, $r, $cgi_object);
}
#
# Get $args hashref via CGI package
#
sub _cgi_args
{
my ($self, $r, $q) = @_;
# For optimization, don't bother creating a CGI object if request
# is a GET with no query string
return {} if $r->method eq 'GET' && !scalar($r->args);
return HTML::Mason::Utils::cgi_request_args($q, $r->method);
}
#
# Get $args hashref via Apache::Request package.
#
sub _mod_perl_args
{
my ($self, $r, $request) = @_;
# for mod_perl2, get back to Apache::Request from Apache::RequestRec
my $apr = Apache::Request->new( $r );
my %args;
foreach my $key ( $apr->param ) {
my @values = $apr->param($key);
$args{$key} = @values == 1 ? $values[0] : \@values;
}
return \%args;
}
#
# Determines whether the http header has been sent.
#
sub http_header_sent { shift->headers_out->{"Content-type"} }
# Utility function to prepare $r before returning NOT_FOUND.
sub return_not_found
{
my ($self, $r) = @_;
if ($r->method eq 'POST') {
$r->method('GET');
$r->headers_in->unset('Content-length');
}
return Apache::NOT_FOUND;
}
#
# PerlHandler MasonX::Apache2Handler
#
BEGIN
{
# A mod_perl2 method handler
my $handler_code = <<'EOF';
sub handler : method
{
my ($package, $r) = @_;
my $ah;
$ah ||= $package->make_ah($r);
return $ah->handle_request($r);
}
EOF
eval $handler_code;
rethrow_exception $@;
}
1;
__END__