[ Team LiB ] Previous Section Next Section

4.2 mod_perl Configuration

When you have tested that the Apache server works on your machine, it's time to configure the mod_perl part. Although some of the configuration directives are already familiar to you, mod_perl introduces a few new ones.

It's a good idea to keep all mod_perl-related configuration at the end of the configuration file, after the native Apache configuration directives, thus avoiding any confusion.

To ease maintenance and to simplify multiple-server installations, the mod_perl-enabled Apache server configuration system provides several alternative ways to keep your configuration directives in separate places. The Include directive in httpd.conf lets you include the contents of other files, just as if the information were all contained in httpd.conf. This is a feature of Apache itself. For example, placing all mod_perl-related configuration in a separate file named conf/mod_perl.conf can be done by adding the following directive to httpd.conf:

Include conf/mod_perl.conf

If you want to include this configuration conditionally, depending on whether your Apache has been compiled with mod_perl, you can use the IfModule directive :

<IfModule mod_perl.c>
  Include conf/mod_perl.conf
</IfModule>

mod_perl adds two more directives. <Perl> sections allow you to execute Perl code from within any configuration file at server startup time. Additionally, any file containing a Perl program can be executed at server startup simply by using the PerlRequire or PerlModule directives, as we will show shortly.

4.2.1 Alias Configurations

For many reasons, a server can never allow access to its entire directory hierarchy. Although there is really no indication of this given to the web browser, every path given in a requested URI is therefore a virtual path; early in the processing of a request, the virtual path given in the request must be translated to a path relative to the filesystem root, so that Apache can determine what resource is really being requested. This path can be considered to be a physical path, although it may not physically exist.

For instance, in mod_perl systems, you may intend that the translated path does not physically exist, because your module responds when it sees a request for this non-existent path by sending a virtual document. It creates the document on the fly, specifically for that request, and the document then vanishes. Many of the documents you see on the Web (for example, most documents that change their appearance depending on what the browser asks for) do not physically exist. This is one of the most important features of the Web, and one of the great powers of mod_perl is that it allows you complete flexibility to create virtual documents.

The ScriptAlias and Alias directives provide a mapping of a URI to a filesystem directory. The directive:

Alias /foo /home/httpd/foo

will map all requests starting with /foo to the files starting with /home/httpd/foo/. So when Apache receives a request to http://www.example.com/foo/test.pl, the server will map it to the file test.pl in the directory /home/httpd/foo/.

Additionally, ScriptAlias assigns all the requests that match the specified URI (i.e., /cgi-bin) to be executed by mod_cgi.

ScriptAlias /cgi-bin /home/httpd/cgi-bin

is actually the same as:

Alias /cgi-bin /home/httpd/cgi-bin
<Location /cgi-bin>
    SetHandler cgi-script
    Options +ExecCGI
</Location>

where the SetHandler directive invokes mod_cgi. You shouldn't use the ScriptAlias directive unless you want the request to be processed under mod_cgi. Therefore, when configuring mod_perl sections, use Alias instead.

Under mod_perl, the Alias directive will be followed by a section with at least two directives. The first is the SetHandler/perl-script directive, which tells Apache to invoke mod_perl to run the script. The second directive (for example, PerlHandler) tells mod_perl which handler (Perl module) the script should be run under, and hence for which phase of the request. Later in this chapter, we discuss the available Perl*Handlers[1] for the various request phases. A typical mod_perl configuration that will execute the Perl scripts under the Apache::Registry handler looks like this:

[1] When we say Perl*Handler, we mean the collection of all Perl handler directives (PerlHandler, PerlAccessHandler, etc.).

Alias /perl/ /home/httpd/perl/
<Location /perl>
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options +ExecCGI
</Location>

The last directive tells Apache to execute the file as a program, rather than return it as plain text.

When you have decided which methods to use to run your scripts and where you will keep them, you can add the configuration directive(s) to httpd.conf. They will look like those below, but they will of course reflect the locations of your scripts in your filesystem and the decisions you have made about how to run the scripts:

ScriptAlias /cgi-bin/ /home/httpd/cgi-bin/
Alias       /perl/    /home/httpd/perl/
<Location /perl>
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options +ExecCGI
</Location>

