|
Mason Administrator's Manual (version 1.39)
NAMEHTML::Mason::Admin - Mason Administrator's Manual
DESCRIPTIONThis 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 METHODSMason includes a module specifically designed to integrate Mason and
mod_perl (1 and 2),
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 DIRECTIVESThe 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 ExtensionAs it turns out, serving every URL through Mason is a bad idea for two reasons:
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
Configuration ParametersMason allows you to flexibly configure its behavior via httpd.conf configuration parameters. These configuration parameters are set via mod_perl's
See HTML::Mason::Params for a full list of parameters, and their associated types.
GENERAL SERVER CONFIGURATION
Component RootThe 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
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 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
Data DirectoryThe 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 ModulesComponents will often need access to external Perl modules. There are several ways to load them.
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 ( If you want to preload the modules in your httpd.conf file, and
still have them export symbols into the
<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 RequestsBy 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
Configuring Virtual SitesThese examples extend the single site configurations given so far.
Multiple sites, one component rootIf 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 rootsIf 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 CONFIGURATIONAs 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
Writing a WrapperTo create a wrapper, you simply need to define a Nowadays, we recommend that you create a custom module in the
appropriate namespace and define your The eg/ directory of the Mason distribution contains a couple
sample modules that define 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 configurationSometimes people attempt to write a wrapper and configure Mason
with PerlHandler HTML::Mason::ApacheHandler it will dispatch directly to the
Wrapping with a <Perl> blockYou can also put your wrapper code in a
The Wrapper CodeRegardless of how you load your wrapper code, it will always work the
same way. The 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 RevisitedSince 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 Alternatively, you might consider creating a separate piece of code to
load the modules you need. For example, you might create a module
called
{
package HTML::Mason::Commands;
use Apache::Constants qw(:common);
use Apache::URI;
use File::Temp;
}
1; This can be loaded via a
Example: Controlling access with component attributesAn 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
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 HostsIf 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
<VirtualHost site1.example.com>
PerlSetVar SiteName site1
<LocationMatch ...>
SetHandler perl-script
PerlHandler MyApp::Mason
</LocationMatch>
</VirtualHost>
Creating apachehandler objects on the flyYou 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 wrapperIf you have some code which must always run after a request, then
the only way to guarantee that this happens is to wrap the
Mixing httpd.conf Configuration with a WrapperYou 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 package MyApp::Mason; use strict; use HTML::Mason::ApacheHandler;
sub handler {
my ($r) = @_;
return HTML::Mason::ApacheHandler->handler($r);
}
The Alternately you could subclass the
DEVELOPMENTThis section describes how to set up common developer features.
Global VariablesGlobal 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 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
SessionsMason does not have a built-in session mechanism, but you can use the
Data CachingData caching is implemented with DeWitt Clinton's
PERFORMANCEThis 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 CacheWhen 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 FilesThe 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:
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 ModeIn 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 AutoflushTo 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
Write a handler subroutineWriting your own
Preloading ComponentsYou 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:
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 BufferYou 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 EXCEPTIONSWhen an error occurs, Mason can respond by:
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
Exceptions Under the HoodThe way that Mason really reports errors is through the use of
exception objects, which are implemented with the If, during the execution of a component, execution stops because some
code calls
Calling a Component to Handle ErrorsReturning 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
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 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 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_PERLAlthough 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 ScriptThe 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 ScriptMason 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
AUTHORSJonathan Swartz <swartz@pobox.com>, Dave Rolsky <autarch@urth.org>, Ken Williams <ken@mathforum.org>
SEE ALSOHTML::Mason, HTML::Mason::Interp, HTML::Mason::ApacheHandler, HTML::Mason::Lexer, HTML::Mason::Compiler |