——=head1 NAME
Apache::XAO - Apache XAO handler
=head1 SYNOPSIS
In httpd.conf or <VirtualHost ..> section:
PerlFreshRestart On
PerlSetVar SiteName testsite
SetHandler perl-script
PerlTransHandler Apache::XAO
=head1 DESCRIPTION
Apache::XAO provides a clean way to integrate XAO::Web based web
sites into mod_perl for maximum performance. The same site can still be
used in CGI mode (see L<XAO::Web::Intro> for configuration samples).
Apache::XAO must be installed as PerlTransHandler, not as a
PerlHandler.
If some areas of the tree need to be excluded from XAO::Web (as it
usually happens with images -- /images or something similar) these
areas need to be configured in the site's configuration. This is
described in details below.
As a convenience, there is also a simple way to exclude certain locations
using Apache configuration only. Most common is ExtFilesMap:
PerlSetVar ExtFilesMap /images
This tells Apache::XAO to map everything under /images location in
URI to physical files in 'images' directory off the project home
directory. For a site named 'testsite' this is roughly the same as the
following, only you do not have to worry about exact path to site's home
directory:
Alias /images /usr/local/xao/projects/testsite/images
To achieve the same effect from the site configuration you need:
path_mapping_table => {
'/images' => {
type => 'maptodir',
},
},
A more generic way is to just disable Apache::XAO from handling some
area altogether:
PerlSetVar ExtFiles /images
In this case no mapping is performed and generally Apache::XAO does
nothing and lets Apache handle the files.
A site configuration equivalent is:
path_mapping_table => {
'/images' => {
type => 'external',
},
},
More then one prefix can be listed using ':' as a separator:
PerlSetVar ExtFilesMap /images:/icons:/ftp
=head2 PERFORMANCE
Using Apache::XAO gives of course incomparable with CGI mode
performance, but even in comparision with mod_rewrite/mod_perl
combination we got 5 to 10 percent boost in performance in tests.
Not to mention cleaner looking config files and reduced server memory
footprint -- no need to load mod_rewrite.
For additional improvement in memory size it is recommended to add
the following line into the main Apache httpd.conf (not into any
VirtualHost):
PerlModule XAO::PreLoad
This way most of XAO modules will be pre-compiled and shared between all
apache child processes thus saving memory and child startup time.
=cut
###############################################################################
package
Apache::XAO;
use
strict;
use
warnings;
use
XAO::Utils;
use
XAO::Web;
our
$VERSION
=
'2.01'
;
# Needed for CPAN
###############################################################################
BEGIN {
eval
{
Apache2::Const->
import
(
qw(:common)
);
$MP2
=2;
};
if
($@) {
eval
{
Apache::Const->
import
(
qw(:common)
);
$MP2
=1;
};
}
if
($@) {
eval
{
Apache::Constants->
import
(
qw(:common)
);
$MP2
=0;
};
}
if
($@) {
die
"Error setting up Apache::XAO: $@"
;
}
}
###############################################################################
sub
handler_content ($);
sub
server_error ($$;$);
###############################################################################
sub
handler {
my
$r
=
shift
;
# Request URI
#
my
$uri
=
$r
->uri;
### $r->server->log_error("HANDLER: $uri");
##
# Checking if we were called as a PerlHandler and complaining
# otherwise.
#
if
(
$r
->is_initial_req &&
exists
$ENV
{
'REQUEST_METHOD'
}) {
return
server_error(
$r
,
'Use PerlTransHandler'
,
<<EOT);
Please use 'PerlTransHandler Apache::XAO' instead of just PerlHandler.
EOT
}
##
# Getting site name and loading the site configuration
#
my
$sitename
=
$r
->dir_config(
'sitename'
) ||
$r
->dir_config(
'SiteName'
);
if
(!
$sitename
) {
return
server_error(
$r
,
'No Site Name'
,
<<EOT);
Please use 'PerlSetVar sitename yoursitename' directive in the
configuration.
EOT
}
my
$web
=XAO::Web->new(
sitename
=>
$sitename
,
init_args
=> {
environment
=>
'web'
,
});
# Checking access rules
#
if
(!
$web
->check_uri_access(
$uri
)) {
$r
->server->log_error(
"Access denied to $uri, see path_deny_table (/CVS/ and /bits/ are denied by default)"
);
return
NOT_FOUND;
}
# Checking if we need to worry about ExtFilesMap or ExtFiles in the
# apache config.
#
my
$efm
=
$r
->dir_config(
'ExtFilesMap'
) ||
''
;
my
$ef
=
$r
->dir_config(
'ExtFiles'
) ||
''
;
if
(
$efm
||
$ef
) {
my
$config
=
$web
->config;
my
$pmt
=
$config
->get(
'path_mapping_table'
);
my
$pmt_orig
=
$pmt
;
foreach
my
$path
(
split
(/:+/,
$efm
)) {
$path
=
'/'
.
$path
;
$path
=~s/\/{2,}/\//g;
$path
=~s/\/$//g;
next
if
$pmt
->{
$path
};
$pmt
->{
$path
}={
type
=>
'maptodir'
};
}
foreach
my
$path
(
split
(/:+/,
$ef
)) {
$path
=
'/'
.
$path
;
$path
=~s/\/{2,}/\//g;
$path
=~s/\/$//g;
next
if
$pmt
->{
$path
};
$pmt
->{
$path
}={
type
=>
'external'
};
}
if
(!
$pmt_orig
) {
$config
->put(
path_mapping_table
=>
$pmt
);
}
}
# Checking if we should serve this request at all. If the URI ends
# with / we always add index.html to the URI before checking.
#
my
$pagedesc
;
if
(
substr
(
$uri
,-1,1) eq
'/'
) {
$pagedesc
=
$web
->analyze(
$uri
.
'index.html'
,
$sitename
,1);
}
else
{
$pagedesc
=
$web
->analyze(
$uri
,
$sitename
,1);
}
my
$ptype
=
$pagedesc
->{
'type'
} ||
'xaoweb'
;
if
(
$ptype
eq
'external'
) {
### $r->server->log_error("EXTERNAL: uri=$uri");
return
DECLINED;
}
elsif
(
$ptype
eq
'maptodir'
) {
my
$dir
=
$pagedesc
->{
'directory'
} ||
''
;
if
(!
length
(
$dir
) ||
substr
(
$dir
,0,1) ne
'/'
) {
my
$phdir
=
$XAO::Base::projectsdir
.
"/"
.
$sitename
;
if
(
length
(
$dir
)) {
$dir
=
$phdir
.
'/'
.
$dir
;
}
else
{
$dir
=
$phdir
;
}
}
$dir
.=
'/'
.
$uri
;
$dir
=~s/\/{2,}/\//g;
$r
->filename(
$dir
);
### $r->server->log_error("MAPTODIR: => $dir");
return
OK;
}
# We pass the knowledge along in the 'notes' table.
#
$r
->pnotes(
xaoweb
=>
$web
);
$r
->pnotes(
pagedesc
=>
$pagedesc
);
$r
->pnotes(
uri
=>
$uri
);
# Default is to install a content handler to produce actual output.
# It could be more optimal to have two always present handlers
# instead of pushing/popping automatically -- in this case
# 'HandlerType' must be set to 'static' in the server config.
#
# We return OK to indicate to Apache that there is no need to try
# to map that URI to anything else, we know how to produce results
# for it.
#
my
$htype
=
lc
(
$r
->dir_config(
'HandlerType'
) ||
'auto'
);
if
(
$htype
eq
'auto'
) {
### $r->server->log_error("TRANS: auto (uri=$uri)");
# In mod_perl 2.x filepath translation is done in a separate
# phase, we need to set up a handler for it -- otherwise apache
# will still attempt to map filename, and worse yet -- attempt
# to redirect to language specific 'index.html.en' for example.
#
if
(
$MP2
) {
$r
->push_handlers(
PerlMapToStorageHandler
=> \
&handler_map_to_storage
);
$r
->push_handlers(
PerlResponseHandler
=> \
&handler_content
);
return
OK;
}
else
{
$r
->push_handlers(
PerlHandler
=> \
&handler_content
);
return
OK;
}
}
elsif
(
$htype
eq
'static'
) {
### $r->server->log_error("TRANS: static (uri=$uri)");
return
OK;
}
else
{
return
server_error(
$r
,
"Unknown HandlerType '$htype'"
);
}
}
###############################################################################
sub
handler_content ($) {
my
$r
=
shift
;
# Getting the data. If there is no data then trans handler was not
# executed or has declined, so we do not need to do anything.
#
my
$web
=
$r
->pnotes(
'xaoweb'
) ||
return
DECLINED;
my
$pagedesc
=
$r
->pnotes(
'pagedesc'
);
# If needed setting up SizeLimit to possibly restart this process
# when it gets too big. Controlled by memory_size_limit parameters
# in the site config:
#
# check_every_n_requests | every_n_requests
# max_process_size (in KB)
# min_share_size (in KB)
# max_unshared_size (in KB)
#
if
(
my
$msl
=
$web
->config->get(
'memory_size_limit'
)) {
if
(
$MP2
&&
$MP2
==2) {
if
(!
$HAVE_SIZE_LIMIT
) {
$HAVE_SIZE_LIMIT
=1;
}
$Apache2::SizeLimit::CHECK_EVERY_N_REQUESTS
=
$msl
->{
'check_every_n_requests'
}
if
$msl
->{
'check_every_n_requests'
};
$Apache2::SizeLimit::CHECK_EVERY_N_REQUESTS
=
$msl
->{
'every_n_requests'
}
if
$msl
->{
'every_n_requests'
};
Apache2::SizeLimit::setmin(
$msl
->{
'min_share_size'
})
if
$msl
->{
'min_share_size'
};
Apache2::SizeLimit::setmin(
$msl
->{
'min_shared_size'
})
if
$msl
->{
'min_shared_size'
};
Apache2::SizeLimit::setmax(
$msl
->{
'max_process_size'
},
$r
)
if
$msl
->{
'max_process_size'
};
Apache2::SizeLimit::setmax_unshared(
$msl
->{
'max_unshared_size'
})
if
$msl
->{
'max_unshared_size'
};
### $r->server->log_error("$Apache2::SizeLimit::CHECK_EVERY_N_REQUESTS / $Apache2::SizeLimit::MIN_SHARE_SIZE / $Apache2::SizeLimit::MAX_UNSHARED_SIZE / $Apache2::SizeLimit::MAX_PROCESS_SIZE");
}
else
{
$r
->server->log_error(
"memory_size_limit is only supported when Apache2::SizeLimit is available"
);
}
}
# We have to get the original URI, the one in $r->uri may get mangled
#
my
$uri
=
$r
->pnotes(
'uri'
);
### $r->server->log_error("CONTENT: uri=$uri");
# Executing
#
$web
->execute(
path
=>
$uri
,
apache
=>
$r
,
pagedesc
=>
$pagedesc
,
);
return
OK;
}
###############################################################################
sub
handler_map_to_storage {
my
$r
=
shift
;
### $r->server->log_error("MAPTOSTORAGE: uri=".$r->uri);
return
OK;
}
###############################################################################
sub
server_error ($$;$) {
my
(
$r
,
$name
,
$desc
)=
@_
;
$desc
=
$name
unless
$desc
;
$r
->server->log_error(
"*ERROR: Apache::XAO - $name"
);
$r
->custom_response(
SERVER_ERROR,
"<H2>XAO::Web System Error: $name</H2>\n$desc"
);
return
SERVER_ERROR;
}
###############################################################################
1;
__END__
=head1 EXPORTS
Nothing.
=head1 AUTHOR
Copyright (c) 2005 Andrew Maltsev
Copyright (c) 2001-2004 Andrew Maltsev, XAO Inc.
<am@ejelta.com> -- http://ejelta.com/xao/
=head1 SEE ALSO
Recommended reading:
L<XAO::Web::Intro>,
L<XAO::Web>,
L<XAO::DO::Config>,
L<Apache>.