In the examples above, all requests issued for URIs starting with /cgi-bin will be served from the directory /home/httpd/cgi-bin/, and those starting with /perl will be served from the directory /home/httpd/perl/.

4.2.1.1 Running scripts located in the same directory under different handlers

Sometimes you will want to map the same directory to a few different locations and execute each file according to the way it was requested. For example, in the following configuration:

# Typical for plain cgi scripts:
ScriptAlias /cgi-bin/  /home/httpd/perl/

# Typical for Apache::Registry scripts:
Alias       /perl/     /home/httpd/perl/

# Typical for Apache::PerlRun scripts:
Alias       /cgi-perl/ /home/httpd/perl/

<Location /perl/>
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options +ExecCGI
</Location>
<Location /cgi-perl/>
    SetHandler perl-script
    PerlHandler Apache::PerlRun
    Options +ExecCGI
</Location>

the following three URIs:

http://www.example.com/perl/test.pl
http://www.example.com/cgi-bin/test.pl
http://www.example.com/cgi-perl/test.pl

are all mapped to the same file, /home/httpd/perl/test.pl. If test.pl is invoked with the URI prefix /perl, it will be executed under the Apache::Registry handler. If the prefix is /cgi-bin, it will be executed under mod_cgi, and if the prefix is /cgi-perl, it will be executed under the Apache::PerlRun handler.

This means that we can have all our CGI scripts located at the same place in the filesystem and call the script in any of three ways simply by changing one component of the URI (cgi-bin|perl|cgi-perl).

This technique makes it easy to migrate your scripts to mod_perl. If your script does not seem to work while running under mod_perl, in most cases you can easily call the script in straight mod_cgi mode or under Apache::PerlRun without making any script changes. Simply change the URL you use to invoke it.

Although in the configuration above we have configured all three Aliases to point to the same directory within our filesystem, you can of course have them point to different directories if you prefer.

This should just be a migration strategy, though. In general, it's a bad idea to run scripts in plain mod_cgi mode from a mod_perl-enabled server—the extra resource consumption is wasteful. It is better to run these on a plain Apache server.

4.2.2 <Location /perl> Sections

The <Location> section assigns a number of rules that the server follows when the request's URI matches the location. Just as it is a widely accepted convention to use /cgi-bin for mod_cgi scripts, it is habitual to use /perl as the base URI of the Perl scripts running under mod_perl. Let's review the following very widely used <Location> section:

Alias /perl/ /home/httpd/perl/
PerlModule Apache::Registry
<Location /perl>
    SetHandler perl-script
    PerlHandler Apache::Registry
    Options +ExecCGI
    Allow from all
    PerlSendHeader On
</Location>

This configuration causes all requests for URIs starting with /perl to be handled by the mod_perl Apache module with the handler from the Apache::Registry Perl module.

Remember the Alias from the previous section? We use the same Alias here. If you use a <Location> that does not have the same Alias, the server will fail to locate the script in the filesystem. You need the Alias setting only if the code that should be executed is located in a file. Alias just provides the URI-to-filepath translation rule.

Sometimes there is no script to be executed. Instead, a method in a module is being executed, as with /perl-status, the code for which is stored in an Apache module. In such cases, you don't need Alias settings for these <Location>s.

PerlModule is equivalent to Perl's native use( ) function call. We use it to load the Apache::Registry module, later used as a handler in the <Location> section.

Now let's go through the directives inside the <Location> section:

SetHandler perl-script

The SetHandler directive assigns the mod_perl Apache module to handle the content generation phase.

PerlHandler Apache::Registry

The PerlHandler directive tells mod_perl to use the Apache::Registry Perl module for the actual content generation.

Options +ExecCGI

Options +ExecCGI ordinarily tells Apache that it's OK for the directory to contain CGI scripts. In this case, the flag is required by Apache::Registry to confirm that you really know what you're doing. Additionally, all scripts located in directories handled by Apache::Registry must be executable, another check against wayward non-script files getting left in the directory accidentally. If you omit this option, the script either will be rendered as plain text or will trigger a Save As dialog, depending on the client. [2]

