HTML::...:Admin(3pm)
π HTML::Mason::Admin π
okay bestie, welcome to the Mason Administrator's Manual β that's the HTML::Mason::Admin page, and yes, it's giving sysadmin era. pull up a chair πͺ
β¨ DESCRIPTION β¨
so like, this whole manual is for the sysadmin/webmaster bestie in charge of installing, configuring, or tuning a Mason system. the bulk of it lowkey assumes you're running mod_perl. π
if you're not on mod_perl, no stress β go peek at RUNNING OUTSIDE OF MOD_PERL for the deets. and for more mod_perl lore in general, the mod_perl website is right there at http://perl.apache.org/. π
πΈ SITE CONFIGURATION METHODS πΈ
Mason ships with a module that's literally purpose-built to glue Mason and mod_perl (both 1 and 2) together: HTML::Mason::ApacheHandler. tell mod_perl to hand content requests to this module and boom β Mason is generating your web pages. π
there are two ways to configure Mason under mod_perl:
- Basic β Mason comes with reasonable default behavior under mod_perl, so honestly it can be as simple as adding two directives to your Apache config file. throughout this document we're gonna assume your Apache config file is called
httpd.conf. add more configuration parameters to it and you unlock more complex behaviors. easy. - Advanced β if basic just isn't flexible enough for you (valid!), you can wrap Mason in a custom mod_perl handler. the wrapper code you write can either create its own Mason objects, or it can take advantage of
httpd.confconfiguration parameters and let Mason create the objects it needs all by itself. main character energy either way. β¨
our rec? start with basic and work your way forward as the need for flexibility arises. no skipping ahead bestie.
also, Mason is super flexible β you can replace parts of it by creating your own classes. this documentation assumes you're simply using the classes provided in the Mason distribution. Subclassing gets its own whole document (the Subclassing doc, iykyk). the two topics are orthogonal, so you can absolutely mix the configuration techniques from here with your own custom subclasses. do both, it's fine. π«Ά
π BASIC CONFIGURATION VIA httpd.conf DIRECTIVES π
the absolutely, most minimal, bare-bones config looks like this:
PerlModule HTML::Mason::ApacheHandler
<Location />
SetHandler perl-script
PerlHandler HTML::Mason::ApacheHandler
</Location>
what's happening here? this tells Apache to serve all URLs through Mason (heads up β the next section has a more realistic strategy, don't @ me). the PerlModule line tells mod_perl to load Mason once at startup time, which saves time and memory. iconic. β and because this example doesn't set any Mason configuration parameters at all, Mason just uses its default values. no notes.
if this is your first time installing and using Mason, we recommend you run the above configuration in a test webserver first. that way you can play with Mason under mod_perl with a minimum of fuss. once you've gotten it working, come back and read the rest of the document for further possibilities. π¦
π Controlling Access via Filename Extension π
turns out, serving every URL through Mason is a bad idea, for two reasons:
- 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
<%. those files should instead be served by Apache's default content handler. - Mason should be prevented from serving private (non-top-level) Mason components to users. like, imagine you used a utility component to perform arbitrary sql queries β you would not want external users able to access it via a URL. requests for private components should simply result in a
404 NOT_FOUND. periodt.
the easiest way to tell different types of files apart is with filename extensions. many naming schemes are possible, but here's what we suggest: use "normal" extensions for top-level components, and add an m prefix for private components. like so:
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 it transparent that you're using Mason versus some other package. slay. π
here's 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>
break it down: 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, which stops unscrupulous users from even knowing which private components exist. sneaky-proof. π₯ any other file extensions (e.g. .gif, .tgz) get served by Apache's default content handler.
you might prefer FilesMatch over LocationMatch. however β and listen closely bestie β be aware that LocationMatch will work best in conjunction with Mason's dhandlers. π
π Configuration Parameters π
Mason lets you flexibly configure its behavior via httpd.conf configuration parameters. yes ma'am.
these configuration parameters get set via mod_perl's PerlSetVar and PerlAddVar directives. even 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 it contains whitespace. the quotes will be automatically removed by Apache before Mason ever sees the variable.
- boolean β the variable's value is used as a boolean, and is subject to Perl's rules on truth/falseness. we recommend you use
0(false) or1(true) for these arguments. keep it simple. - 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 { ... }"
and a named subroutine reference would look like this:
PerlSetVar MasonOutMode "\&Some::Module::handle_output"
- list β to set a list parameter, use
PerlAddVarfor the values, like this:
PerlAddVar MasonPreloads /foo/bar/baz.comp
PerlAddVar MasonPreloads /foo/bar/quux.comp
- hash_list β just like a list parameter, use
PerlAddVarfor the values. however, in the case of ahash_list, each element should be a key/value pair separated by=>:
PerlAddVar MasonDataCacheDefaults "cache_class => MemoryCache"
PerlAddVar MasonDataCacheDefaults "namespace => foo"
heads up bestie: take note that the right hand side of each pair should not be quoted. π¨
see HTML::Mason::Params for a full list of parameters, and their associated types. go forth and configure, queen π
π GENERAL SERVER CONFIGURATION π
π Component Root
okay bestie so the component root (comp_root) is literally the ceiling of your whole component hierarchy β it marks the top π
. When you're running Mason with the ApacheHandler or CGIHandler modules, this defaults to your document root, no cap.
The component root is the thing that decides how component paths turn into actual real-life file paths. So if your component root is /usr/local/httpd/docs, then a component path of /products/index.html translates to the file /usr/local/httpd/docs/products/index.html. Math-ing β¨.
You literally cannot call a component outside the component root. Nope. If Apache passes a file through Mason that lives outside the component root (say, as the result of an Alias), you're getting a 404 and a warning in the logs. it's giving "denied" π.
You can also specify multiple component roots, in the spirit of Perl's @INC π«Ά. Each root gets assigned a key that identifies it mnemonically. Like, in httpd.conf:
PerlAddVar MasonCompRoot "private => /usr/home/joe/comps"
PerlAddVar MasonCompRoot "main => /usr/local/www/htdocs"
So 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. iconic.
The component root keys must be unique in a case-insensitive comparison, just so you know π. The keys get 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's a directory called mason under your Apache server root πΈ. And here's the tea: because Mason will not use a default data directory under a top-level directory, you'll 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 it needs to, and set its permissions according to the web server User/Group. slay, it handles itself π.
π¦ External Modules
Components will often need access to external Perl modules, and there are several ways to load them, bestie.
- The httpd
PerlModuledirective:
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, and I'm gonna be real with you about them:
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. iykyk π€.
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/. rude, honestly π.
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(). don't forget, bestie π
.
π Configuring Virtual Sites
These examples extend the single site configurations given so far, so buckle up π¦.
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. lowkey a secret club π.
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 β¨
Okay bestie, so like we mentioned up top, you can absolutely write your own custom mod_perl content handler that wraps around Mason and gives you basically unlimited flexibility when it comes to handling requests. No cap. In this section we're gonna show you some basic wrappers and re-implement a few of the things we already talked about β like declining image requests and protecting private components. You know, the classics. π
Plus we're gonna chat about all the extra possibilities that unlock when you build a custom wrapper around Mason's request handling mechanism. This wrapper is generally giving two parts, bestie:
- The initialization portion, which runs at server startup, loads up any modules you need and creates your objects. β¨
- The other part is the
handler()subroutine, which handles the actual web page requests. πΈ
Writing a Wrapper π
To create a wrapper, you literally just define a handler() subroutine in whatever package you want, and then tell mod_perl to use it as a content handler. The file that defines the handler() subroutine can be a module, or you can just load a simple file containing that subroutine definition. The file approach was, for a long time, the only way to configure Mason β and that file was traditionally called handler.pl. It's giving legacy, iykyk. π
Nowadays though, we recommend you make a custom module in the appropriate namespace and define your handler() subroutine there. The glow-up here is that it uses well-known techniques for creating and installing modules. Lowkey it requires a bit more work than just 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. Valid.
The eg/ directory of the Mason distribution contains a couple sample modules that define handler() subroutines. Let's assume your module, like the example, defines a handler() in the package MyApp::Mason. In that 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. Those references will generally be applicable to any custom code wrapping Mason, so don't stress. π«Ά
Wrappers and PerlSetVar-style configuration
Sometimes people try to write a wrapper and configure Mason with PerlSetVar directives in their Apache configuration file. Bestie. 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. Periodt. However β you can mix the two methods. See Mixing httpd.conf Configuration with a Wrapper below. π
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. Same vibe, different font.
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 figure out what component is being called. π
Let's peek 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. It's giving bare minimum. π However, it does serve as a great skeleton to which additional functionality can easily be added. Main character energy for a skeleton, honestly.
External Modules Revisited π
Since you're 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 simply 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. Don't forget, bestie!! β
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. Slay. π
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;
}
Not me writing security checks with β¨magic hand-wavingβ¨. We stay silly but we stay secure. π₯
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 heads up: you lose the memory savings of creating all your objects during server startup. Trade-offs, bestie. π
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. Iconic, flexible, chef's kiss. π
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. Remix culture, fr fr. π
Alternately you could subclass the HTML::Mason::ApacheHandler class, and override the handler() method it provides. See the Subclassing documentation for more details. And of course, you could even create a subclass and write a wrapper that called it. The power. The range. π
π» DEVELOPMENT π»
okay bestie, this section is all about the lil dev-quality-of-life features, so let's get into it π οΈ
π Global Variables π
so, global variables? they can make your programs harder to read, harder to maintain, and harder to debug, and ngl that's just as true for Mason components as it is for anything else. and because mod_perl keeps the environment persistent, globals need extra care with initialization and cleanup. it's giving high-maintenance π
that said, there are times when it's lowkey super useful to make a value available to all Mason components: a DBI database handle, a hash of user session info, the server root for building absolute URLs. iconic use cases fr.
because Mason by default parses components in "strict" mode, you'll need to declare a global if you don't wanna access it with an explicit package name. the easiest way to declare a global is with the allow_globals parameter. periodt.
and since all components run in the same package, you can set the global in one component and access it in all the others. slay.
Autohandlers are the common place to assign values to globals. use the <%once> section if the global only needs initializing at load time, or the <%init> section if it needs initializing every single request. choose wisely bestie π«Ά
πͺ 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. no cap, that's handy.
πΎ Data Caching πΎ
data caching is implemented with DeWitt Clinton's "Cache::Cache" module. for a full understanding of this section you should read the documentation for "Cache::Cache" as well as for relevant subclasses (e.g. "Cache::FileCache"). go read the docs, i'm literally begging π
π 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". cute lil file tree honestly π³
βοΈ 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. override, not merge β got it? π
π« Disabling data caching π«
if for some reason you wanna disable data caching entirely, set the default "cache_class" to "NullCache". this subclass faithfully implements the cache API but never stores data. it's giving "i'm here but i'm not really here" π
β‘ PERFORMANCE β‘
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. bestie, bookmark that one π
π§ 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 that 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. main character components stay, side characters get cut π
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. iconic.
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. they're not VIPs, bestie, sorry π
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. history lesson, but make it β¨ useful β¨
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. not me getting hyped over filesystem stats π
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. dev server = vibes, prod server = static_source. periodt π«Ά
π° 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. like, maybe you don't need autoflush, maybe you just need better timing bestie π
π§βπ» 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.) choose your preloads like you choose your faves: carefully π
π§Ί 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. fewer reallocations, more slay β¨
π ERROR REPORTING AND EXCEPTIONS π
So when something goes π wrong, Mason has two whole moods it can be in:
- showing a detailed error message right in the browser, in HTML. (the drama, the transparency)
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, like bestie I need to know right now what broke. The second behavior is usually what you want for production, so that users are not exposed to messy error messages. (protect the civilians fr) 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. go read that one too, iykyk
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. the lore
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. no cap, it just rethrows and minds its business π
π 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. (respect the originals π)
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. heads up bestie β οΈ
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. gentle parenting the end user fr
For this to work properly, you must seterror_modeto"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. versatility queen π
π¦ Using Mason from a CGI Script
The easiest way to use Mason via a CGI script is with the CGIHandler 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. so efficient π«
To exclude certain 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). main character energy honestly
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. ngl this matters, don't skip it π«Ά
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
and that's a wrap on the whole page bestie!! π this one was shipped as perl v5.38.2, dated 2024-03-05, and it's the HTML::...:Admin(3pm) page β so go forth, configure your Mason, and slay π
β¨π¦
π¦ where this page comes from
- debian trixie: libhtml-mason-perl 1:1.60-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libhtml-mason-perl 1:1.60-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libhtml-mason-perl 1:1.60-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms