NAME¶
HTML::Mason::Admin - Mason Administrator's Manual
VERSION¶
version 1.54
DESCRIPTION¶
This manual is written for the sysadmin/webmaster in charge of installing,
configuring, or tuning a Mason system. The bulk of the documentation assumes
that you are using mod_perl. See RUNNING OUTSIDE OF MOD_PERL for more details.
For more details on mod_perl, visit the mod_perl website at
http://perl.apache.org/.
SITE CONFIGURATION METHODS¶
Mason includes a module specifically designed to integrate Mason and mod_perl (1
and 2), "HTML::Mason::ApacheHandler". By telling mod_perl to hand
content requests to this module, you can use Mason to generate web pages.
There are two ways to configure Mason under mod_perl.
- •
- Basic
Mason provides reasonable default behavior under mod_perl, so using Mason
can be as simple as adding two directives to your Apache configuration
file. Throughout this document, we will assume that your Apache
configuration file is called httpd.conf. By adding more
configuration parameters to this file you can implement more complex
behaviors.
- •
- Advanced
If the basic method does not provide enough flexibility for you, you can
wrap Mason in a custom mod_perl handler. The wrapper code you write can
create its own Mason objects, or it can take advantage of
httpd.conf configuration parameters and let Mason create the
objects it needs by itself.
We recommend that you start with the basic method and work your way forward as
the need for flexibility arises.
Mason is very flexible, and you can replace parts of it by creating your own
classes. This documentation assumes that you are simply using the classes
provided in the Mason distribution. Subclassing is covered in the Subclassing
document. The two topics are orthogonal, as you can mix the configuration
techniques discussed here with your own custom subclasses.
BASIC CONFIGURATION VIA httpd.conf DIRECTIVES¶
The absolutely most minimal configuration looks like this:
PerlModule HTML::Mason::ApacheHandler
<Location />
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</Location>
This configuration tells Apache to serve all URLs through Mason (see the next
section for a more realistic strategy). We use the PerlModule line to tell
mod_perl to load Mason once at startup time, saving time and memory. This
example does not set any Mason configuration parameters, so Mason uses its
default values.
If this is your first time installing and using Mason, we recommend that you use
the above configuration in a test webserver to start with. This will let you
play with Mason under mod_perl with a minimum of fuss. Once you've gotten this
working, then come back and read the rest of the document for further
possibilities.
Controlling Access via Filename Extension¶
As it turns out, serving every URL through Mason is a bad idea for two reasons:
- 1.
- Mason should be prevented from handling images, tarballs, and other binary
files. Not only will performance suffer, but binary files may
inadvertently contain a Mason character sequence such as
"<%". These files should be instead served by Apache's
default content handler.
- 2.
- Mason should be prevented from serving private (non-top-level) Mason
components to users. For example, if you used a utility component for
performing arbitrary sql queries, you wouldn't want external users to be
able to access it via a URL. Requests for private components should simply
result in a 404 NOT_FOUND.
The easiest way to distinguish between different types of files is with filename
extensions. While many naming schemes are possible, we suggest using
"normal" extensions for top-level components and adding an
"m" prefix for private components. For example,
Top-level Private
Component outputs HTML .html .mhtml
Component outputs text .txt .mtxt
Component executes Perl .pl .mpl
This scheme minimizes the chance of confusing browsers about content type,
scales well for new classes of content (e.g. .js/.mjs for javascript), and
makes transparent the fact that you are using Mason versus some other package.
Here is a configuration that enforces this naming scheme:
PerlModule HTML::Mason::ApacheHandler
<LocationMatch "(\.html|\.txt|\.pl)$">
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</LocationMatch>
<LocationMatch "(\.m(html|txt|pl)|dhandler|autohandler)$">
SetHandler perl-script
PerlInitHandler Apache::Constants::NOT_FOUND
</LocationMatch>
The first block causes URLs ending in .html, .txt, or .pl to be served through
Mason. The second block causes requests to private components to return 404
NOT_FOUND, preventing unscrupulous users from even knowing which private
components exist. Any other file extensions (e.g. .gif, .tgz) will be served
by Apache's default content handler.
You might prefer "FilesMatch" to "LocationMatch". However,
be aware that "LocationMatch" will work best in conjunction with
Mason's dhandlers.
Configuration Parameters¶
Mason allows you to flexibly configure its behavior via
httpd.conf
configuration parameters.
These configuration parameters are set via mod_perl's "PerlSetVar" and
"PerlAddVar" directives. Though these parameters are all strings in
your
httpd.conf file, Mason treats different directives as containing
different types of values:
- •
- string
The variable's value is simply taken literally and used. The string should
be surrounded by quotes if the it contains whitespace. The quotes will be
automatically removed by Apache before Mason sees the variable.
- •
- boolean
The variable's value is used as a boolean, and is subject to Perl's rules on
truth/falseness. It is recommended that you use 0 (false) or 1 (true) for
these arguments.
- •
- code
The string is treated as a piece of code and "eval"'ed. This is
used for parameters that expect subroutine references. For example, an
anonymous subroutine might look like:
PerlSetVar MasonOutMode "sub { ... }"
A named subroutine reference would look like this:
PerlSetVar MasonOutMode "\&Some::Module::handle_output"
- •
- list
To set a list parameter, use "PerlAddVar" for the values, like
this:
PerlAddVar MasonPreloads /foo/bar/baz.comp
PerlAddVar MasonPreloads /foo/bar/quux.comp
- •
- hash_list
Just like a list parameter, use "PerlAddVar" for the values.
However, in the case of a hash_list, each element should be a key/value
pair separated by "=>":
PerlAddVar MasonDataCacheDefaults "cache_class => MemoryCache"
PerlAddVar MasonDataCacheDefaults "namespace => foo"
Take note that the right hand side of the each pair should not be
quoted.
See HTML::Mason::Params for a full list of parameters, and their associated
types.
GENERAL SERVER CONFIGURATION¶
Component Root¶
The component root (comp_root) marks the top of your component hierarchy. When
running Mason with the ApacheHandler or CGIHandler modules, this defaults to
your document root.
The component root defines how component paths are translated into real file
paths. If your component root is
/usr/local/httpd/docs, a component
path of
/products/index.html translates to the file
/usr/local/httpd/docs/products/index.html.
One cannot call a component outside the component root. If Apache passes a file
through Mason that is outside the component root (say, as the result of an
Alias) you will get a 404 and a warning in the logs.
You may also specify multiple component roots in the spirit of Perl's @INC. Each
root is assigned a key that identifies the root mnemonically. For example, in
httpd.conf:
PerlAddVar MasonCompRoot "private => /usr/home/joe/comps"
PerlAddVar MasonCompRoot "main => /usr/local/www/htdocs"
This specifies two component roots, a main component tree and a private tree
which overrides certain components. The order is respected ala @INC, so
private is searched first and
main second.
The component root keys must be unique in a case-insensitive comparison. The
keys are used in several ways. They help to distinguish component caches and
object files between different component roots, and they appear in the
"title()" of a component.
Data Directory¶
The data directory (data_dir) is a writable directory that Mason uses for
various features and optimizations. By default, it is a directory called
"mason" under your Apache server root. Because Mason will not use a
default data directory under a top-level directory, you will need to
change this on certain systems that assign a high-level server root such as
/usr or
/etc.
Mason will create the directory on startup, if necessary, and set its
permissions according to the web server User/Group.
External Modules¶
Components will often need access to external Perl modules. There are several
ways to load them.
- •
- The httpd PerlModule directive:
PerlModule CGI
PerlModule LWP
- •
- In the "<%once>" section of the component(s) that use the
module.
<%once>
use CGI ':standard';
use LWP;
</%once>
Each method has its own trade-offs:
The first method ensures that the module will be loaded by the Apache parent
process at startup time, saving time and memory. The second method, in
contrast, will cause the modules to be loaded by each server child. On the
other hand this could save memory if the component and module are rarely used.
See the mod_perl guide's tuning section and Vivek Khera's mod_perl tuning
guide for more details on this issue.
The second method uses the modules from inside the package used by components
("HTML::Mason::Commands"), meaning that exported method names and
other symbols will be usable from components. The first method, in contrast,
will import symbols into the "main" package. The significance of
this depends on whether the modules export symbols and whether you want to use
them from components.
If you want to preload the modules in your
httpd.conf file, and still
have them export symbols into the "HTML::Mason::Commands" namespace,
you can do this:
<Perl>
{ package HTML::Mason::Commands;
use CGI;
use LWP;
}
</Perl>
A Perl section will also work for including local library paths:
<Perl>
use lib '/path/to/local/lib';
</Perl>
Allowing Directory Requests¶
By default Mason will decline requests for directories, leaving Apache to serve
up a directory index or a FORBIDDEN as appropriate. Unfortunately this rule
applies even if there is a dhandler in the directory:
/foo/bar/dhandler
does not get a chance to handle a request for
/foo/bar/.
If you would like Mason to handle directory requests, set decline_dirs to 0. The
dhandler that catches a directory request is responsible for setting a
reasonable content type via "$r->content_type()".
Configuring Virtual Sites¶
These examples extend the single site configurations given so far.
Multiple sites, one component root
If you want to share some components between your sites, arrange your
httpd.conf so that all DocumentRoots live under a single component
space:
# Web site #1
<VirtualHost www.site1.com>
DocumentRoot /usr/local/www/htdocs/site1
<LocationMatch ...>
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</LocationMatch>
</VirtualHost>
# Web site #2
<VirtualHost www.site2.com>
DocumentRoot /usr/local/www/htdocs/site2
<LocationMatch ...>
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</LocationMatch>
</VirtualHost>
# Mason configuration
PerlSetVar MasonCompRoot /usr/local/www/htdocs
PerlSetVar MasonDataDir /usr/local/mason
PerlModule HTML::Mason::ApacheHandler
The directory structure for this scenario might look like:
/usr/local/www/htdocs/ # component root
+- shared/ # shared components
+- site1/ # DocumentRoot for first site
+- site2/ # DocumentRoot for second site
Incoming URLs for each site can only request components in their respective
DocumentRoots, while components internally can call other components anywhere
in the component space. The
shared/ directory is a private directory
for use by components, inaccessible from the Web.
Multiple sites, multiple component roots
If your sites need to have completely distinct component hierarchies, e.g. if
you are providing Mason ISP services for multiple users, then the component
root must change depending on the site requested.
<VirtualHost www.site1.com>
DocumentRoot /usr/local/www/htdocs/site1
# Mason configuration
PerlSetVar MasonCompRoot /usr/local/www/htdocs/site1
PerlSetVar MasonDataDir /usr/local/mason/site1
<LocationMatch ...>
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</LocationMatch>
</VirtualHost>
# Web site #2
<VirtualHost www.site2.com>
DocumentRoot /usr/local/www/htdocs/site2
# Mason configuration
PerlSetVar MasonCompRoot /usr/local/www/htdocs/site2
PerlSetVar MasonDataDir /usr/local/mason/site2
<LocationMatch ...>
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</LocationMatch>
</VirtualHost>
ADVANCED CONFIGURATION¶
As mentioned previously, it is possible to write a custom mod_perl content
handler that wraps around Mason and provides basically unlimited flexibility
when handling requests. In this section, we show some basic wrappers and
re-implement some of the functionality previously discussed, such as declining
image requests and protecting private components.
In addition, we discuss some of the possibilities that become available when you
create a custom wrapper around Mason's request handling mechanism. This
wrapper generally consists of two parts. The initialization portion, run at
server startup, will load any needed modules and create objects. The other
portion is the "handler()" subroutine, which handles web page
requests.
Writing a Wrapper¶
To create a wrapper, you simply need to define a "handler()"
subroutine in the package of your choice, and tell mod_perl to use it as a
content handler. The file that defines the "handler()" subroutine
can be a module, or you can simply load a simple file that contains this
subroutine definition. The latter solution was, for a long time, the
only way to configure Mason, and the file used was traditionally called
handler.pl.
Nowadays, we recommend that you create a custom module in the appropriate
namespace and define your "handler()" subroutine there. The
advantage to this approach is that it uses well-known techniques for creating
and installing modules, but it does require a bit more work than simply
dropping a script file into the Apache configuration directory. But because
the process is better defined, it may "feel" more solid to some
folks than the script approach.
The
eg/ directory of the Mason distribution contains a couple sample
modules that define "handler()" subroutines. Let's assume that your
module, like the example, defines a "handler()" in the package
"MyApp::Mason". In this case, your Apache configuration would look
like this:
PerlModule MyApp::Mason
<LocationMatch ...>
SetHandler perl-script
PerlHandler MyApp::Mason
</LocationMatch>
You may still see references to a
handler.pl file in the Mason users list
archives, as well as the FAQ. These references will generally be applicable to
any custom code wrapping Mason.
Wrappers and PerlSetVar-style configuration
Sometimes people attempt to write a wrapper
and configure Mason with
"PerlSetVar" directives in their Apache configuration file.
This
does not work. When you give mod_perl this configuration:
PerlHandler HTML::Mason::ApacheHandler
it will dispatch directly to the
"HTML::Mason::ApacheHandler->handler()" method, without ever
executing your wrapper code. However, you can mix the two methods. See Mixing
httpd.conf Configuration with a Wrapper
Wrapping with a <Perl> block¶
You can also put your wrapper code in a "<Perl>" block as part
of your
httpd.conf file. The result is no different than loading a file
via the "PerlRequire" directive.
The Wrapper Code¶
Regardless of how you load your wrapper code, it will always work the same way.
The "handler()" subroutine should expect to receive the Apache
request object representing the current request. This request object is used
by the ApacheHandler module to determine what component is being called.
Let's look at the guts of some wrapper code. Here's a first version:
package MyApp::Mason;
use strict;
use HTML::Mason::ApacheHandler;
my $ah =
HTML::Mason::ApacheHandler->new
( comp_root => '/path/to/comp/root',
data_dir => '/path/to/data/dir' );
sub handler {
my ($r) = @_;
return $ah->handle_request($r);
}
This wrapper is fully functional, but it doesn't actually do anything you
couldn't do more easily by configuring Mason via the
httpd.conf file.
However, it does serve as a good skeleton to which additional functionality
can easily be added.
External Modules Revisited¶
Since you are loading an arbitrary piece of code to define your wrapper, you can
easily load other modules needed for your application at the same time. For
example, you might simple add these lines to the wrapper code above:
{
package HTML::Mason::Commands;
use MIME::Base64;
}
Explicitly setting the package to "HTML::Mason::Commands" makes sure
that any symbols that the loaded modules export (constants, subroutines, etc.)
get exported into the namespace under which components run. Of course, if
you've changed the component namespace, make sure to change the package name
here as well.
Alternatively, you might consider creating a separate piece of code to load the
modules you need. For example, you might create a module called
"MyApp::MasonInit":
{
package HTML::Mason::Commands;
use Apache::Constants qw(:common);
use Apache::URI;
use File::Temp;
}
1;
This can be loaded via a "PerlModule" directive in the
httpd.conf file, or in the wrapper code itself via "use".
Example: Controlling access with component attributes
An example of something you can only do with wrapper code is deciding at
run-time whether a component can be accessed at the top-level based on a
complex property of the component. For example, here's a piece of code that
uses the current user and a component's "access_level" attribute to
control access:
sub handler {
my ($r) = @_;
my $req = $ah->prepare_request($r);
my $comp = $req->request_comp;
# this is done via magic hand-waving ...
my $user = get_user_from_cookie();
# remember, attributes are inherited so this could come from a
# component higher up the inheritance chain
my $required_access = $comp->attr('access_level');
return NOT_FOUND
if $user->access_level < $required_access;
return $req->exec;
}
Wrappers with Virtual Hosts¶
If you had several virtual hosts, each of which had a separate component root,
you'd need to create a separate ApacheHandler object for each host, one for
each host. Here's some sample code for that:
my %ah;
foreach my $site ( qw( site1 site2 site3 ) ) {
$ah{$site} =
HTML::Mason::ApacheHandler->new
( comp_root => "/usr/local/www/$site",
data_dir => "/usr/local/mason/$site" );
}
sub handler {
my ($r) = @_;
my $site = $r->dir_config('SiteName');
return DECLINED unless exists $ah{$site};
return $ah{$site}->handle_request($r);
}
This code assumes that you set the "SiteName" variable via a
"PerlSetVar" directive in each "VirtualHost" block, like
this:
<VirtualHost site1.example.com>
PerlSetVar SiteName site1
<LocationMatch ...>
SetHandler perl-script
PerlHandler MyApp::Mason
</LocationMatch>
</VirtualHost>
Creating apachehandler objects on the fly
You might also consider creating ApacheHandler objects on the fly, like this:
my %ah;
sub handler {
my ($r) = @_;
my $site = $r->dir_config('SiteName');
return DECLINED unless $site;
unless exists($ah{$site}) {
$ah{$site} = HTML::Mason::ApacheHandler->new( ... );
}
$ah{$site}->handle_request($r);
}
This is more flexible but you lose the memory savings of creating all your
objects during server startup.
Other uses for a wrapper
If you have some code which must
always run after a request, then the
only way to guarantee that this happens is to wrap the
"$ah->handle_request()" call in an "eval {}" block, and
then run the needed code after the request returns. You can then handle errors
however you like.
Mixing httpd.conf Configuration with a Wrapper¶
You can take advantage of Mason's
httpd.conf configuration system while
at the same time providing your own wrapper code. The key to doing this is
not creating your own ApacheHandler object. Instead, you call the
"HTML::Mason::ApacheHandler->handler()" class method from your
"handler()" subroutine. Here's a complete wrapper that does this:
package MyApp::Mason;
use strict;
use HTML::Mason::ApacheHandler;
sub handler {
my ($r) = @_;
return HTML::Mason::ApacheHandler->handler($r);
}
The "HTML::Mason::ApacheHandler->handler" method will create an
ApacheHandler object based on the configuration directives it finds in your
httpd.conf file. Obviously, this wrapper is again a skeleton, but you
could mix and match this wrapper code with any of the code shown above.
Alternately you could subclass the "HTML::Mason::ApacheHandler" class,
and override the "handler()" method it provides. See the Subclassing
documentation for more details. Of course, you could even create a subclass
and write a wrapper that called it.
DEVELOPMENT¶
This section describes how to set up common developer features.
Global Variables¶
Global variables can make programs harder to read, maintain, and debug, and this
is no less true for Mason components. Due to the persistent mod_perl
environment, globals require extra initialization and cleanup care.
That said, there are times when it is very useful to make a value available to
all Mason components: a DBI database handle, a hash of user session
information, the server root for forming absolute URLs.
Because Mason by default parses components in "strict" mode, you'll
need to declare a global if you don't want to access it with an explicit
package name. The easiest way to declare a global is with the allow_globals
parameter.
Since all components run in the same package, you'll be able to set the global
in one component and access it in all the others.
Autohandlers are common places to assign values to globals. Use the
"<%once>" section if the global only needs to be initialized
at load time, or the "<%init>" section if it needs to be
initialized every request.
Sessions¶
Mason does not have a built-in session mechanism, but you can use the
"MasonX::Request::WithApacheSession" module, available from CPAN, to
add a session to every request. It can also automatically set and read cookies
containing the session id.
Data Caching¶
Data caching is implemented with DeWitt Clinton's "Cache::Cache"
module. For full understanding of this section you should read the
documentation for "Cache::Cache" as well as for relevant subclasses
(e.g. "Cache::FileCache").
- Cache files
- By default, "Cache::FileCache" is the subclass used for data
caching, although this may be overridden by the developer.
"Cache::FileCache" creates a separate subdirectory for every
component that uses caching, and one file some number of levels underneath
that subdirectory for each cached item. The root of the cache tree is
data_dir/"cache". The name of the cache subdirectory for a
component is determined by the function
"HTML::Mason::Utils::data_cache_namespace".
- Default constructor options
- Ordinarily, when "$m->cache" is called, Mason passes to the
cache constructor the "namespace", and "cache_root"
options, along with any other options given in the
"$m->cache" method.
You may specify other default constructor options with the
data_cache_defaults parameter. For example,
PerlSetVar MasonDataCacheDefaults "cache_class => SizeAwareFileCache"
PerlAddVar MasonDataCacheDefaults "cache_depth => 2"
PerlAddVar MasonDataCacheDefaults "default_expires_in => 1 hour"
Any options passed to individual "$m->cache" calls override
these defaults.
- Disabling data caching
- If for some reason you want to disable data caching entirely, set the
default "cache_class" to "NullCache". This subclass
faithfully implements the cache API but never stores data.
This section explains Mason's various performance enhancements and how to
administer them. One of the best ways to maximize performance on your
production server is run in static_source mode; see the third subsection
below.
Code Cache¶
When Mason loads a component, it places it in a memory cache. By default, the
cache has no limit, but you can specify a maximum number of components to
cache with the code_cache_max_size parameter. In this case, Mason will free up
space as needed by discarding components. The discard algorithm is least
frequently used (LFU), with a periodic decay to gradually eliminate old
frequency information. In a nutshell, the components called most often in
recent history should remain in the cache.
Previous versions of Mason attempted to estimate the size of each component, but
this proved so inaccurate as to be virtually useless for cache policy. The max
size is now specified purely in number of components.
Mason can use certain optimizations with an unlimited cache, especially in
conjunction with static_source, so don't limit the cache unless experience
shows that your servers are growing too large. Many dynamic sites can be
served comfortably with all components in memory.
You can prepopulate the cache with components that you know will be accessed
often; see Preloading Components. Note that preloaded components possess no
special status in the cache and can be discarded like any others.
Naturally, a cache entry is invalidated if the corresponding component source
file changes.
To turn off code caching completely, set code_cache_max_size to 0.
Object Files¶
The in-memory code cache is only useful on a per-process basis. Each process
must build and maintain its own cache. Shared memory caches are conceivable in
the future, but even those will not survive between web server restarts.
As a secondary, longer-term cache mechanism, Mason stores a compiled form of
each component in an object file under data_dir/obj. Any server process can
eval the object file and save time on parsing the component source file. The
object file is recreated whenever the source file changes.
The object file pathname is formed from three parts:
- •
- the compiler "object_id" - this prevents different versions of
Mason or compilers from using the same object file, such as after an
upgrade
- •
- the component path
- •
- object_file_extension, by default ".obj"
Besides improving performance, object files can be useful for debugging. If you
feel the need to see what your source has been translated into, you can peek
inside an object file to see exactly how Mason converted a given component to
a Perl object. This was crucial for pre-1.10 Mason, in which error line
numbers were based on the object file rather than the source file.
If for some reason you don't want Mason to create object files, set
use_object_files to 0.
Static Source Mode¶
In static_source mode, Mason assumes that the component hierarchy is unchanging
and thus does not check source timestamps when using an in-memory cached
component or object file. This significantly reduces filesystem stats and
other overhead. We've seen speedups by a factor of two or three as a result of
this mode, though of course YMMV.
When in static_source mode, you must remove object files and call
$interp->flush_code_cache in order for the server to recognize component
changes. The easiest way to arrange this is to point static_source_touch_file
to a file that can be touched whenever components change.
We highly recommend running in this mode in production if you can manage it.
Many of Mason's future optimizations will be designed for this mode. On
development servers, of course, it makes sense to keep this off so that
components are reloaded automatically.
Disabling Autoflush¶
To support the dynamic autoflush feature, Mason has to check for autoflush mode
after printing every piece of text. If you can commit to not using autoflush,
setting enable_autoflush to 0 will allow Mason to compile components more
efficiently. Consider whether a few well-placed
"$m->flush_buffer" calls would be just as good as autoflush.
Write a handler subroutine¶
Writing your own "handler()" subroutine which uses an ApacheHandler
object (or objects) created during server startup is slightly faster (around
5% or so) than configuring mason via your
httpd.conf file and letting
Mason create its own ApacheHandler objects internally.
Preloading Components¶
You can tell Mason to preload a set of components in the parent process, rather
than loading them on demand, using the preloads parameter. Each child server
will start with those components loaded in the memory cache. The trade-offs
are:
- time
- a small one-time startup cost, but children save time by not having to
load the components
- memory
- a fatter initial server, but the memory for preloaded components are
shared by all children. This is similar to the advantage of using modules
only in the parent process.
Try to preload components that are used frequently and do not change often. (If
a preloaded component changes, all the children will have to reload it from
scratch.)
Preallocating the Output Buffer¶
You can set buffer_preallocate_size to set the size of the preallocated output
buffer for each request. This can reduce the number of reallocations Perl
performs as components output text.
ERROR REPORTING AND EXCEPTIONS¶
When an error occurs, Mason can respond by:
- •
- showing a detailed error message in the browser in HTML.
- •
- die'ing, which sends a 500 status to the browser and lets the error
message go to the error logs.
The first behavior is ideal for development, where you want immediate feedback
on the error. The second behavior is usually desired for production so that
users are not exposed to messy error messages. You choose the behavior by
setting error_mode to "output" or "fatal" respectively.
Error formatting is controlled by the error_format parameter. When showing
errors in the browser, Mason defaults to the "html" format. When the
error_mode is set to "fatal", the default format is
"line", which puts the entire error message on one line in a format
suitable for web server error logs. Mason also offers other formats, which are
covered in the Request class documentation.
Finally, you can use Apache's "ErrorDocument" directive to specify a
custom error handler for 500 errors. In this case, you'd set the error_mode to
"fatal". The URL specified by the "ErrorDocument"
directive could point to a Mason component.
Exceptions Under the Hood¶
The way that Mason really reports errors is through the use of exception
objects, which are implemented with the "Exception::Class" module
from CPAN, and some custom code in the HTML::Mason::Exceptions module.
If, during the execution of a component, execution stops because some code calls
"die()", then Mason will catch this exception. If the exception
being thrown is just a string, then it will be converted to an
"HTML::Mason::Exception" object. If the exception being thrown is an
object with a "rethrow()" method, then this method will be called.
Otherwise, Mason simply leaves the exception untouched and calls
"die()" again.
Calling a Component to Handle Errors
Returning to the topic of wrapper code that we covered earlier, what if you
wanted to handle all request errors by calling an error handling component?
There is no way to do this without wrapper code. Here's an example
"handler()" subroutine that does this:
sub handler {
my ($r) = @_;
my $return = eval { $ah->handle_request($r) };
if ( my $err = $@ )
{
$r->pnotes( error => $err );
$r->filename( $r->document_root . '/error/500.html' );
return $ah->handle_request($r);
}
return $return;
}
First, we wrap our call to "$ah->handle_request()" in an
"eval{}" block. If an error occurs, we store it in the request
object using the "$r->pnotes()" method. Then we change the
filename property of the Apache request object to point to our error-handling
component and call the "$ah->handle_request()" method again,
passing it the altered request object. We could have put the exception in
"$r->args", but we want to leave this untouched so that the
error-handling component can see the original arguments.
Here's what that component error-handling component might look like:
<html>
<head>
<title>Error</title>
</head>
<body>
<p>
Looks like our application broke. Whatever you did, don't do it again!
</p>
<p>
If you have further questions, please feel free to contact us at <a
href="mailto:support@example.com">support@example.com</a>.
</p>
<p><a href="/">Click here</a> to continue.</p>
</body>
</html>
<%init>
my $error = $r->pnotes('error');
my $error_text = "Page is " . $r->parsed_uri->unparse . "\n\n";
$error_text .= UNIVERSAL::can( $error, 'as_text' ) ? $error->as_text : $error;
$r->log_error($error_text);
my $mail =
MIME::Lite->new
( From => 'error-handler@example.com',
To => 'rt@example.com',
Subject => 'Application error',
Data => $error_text,
);
$r->register_cleanup( sub { $mail->send } );
</%init>
<%flags>
inherit => undef
</%flags>
This component does several things. First of all, it logs the complete error to
the Apache error logs, along with the complete URL, including query string,
that was requested. The "$r->parsed_uri()" method that we use
above is only available if the "Apache::URI" module has been loaded.
The component also sends an email containing the error, in this case to an RT
installation, so that the error is logged in a bug tracking system. Finally,
it displays a less technical error message to the user.
For this to work properly, you must set error_mode to "fatal", so that
Mason doesn't just display its own HTML error page.
RUNNING OUTSIDE OF MOD_PERL¶
Although Mason is most commonly used in conjunction with mod_perl, the APIs are
flexible enough to use in any environment. Below we describe the two most
common alternative environments, CGI and standalone scripts.
Using Mason from a CGI Script¶
The easiest way to use Mason via a CGI script is with the CGIHandler module
module.
Here is a skeleton CGI script that calls a component and sends the output to the
browser.
#!/usr/bin/perl
use HTML::Mason::CGIHandler;
my $h = HTML::Mason::CGIHandler->new
(
data_dir => '/home/jethro/code/mason_data',
);
$h->handle_request;
The relevant portions of the
httpd.conf file look like:
DocumentRoot /path/to/comp/root
ScriptAlias /cgi-bin/ /path/to/cgi-bin/
<LocationMatch "\.html$">
Action html-mason /cgi-bin/mason_handler.cgi
AddHandler html-mason .html
</LocationMatch>
<LocationMatch "^/cgi-bin/">
RemoveHandler .html
</LocationMatch>
<FilesMatch "(autohandler|dhandler)$">
Order allow,deny
Deny from all
</FilesMatch>
This simply causes Apache to call the mason_handler.cgi script every time a URL
ending in ".html" under the component root is requested.
To exclude certian directories from being under Mason control, you can use
something like the following:
<LocationMatch "^/(dir1|dir2|dir3)/">
RemoveHandler .html
</LocationMatch>
This script uses the CGIHandler class to do most of the heavy lifting. See that
class's documentation for more details.
Using Mason from a Standalone Script¶
Mason can be used as a pure text templating solution -- like Text::Template and
its brethren, but with more power (and of course more complexity).
Here is a bare-bones script that calls a component file and sends the result to
standard output:
#!/usr/bin/perl
use HTML::Mason;
use strict;
my $interp = HTML::Mason::Interp->new ();
$interp->exec(<relative path to file>, <args>...);
Because no component root was specified, the root is set to your current working
directory. If you have a well defined and contained component tree, you'll
probably want to specify a component root.
Because no data directory was specified, object files will not be created and
data caching will not work in the default manner. If performance is an issue,
you will want to specify a data directory.
Here's a slightly fuller script that specifies a component root and data
directory, and captures the result in a variable rather than sending to
standard output:
#!/usr/bin/perl
use HTML::Mason;
use strict;
my $outbuf;
my $interp = HTML::Mason::Interp->new
(comp_root => '/path/to/comp_root',
data_dir => '/path/to/data_dir',
out_method => \$outbuf
);
$interp->exec(<component-path>, <args>...);
# Do something with $outbuf
SEE ALSO¶
Mason
AUTHORS¶
- •
- Jonathan Swartz <swartz@pobox.com>
- •
- Dave Rolsky <autarch@urth.org>
- •
- Ken Williams <ken@mathforum.org>
COPYRIGHT AND LICENSE¶
This software is copyright (c) 2012 by Jonathan Swartz.
This is free software; you can redistribute it and/or modify it under the same
terms as the Perl 5 programming language system itself.