[2] You can use Apache::RegistryBB to skip this and a few other checks.

Allow from all

The Allow directive is used to set access control based on the client's domain or IP adress. The from all setting allows any client to run the script.

PerlSendHeader On

The PerlSendHeader On line tells mod_perl to intercept anything that looks like a header line (such as Content-Type: text/html) and automatically turn it into a correctly formatted HTTP header the way mod_cgi does. This lets you write scripts without bothering to call the request object's send_http_header( ) method, but it adds a small overhead because of the special handling.

If you use CGI.pm's header( ) function to generate HTTP headers, you do not need to activate this directive, because CGI.pm detects that it's running under mod_perl and calls send_http_header( ) for you.

You will want to set PerlSendHeader Off for non-parsed headers (nph) scripts and generate all the HTTP headers yourself. This is also true for mod_perl handlers that send headers with the send_http_header( ) method, because having PerlSendHeader On as a server-wide configuration option might be a performance hit.

</Location>

</Location> closes the <Location> section definition.

Overriding <Location> Settings

Suppose you have:

<Location /foo>
    SetHandler perl-script
    PerlHandler Book::Module
 </Location>

To remove a mod_perl handler setting from a location beneath a location where a handler is set (e.g., /foo/bar), just reset the handler like this:

<Location /foo/bar>
    SetHandler default-handler
</Location>

Now all requests starting with /foo/bar will be served by Apache's default handler, which serves the content directly.

4.2.3 PerlModule and PerlRequire

As we saw earlier, a module should be loaded before its handler can be used. PerlModule and PerlRequire are the two mod_perl directives that are used to load modules and code. They are almost equivalent to Perl's use( ) and require( ) functions (respectively) and are called from the Apache configuration file. You can pass one or more module names as arguments to PerlModule:

PerlModule Apache::DBI CGI DBD::Mysql

Generally, modules are preloaded from the startup script, which is usually called startup.pl. This is a file containing Perl code that is executed through the PerlRequire directive. For example:

PerlRequire  /home/httpd/perl/lib/startup.pl

A PerlRequire filename can be absolute or relative to the ServerRoot or to a path in @INC.

As with any file with Perl code that gets use( )d or require( )d, it must return a true value. To ensure that this happens, don't forget to add 1; at the end of startup.pl.

4.2.4 Perl*Handlers

As mentioned in Chapter 1, Apache specifies 11 phases of the request loop. In order of processing, they are: Post-read-request, URI translation, header parsing, access control, authentication, authorization, MIME type checking, fixup, response (also known as the content handling phase), logging, and finally cleanup. These are the stages of a request where the Apache API allows a module to step in and do something. mod_perl provides dedicated configuration directives for each of these stages:

PerlPostReadRequestHandler
PerlInitHandler
PerlTransHandler
PerlHeaderParserHandler
PerlAccessHandler
PerlAuthenHandler
PerlAuthzHandler
PerlTypeHandler
PerlFixupHandler
PerlHandler
PerlLogHandler
PerlCleanupHandler

These configuration directives usually are referred to as Perl*Handler directives. The * in Perl*Handler is a placeholder to be replaced by something that identifies the phase to be handled. For example, PerlLogHandler is the Perl handler that (fairly obviously) handles the logging phase.

In addition, mod_perl adds a few more stages that happen outside the request loop:

PerlChildInitHandler

Allows your modules to initialize data structures during the startup of the child process.

PerlChildExitHandler

Allows your modules to clean up during the child process shutdown.

PerlChildInitHandler and PerlChildExitHandler might be used, for example, to allocate and deallocate system resources, pre-open and close database connections, etc. They do not refer to parts of the request loop.

PerlRestartHandler

Allows you to specify a routine that is called when the server is restarted. Since Apache always restarts itself immediately after it starts, this is a good phase for doing various initializations just before the child processes are spawned.

PerlDispatchHandler

Can be used to take over the process of loading and executing handler code. Instead of processing the Perl*Handler directives directly, mod_perl will invoke the routine pointed to by PerlDispatchHandler and pass it the Apache request object and a second argument indicating the handler that would ordinarily be invoked to process this phase. So for example, you can write a PerlDispatchHandler handler with a logic that will allow only specific code to be executed.

