home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam    

Writing Apache Modules with Perl and C
By:   Lincoln Stein and Doug MacEachern
Published:   O'Reilly & Associates, Inc.  - March 1999

Copyright © 1999 by O'Reilly & Associates, Inc.


   Show Contents   Previous Page   Next Page

Chapter 4 - Content Handlers

In this section...

A Useful Apache::Registry Application
Apache::Registry Traps


   Show Contents   Go to Top   Previous Page   Next Page

If you are using mod_perl to write Apache modules, then you probably want to take advantage of Apache::Registry. Apache::Registry is a prewritten Apache Perl module that is a content handler for files containing Perl code. In addition to making it unnecessary to restart the server every time you revise a source file, Apache::Registry sets up a simulated CGI environment, so that programs that expect to get information about the transaction from environment variables can continue to do so. This allows legacy CGI applications to run under the Apache Perl API, and lets you use server-side code libraries (such as the original CGI.pm) that assume the script is running in a CGI environment.

Apache::Registry is similar in concept to the content filters we created earlier in this chapter, but instead of performing simple string substitutions on the contents of the requested file, Apache::Registry compiles and executes the code contained within it. In order to avoid recompiling the script each time it's requested, Apache::Registry caches the compiled code and checks the file modification time each time it's requested in order to determine whether it can safely use the cached code or whether it must recompile the file. Should you ever wish to look at its source code, Apache::Registry is a good example of a well-written Apache content handler that exercises much of the Perl API.

We created a typical configuration file entry for Apache::Registry in Chapter 2. Let's examine it in more detail now.

Alias /perl/ /usr/local/apache/perl/
<Location /perl>
  SetHandler     perl-script
  PerlHandler    Apache::Registry
  PerlSendHeader On
  Options        +ExecCGI

The Alias directive simply maps the physical directory /usr/local/apache/perl/ to a virtual directory named /perl. The <Location> section is more interesting. It uses SetHandler to make perl-script the content handler for this directory and sets Apache::Registry to be the module to handle requests for files within this part of the document tree.

The PerlSendHeader On line tells mod_perl to intercept anything that looks like a header line (such as Content-Type: text/html) and to automatically turn it into a correctly formatted HTTP/1.0 header the way that Apache does with CGI scripts. This allows you to write scripts without bothering to call the request object's send_http_header() method. Like other Apache::Registry features, this option makes it easier to port CGI scripts to the Apache API. If you use CGI.pm's header() function to generate HTTP headers, you do not need to activate this directive because CGI.pm detects mod_perl and calls send_http_header() for you. However, it does not hurt to use this directive anyway.

Option +ExecCGI ordinarily tells Apache that it's all right 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. In addition, all scripts located in directories handled by Apache::Registry must be executable--another check against accidentally leaving wayward nonscript files in the directory.

When you use Apache::Registry, you can program in either of two distinct styles. You can choose to ignore the Apache Perl API entirely and act as if your script were executed within a CGI environment, or you can ignore the CGI compatibility features and make Apache API calls. You can also combine both programming styles in a single script, although you run the risk of confusing yourself and anyone else who needs to maintain your code!

A typical example of the first style is the hello.pl script (Example 4-12), which you also saw in Chapter 2. The interesting thing about this script is that there's nothing Apache-specific about it. The same script will run as a standard CGI script under Apache or any other web server. Any library modules that rely on the CGI environment will work as well.

Example 4-12. An Apache::Registry Script That Uses CGI-Compatibility Mode

# file: hello.pl
print "Content-Type: text/html\n\n";
print <<END;
<TITLE>Hello There</TITLE>
<H1>Hello $ENV{REMOTE_HOST}</H1>
Who would take this book seriously if the examples
didn't say "hello world" in at least four different ways? 

Example 4-13 shows the same script rewritten more compactly by taking advantage of the various shortcuts provided by the CGI.pm module.

Example 4_13. An Apache::Registry Script That Uses CGI.pm

# file: hello2.pl
use CGI qw(:standard);
print header,
  start_html('Hello There'),
  'Who would take this book seriously if the examples',
  'didn\'t say "hello world" in at least four different ways?',

In contrast, Example 4-14 shows the script written in the Apache Perl API style. If you compare the script to Example 4-7, which used the vanilla API to define its own content handler, you'll see that the contents of this script (with the exception of the #! line at the top) are almost identical to the body of the handler() subroutine defined there. The main difference is that instead of retrieving the Apache request object from the subroutine argument list, we get it by calling Apache->request(). request() is a static (class) method in the Apache package where the current request object can always be found.

There are also some subtle differences between Apache::Registry scripts that make Apache API calls and plain content handlers. One thing to notice is that there is no return value from Apache::Registry scripts. Apache::Registry normally assumes an HTTP status code of 200 (OK). However, you can change the status code manually by calling the request object's status() method to change the status code before sending out the header:

$r->status(404);  # forbidden

Strictly speaking, it isn't necessary to call send_http_header() if you have PerlSendHeader On. However, it is good practice to do so, and it won't lead to redundant headers being printed.

Alternatively, you can use the CGI compatibility mode to set the status by printing out an HTTP header that contains a Status: field:

print "Status: 404 Forbidden\n\n";

Another subtle difference is that at least one of the command-line switches that may be found on the topmost #! line is significant. The -w switch, if present, will signal Apache::Registry to turn on Perl warnings by setting the $^W global to a true value. Another common switch used with CGI scripts is -T, which turns on taint checking. Currently, taint checking can be activated for the Perl interpreter as a whole only at server startup time by setting the configuration directive PerlTaintCheck On. However, if Apache::Registry notices -T on the #! line and taint checks are not activated, it will print a warning in the server error log.

Since Apache::Registry scripts can do double duty as normal CGI scripts and as mod_perl scripts, it's sometimes useful for them to check the environment and behave differently in the two situations. They can do this by checking for the existence of the environment variable MOD_PERL or for the value of GATEWAY_INTERFACE. When running under mod_perl, GATEWAY_INTERFACE will be equal to CGI-Perl/1.1. Under the normal CGI interface, it will be CGI/1.1.

Example 4-14. An Apache::Registry Script That Uses the Apache API 

# file: hello3.pl
use strict;
my $r = Apache->request;
return OK if $r->header_only;
my $host = $r->get_remote_host;
<TITLE>Hello There</TITLE>
<H1>Hello $host</H1>
Enough with the "Hello worlds" already!
   Show Contents   Go to Top   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.