<%args> $ah # The ApacheHandler 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; } sub prepare_request { my $self = shift; my $r = $self->_apache_request_object(@_); my $interp = $self->interp; my $fs_type = $self->_request_fs_type($r); return DECLINED if $fs_type eq 'dir' && $self->decline_dirs; # # Compute the component path via the resolver. Return NOT_FOUND on failure. # my $comp_path = $interp->resolver->apache_request_to_comp_path($r, $interp->comp_root_array); 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 $fs_type eq '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 $m = eval { $interp->make_request( comp => $comp_path, args => [%$args], ah => $self, apache_req => $r, ); }; if (my $err = $@) { # We rethrow everything but TopLevelNotFound, Abort, and Decline errors. if ( isa_mason_exception($@, 'TopLevelNotFound') ) { $r->log_error("[Mason] File does not exist: ", $r->filename . ($r->path_info || "")); return $self->return_not_found($r); } my $retval = ( isa_mason_exception($err, 'Abort') ? $err->aborted_value : isa_mason_exception($err, 'Decline') ? $err->declined_value : rethrow_exception $err ); $retval = OK if defined $retval && $retval eq HTTP_OK; unless ($retval) { unless (APACHE2) { unless ($r->notes('mason-sent-headers')) { $r->send_http_header(); } } } return $retval; } $self->_set_mason_req_out_method($m, $r) unless $self->{has_custom_out_method}; $m->cgi_object($cgi_object) if $m->can('cgi_object') && $cgi_object; return $m; } my $do_filter = sub { $_[0]->filter_register }; my $no_filter = sub { $_[0] }; sub _apache_request_object { my $self = shift; # We need to be careful to never assign a new apache (subclass) # object to $r or we will leak memory, at least with mp1. my $new_r = APACHE2 ? $_[0] : HTML::Mason::Apache::Request->new( $_[0] ); my $r_sub; my $filter = $_[0]->dir_config('Filter'); if ( defined $filter && lc $filter eq 'on' ) { die "To use Apache::Filter with Mason you must have at least version 1.021 of Apache::Filter\n" unless Apache::Filter->VERSION >= 1.021; $r_sub = $do_filter; } else { $r_sub = $no_filter; } my $apreq_instance = APACHE2 ? sub { Apache2::Request->new( $_[0] ) } : sub { $_[0] }; return $r_sub->( $self->args_method eq 'mod_perl' ? $apreq_instance->( $new_r ) : $new_r ); } sub _request_fs_type { my ($self, $r) = @_; # # 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; return $is_dir ? 'dir' : -f _ ? 'file' : 'other'; } 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, $apr) = @_; my %args; foreach my $key ( $apr->param ) { my @values = $apr->param($key); $args{$key} = @values == 1 ? $values[0] : \@values; } return \%args; } sub _set_mason_req_out_method { my ($self, $m, $r) = @_; 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 $out_method; if (APACHE2) { # mod_perl-2 does not need to call $r->send_http_headers $out_method = sub { eval { $r->$final_output_method( grep { defined } @_ ); $r->rflush; }; my $err = $@; die $err if $err and $err !~ /Software caused connection abort/; }; } else { my $sent_headers = 0; $out_method = sub { # Send headers if they have not been sent by us or by user. # We use instance here because if we store $m we get a # circular reference and a big memory leak. if (!$sent_headers and HTML::Mason::Request->instance->auto_send_headers) { unless ($r->notes('mason-sent-headers')) { $r->send_http_header(); } $sent_headers = 1; } # Call $r->print (using the real Apache method, not our # overridden method). $r->$final_output_method( grep {defined} @_ ); $r->rflush; }; } $m->out_method($out_method); } # 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 NOT_FOUND; } # # PerlHandler HTML::Mason::ApacheHandler # BEGIN { # A method handler is prototyped differently in mod_perl 1.x than in 2.x my $handler_code = sprintf <<'EOF', APACHE2 ? ': method' : '($$)'; sub handler %s { my ($package, $r) = @_; my $ah; $ah ||= $package->make_ah($r); return $ah->handle_request($r); } EOF eval $handler_code; rethrow_exception $@; } 1; __END__ =head1 NAME HTML::Mason::ApacheHandler - Mason/mod_perl interface =head1 SYNOPSIS use HTML::Mason::ApacheHandler; my $ah = HTML::Mason::ApacheHandler->new (..name/value params..); ... sub handler { my $r = shift; $ah->handle_request($r); } =head1 DESCRIPTION The ApacheHandler object links Mason to mod_perl (version 1 or 2), running components in response to HTTP requests. It is controlled primarily through parameters to the new() constructor. =head1 PARAMETERS TO THE new() CONSTRUCTOR =over =item apache_status_title Title that you want this ApacheHandler to appear as under Apache::Status. Default is "HTML::Mason status". This is useful if you create more than one ApacheHandler object and want them all visible via Apache::Status. =item args_method Method to use for unpacking GET and POST arguments. The valid options are 'CGI' and 'mod_perl'; these indicate that a C<%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 = ''; # 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 .= ''; } defined $display && $display =~ s,([\x00-\x1F]),'control-' . chr( ord('A') + ord($1) - 1 ) . '',eg; # does this work for non-ASCII? %perl>
% } <% $property | h %> <% defined $display ? $display : 'undef' %> <% $default ? '(default)' : '' %>