Since most mod_perl applications need to handle only the response phase, in the default compilation, most of the Perl*Handlers are disabled. During the perl Makefile.PL mod_perl build stage, you must specify whether or not you will want to handle parts of the request loop other than the usual content generation phase. If this is the case, you need to specify which phases, or build mod_perl with the option EVERYTHING=1, which enables them all. All the build options are covered in detail in Chapter 3.

Note that it is mod_perl that recognizes these directives, not Apache. They are mod_perl directives, and an ordinary Apache server will not recognize them. If you get error messages about these directives being "perhaps mis-spelled," it is a sure sign that the appropriate part of mod_perl (or the entire mod_perl module!) is missing from your server.

All <Location>, <Directory>, and <Files> sections contain a physical path specification. Like PerlChildInitHandler and PerlChildExitHandler, the directives PerlPostReadRequestHandler and PerlTransHandler cannot be used in these sections, nor in .htaccess files, because the path translation isn't completed and a physical path isn't known until the end of the translation (PerlTransHandler) phase.

PerlInitHandler is more of an alias; its behavior changes depending on where it is used. In any case, it is the first handler to be invoked when serving a request. If found outside any <Location>, <Directory>, or <Files> section, it is an alias for PerlPostReadRequestHandler. When inside any such section, it is an alias for PerlHeaderParserHandler.

Starting with the header parsing phase, the requested URI has been mapped to a physical server pathname, and thus PerlHeaderParserHandler can be used to match a <Location>, <Directory>, or <Files> configuration section, or to process an .htaccess file if such a file exists in the specified directory in the translated path.

PerlDispatchHandler, PerlCleanupHandler, and PerlRestartHandler do not correspond to parts of the Apache API, but allow you to fine-tune the mod_perl API. They are specified outside configuration sections.

The Apache documentation and the book Writing Apache Modules with Perl and C (O'Reilly) provide in-depth information on the request phases.

4.2.5 The handler( ) Subroutine

By default, the mod_perl API expects a subroutine named handler( ) to handle the request in the registered Perl*Handler module. Thus, if your module implements this subroutine, you can register the handler with mod_perl by just specifying the module name. For example, to set the PerlHandler to Apache::Foo::handler, the following setting would be sufficient:

PerlHandler Apache::Foo

mod_perl will load the specified module for you when it is first used. Please note that this approach will not preload the module at startup. To make sure it gets preloaded, you have three options:

  • You can explicitly preload it with the PerlModule directive:

    PerlModule Apache::Foo
  • You can preload it in the startup file:

    use Apache::Foo ( );
  • You can use a nice shortcut provided by the Perl*Handler syntax:

    PerlHandler +Apache::Foo

    Note the leading + character. This directive is equivalent to:

    PerlModule Apache::Foo
    <Location ..>
        ...
        PerlHandler Apache::Foo
    </Location>

If you decide to give the handler routine a name other than handler( ) (for example, my_handler( )), you must preload the module and explicitly give the name of the handler subroutine:

PerlModule Apache::Foo
<Location ..>
    ...
    PerlHandler Apache::Foo::my_handler
</Location>

This configuration will preload the module at server startup.

If a module needs to know which handler is currently being run, it can find out with the current_callback( ) method. This method is most useful to PerlDispatchHandlers that take action for certain phases only.

if ($r->current_callback eq "PerlLogHandler") {
    $r->warn("Logging request");
}

4.2.6 Investigating the Request Phases

Imagine a complex server setup in which many different Perl and non-Perl handlers participate in the request processing, and one or more of these handlers misbehaves. A simple example is one where one of the handlers alters the request record, which breaks the functionality of other handlers. Or maybe a handler invoked first for any given phase of the process returns an unexpected OK status, thus preventing other handlers from doing their job. You can't just add debug statements to trace the offender—there are too many handlers involved.

The simplest solution is to get a trace of all registered handlers for each phase, stating whether they were invoked and what their return statuses were. Once such a trace is available, it's much easier to look only at the players that actually participated, thus narrowing the search path down a potentially misbehaving module.

The Apache::ShowRequest module shows the phases the request goes through, displaying module participation and response codes for each phase. The content response phase is not run, but possible modules are listed as defined. To configure it, just add this snippet to httpd.conf:

<Location /showrequest>
    SetHandler perl-script
    PerlHandler +Apache::ShowRequest
</Location>

To see what happens when you access some URI, add the URI to /showrequest. Apache::ShowRequest uses PATH_INFO to obtain the URI that should be executed. So, to run /index.html with Apache::ShowRequest, issue a request for /showrequest/index.html. For /perl/test.pl, issue a request for /showrequest/perl/test.pl.

This module produces rather lengthy output, so we will show only one section from the report generated while requesting /showrequest/index.html:

Running request for /index.html
Request phase: post_read_request
  [snip]
Request phase: translate_handler
   mod_perl ....................DECLINED
   mod_setenvif ................undef
   mod_auth ....................undef
   mod_access ..................undef
   mod_alias ...................DECLINED
   mod_userdir .................DECLINED
   mod_actions .................undef
   mod_imap ....................undef
   mod_asis ....................undef
   mod_cgi .....................undef
   mod_dir .....................undef
   mod_autoindex ...............undef
   mod_include .................undef
   mod_info ....................undef
   mod_status ..................undef
   mod_negotiation .............undef
   mod_mime ....................undef
   mod_log_config ..............undef
   mod_env .....................undef
   http_core ...................OK
Request phase: header_parser
  [snip]
Request phase: access_checker
  [snip]
Request phase: check_user_id
  [snip]
Request phase: auth_checker
  [snip]
Request phase: type_checker
  [snip]
Request phase: fixer_upper
  [snip]
Request phase: response handler (type: text/html)
   mod_actions .................defined
   mod_include .................defined
   http_core ...................defined
Request phase: logger
  [snip]

For each stage, we get a report of what modules could participate in the processing and whether they took any action. As you can see, the content response phase is not run, but possible modules are listed as defined. If we run a mod_perl script, the response phase looks like:

Request phase: response handler (type: perl-script)
   mod_perl ....................defined

4.2.7 Stacked Handlers

With the mod_perl stacked handlers mechanism, it is possible for more than one Perl*Handler to be defined and executed during any stage of a request.

Perl*Handler directives can define any number of subroutines. For example:

PerlTransHandler Foo::foo Bar::bar

Foo::foo( ) will be executed first and Bar::bar( ) second. As always, if the subroutine's name is handler( ), you can omit it.

With the Apache->push_handlers( ) method, callbacks (handlers) can be added to a stack at runtime by mod_perl modules.

Apache->push_handlers( ) takes the callback handler name as its first argument and a subroutine name or reference as its second. For example, let's add two handlers called my_logger1( ) and my_logger2( ) to be executed during the logging phase:

use Apache::Constants qw(:common);
sub my_logger1 {
    #some code here
    return OK;
}
sub my_logger2 {
    #some other code here
    return OK;
}
Apache->push_handlers("PerlLogHandler", \&my_logger1);
Apache->push_handlers("PerlLogHandler", \&my_logger2);

You can also pass a reference to an anonymous subroutine. For example:

use Apache::Constants qw(:common);

Apache->push_handlers("PerlLogHandler", sub {
    print STDERR "_ _ANON_ _ called\n";
    return OK;
});

After each request, this stack is erased.

All handlers will be called in turn, unless a handler returns a status other than OK or DECLINED.

To enable this feature, build mod_perl with:

panic% perl Makefile.PL PERL_STACKED_HANDLERS=1 [ ... ]

or:

panic% perl Makefile.PL EVERYTHING=1 [ ... ]

To test whether the version of mod_perl you're running can stack handlers, use the Apache->can_stack_handlers method. This method will return a true value if mod_perl was configured with PERL_STACKED_HANDLERS=1, and a false value otherwise.

Let's look at a few real-world examples where this method is used:

  • The widely used CGI.pm module maintains a global object for its plain function interface. Since the object is global, under mod_perl it does not go out of scope when the request is completed, and the DESTROY method is never called. Therefore, CGI->new arranges to call the following code if it detects that the module is used in the mod_perl environment:

    Apache->push_handlers("PerlCleanupHandler", \&CGI::_reset_globals);

    This function is called during the final stage of a request, resetting CGI.pm's globals before the next request arrives.

  • Apache::DCELogin establishes a DCE login context that must exist for the lifetime of a request, so the DCE::Login object is stored in a global variable. Without stacked handlers, users must set the following directive in the configuration file to destroy the context:

    PerlCleanupHandler Apache::DCELogin::purge

    This is ugly. With stacked handlers, Apache::DCELogin::handler can call from within the code:

    Apache->push_handlers("PerlCleanupHandler", \&purge);
  • Apache::DBI, the persistent database connection module, can pre-open the connection when the child process starts via its connect_on_init( ) function. This function uses push_handlers( ) to add a PerlChildInitHandler:

    Apache->push_handlers(PerlChildInitHandler => \&childinit);

    Now when the new process gets the first request, it already has the database connection open.

    Apache::DBI also uses push_handlers( ) to have PerlCleanupHandler handle rollbacks if its AutoCommit attribute is turned off.

  • PerlTransHandlers (e.g., Apache::MsqlProxy) may decide, based on the URI or some arbitrary condition, whether or not to handle a request. Without stacked handlers, users must configure it themselves.

    PerlTransHandler Apache::MsqlProxy::translate
    PerlHandler      Apache::MsqlProxy

    PerlHandler is never actually invoked unless translate( ) sees that the request is a proxy request ($r->proxyreq). If it is a proxy request, translate( ) sets $r->handler("perl-script"), and only then will PerlHandler handle the request. Now users do not have to specify PerlHandler Apache::MsqlProxy, because the translate( ) function can set it with push_handlers( ).

Now let's write our own example using stacked handlers. Imagine that you want to piece together a document that includes footers, headers, etc. without using SSI. The following example shows how to implement it. First we prepare the code as shown in Example 4-1.

Example 4-1. Book/Compose.pm
package Book::Compose;
use Apache::Constants qw(OK);

sub header {
    my $r = shift;
    $r->send_http_header("text/plain");
    $r->print("header text\n");
    return OK;
}
sub body   {
    shift->print("body text\n");
    return OK;
}
sub footer {
    shift->print("footer text\n");
    return OK;
}
1;

The code defines the package Book::Compose, imports the OK constant, and defines three subroutines: header( ) to send the header, body( ) to create and send the actual content, and finally footer( ) to add a standard footer to the page. At the end of each handler we return OK, so the next handler, if any, will be executed.

To enable the construction of the page, we now supply the following configuration:

PerlModule Book::Compose
<Location /compose>
    SetHandler perl-script
    PerlHandler Book::Compose::header Book::Compose::body Book::Compose::footer
 </Location>

We preload the Book::Compose module and construct the PerlHandler directive by listing the handlers in the order in which they should be invoked.[3]

[3] It may not seem to make sense to use this example, as it would be much simpler to write a single handler to call all three subroutines. But what if the three reside in different modules that are maintained by different authors?

Finally, let's look at the technique that allows parsing the output of another PerlHandler. For example, suppose your module generates HTML responses, but you want the same content to be delivered in plain text at a different location. This is a little trickier, but consider the following:

<Location /perl>
    SetHandler perl-script
    PerlHandler Book::HTMLContentGenerator
</Location>
<Location /text>
    SetHandler perl-script
    PerlHandler Book::HTML2TextConvertor Book::HTMLContentGenerator
</Location>

Notice that Book::HTML2TextConvertor is listed first. While its handler( ) will be called first, the actual code that does the conversion will run last, as we will explain in a moment. Now let's look at the sample code in Example 4-2.

Example 4-2. Book/HTML2TextConvertor.pm
package Book::HTML2TextConvertor;

sub handler {
    my $r = shift;
    untie *STDOUT;
    tie *STDOUT => _ _PACKAGE_ _, $r;
}

sub TIEHANDLE {
    my($class, $r) = @_;
    bless { r => $r}, $class;
}

sub PRINT {
    my $self = shift;
    for (@_) {
        # copy it so no 'read-only value modification' will happen
        my $line = $_;
        $line =~ s/<[^>]*>//g; # strip the html <tags>
        $self->{r}->print($line);
    }
}

1;

It untie( )s STDOUT and re-tie( )s it to its own package, so that content printed to STDOUT by the previous content generator in the pipe goes through this module. In the PRINT( ) method, we attempt to strip the HTML tags. Of course, this is only an example; correct HTML stripping actually requires more than one line of code and a quite complex regular expression, but you get the idea.

4.2.8 Perl Method Handlers

If mod_perl was built with:

panic% perl Makefile.PL PERL_METHOD_HANDLERS=1 [ ... ]

or:

panic% perl Makefile.PL EVERYTHING=1 [ ... ]

it's possible to write method handlers in addition to function handlers. This is useful when you want to write code that takes advantage of inheritance. To make the handler act as a method under mod_perl, use the $$ function prototype in the handler definition. When mod_perl sees that the handler function is prototyped with $$, it'll pass two arguments to it: the calling object or a class, depending on how it was called, and the Apache request object. So you can write the handler as:

sub handler ($$) {
    my($self, $r) = @_;
    # ...
}

The configuration is almost as usual. Just use the class name if the default method name handler( ) is used:

PerlHandler Book::SubClass

However, if you choose to use a different method name, the object-oriented notation should be used:

PerlHandler Book::SubClass->my_handler

The my_handler( ) method will then be called as a class (static) method.

Also, you can use objects created at startup to call methods. For example:

<Perl>
    use Book::SubClass;
    $Book::Global::object = Book::SubClass->new( );
</Perl>
...
PerlHandler $Book::Global::object->my_handler

In this example, the my_handler( ) method will be called as an instance method on the global object $Book::Global.

4.2.9 PerlFreshRestart

To reload PerlRequire, PerlModule, and other use( )d modules, and to flush the Apache::Registry cache on server restart, add this directive to httpd.conf:

PerlFreshRestart On

You should be careful using this setting. It used to cause trouble in older versions of mod_perl, and some people still report problems using it. If you are not sure if it's working properly, a full stop and restart of the server will suffice.

Starting with mod_perl Version 1.22, PerlFreshRestart is ignored when mod_perl is compiled as a DSO. But it almost doesn't matter, as mod_perl as a DSO will do a full tear-down (calling perl_destruct( )).[4]

[4] The parent process would leak several MB on each restart without calling perl_destruct( ).

4.2.10 PerlSetEnv and PerlPassEnv

In addition to Apache's SetEnv and PassEnv directives, respectively setting and passing shell environment variables, mod_perl provides its own directives: PerlSetEnv and PerlPassEnv.

If you want to globally set an environment variable for the server, you can use the PerlSetEnv directive. For example, to configure the mod_perl tracing mechanism (as discussed in Chapter 21), add this to httpd.conf:

PerlSetEnv MOD_PERL_TRACE all

This will enable full mod_perl tracing.

Normally, PATH is the only shell environment variable available under mod_perl. If you need to rely on other environment variables, you can have mod_perl make those available for your code with PerlPassEnv.

For example, to forward the environment variable HOME (which is usually set to the home of the user who has invoked the server in httpd.conf), add:

PerlPassEnv HOME

Once you set the environment variable, it can be accessed via the %ENV hash in Perl (e.g., $ENV{HOME}).

PerlSetEnv and PerlPassEnv work just like the Apache equivalents, except that they take effect in the first phase of the Apache request cycle. The standard Apache directives SetEnv and PassEnv don't affect the environment until the fixup phase, which happens much later, just before content generation. This works for CGI scripts, which aren't run before then, but if you need to set some environment variables and access them in a handler invoked before the response stage, you should use the mod_perl directives. For example, handlers that want to use an Oracle relational database during the authentication phase might need to set the following environment variable (among others) in httpd.conf:

PerlSetEnv ORACLE_HOME /share/lib/oracle/

Note that PerlSetEnv will override the environment variables that were available earlier. For example, we have mentioned that PATH is always supplied by Apache itself. But if you explicitly set:

PerlSetEnv PATH /tmp

this setting will be used instead of the one set in the shell program.

As with other configuration scoping rules, if you place PerlSetEnv or PerlPassEnv in the scope of the configuration file, it will apply everywhere (unless overridden). If placed into a <Location> section, or another section in the same group, these directives will influence only the handlers in that section.

4.2.11 PerlSetVar and PerlAddVar

PerlSetVar is another directive introduced by mod_perl. It is very similar to PerlSetEnv, but the key/value pairs are stored in an Apache::Table object and retrieved using the dir_config( ) method.

There are two ways to use PerlSetVar. The first is the usual way, as a configuration directive. For example:

PerlSetVar foo bar

The other way is via Perl code in <Perl> sections:

<Perl>
    push @{ $Location{"/"}->{PerlSetVar} }, [ foo => 'bar' ];
</Perl>

Now we can retrieve the value of foo using the dir_config( ) method:

$foo = $r->dir_config('foo');

Note that you cannot use the following code in <Perl> sections, which we discuss later in this chapter:

<Perl>
    my %foo = (a => 0, b => 1);
    push @{ $Location{"/"}->{PerlSetVar} }, [ foo => \%foo ];
</Perl>

All values are passed to Apache::Table as strings, so you will get a stringified reference to a hash as a value (such as "HASH(0x87a5108)"). This cannot be turned back into the original hash upon retrieval.

However, you can use the PerlAddVar directive to push more values into the variable, emulating arrays. For example:

PerlSetVar foo bar
PerlAddVar foo bar1
PerlAddVar foo bar2

or the equivalent:

PerlAddVar foo bar
PerlAddVar foo bar1
PerlAddVar foo bar2

To retrieve the values, use the $r->dir_config->get( ) method:

my @foo = $r->dir_config->get('foo');

Obviously, you can always turn an array into a hash with Perl, so you can use this directive to pass hashes as well. Consider this example:

PerlAddVar foo key1
PerlAddVar foo value1
PerlAddVar foo key2
PerlAddVar foo value2

You can then retrieve the hash in this way:

my %foo = $r->dir_config->get('foo');

Make sure that you use an even number of elements if you store the retrieved values in a hash.

Passing a list or a hash via the PerlAddVar directive in a <Perl> section should be coded in this way:

<Perl>
  my %foo = (a => 0, b => 1);
  for (%foo) {
      push @{ $Location{"/"}->{PerlAddVar} }, [ foo => $_ ];
  }
</Perl>

Now you get back the hash as before:

my %foo = $r->dir_config->get('foo');

This might not seem very practical; if you have more complex needs, think about having dedicated configuration files.

Customized configuration directives can also be created for the specific needs of a Perl module. To learn how to create these, please refer to Chapter 8 of Writing Apache Modules with Perl and C (O'Reilly), which covers this topic in great detail.

4.2.12 PerlSetupEnv

Certain Perl modules used in CGI code (such as CGI.pm) rely on a number of environment variables that are normally set by mod_cgi. For example, many modules depend on QUERY_STRING, SCRIPT_FILENAME, and REQUEST_URI. When the PerlSetupEnv directive is turned on, mod_perl provides these environment variables in the same fashion that mod_cgi does. This directive is On by default, which means that all the environment variables you are accustomed to being available under mod_cgi are also available under mod_perl.

The process of setting these environment variables adds overhead for each request, whether the variables are needed or not. If you don't use modules that rely on this behavior, you can turn it off in the general configuration and then turn it on in sections that need it (such as legacy CGI scripts):

PerlSetupEnv Off
<Location /perl-run>
    SetHandler perl-script
    PerlHandler Apache::PerlRun
    Options +ExecCGI
    PerlSetupEnv On
</Location>

You can use mod_perl methods to access the information provided by these environment variables (e.g., $r->path_info instead of $ENV{PATH_INFO}). For more details, see the explanation in Chapter 11.

4.2.13 PerlWarn and PerlTaintCheck

PerlWarn and PerlTaintCheck have two possible values, On and Off. PerlWarn turns warnings on and off globally to the whole server, and PerlTaintCheck controls whether the server is running with taint checking or not. These two variables are also explained in Chapter 6.

    [ Team LiB ] Previous Section Next Section