Show Contents Previous Page Next Page Chapter 4 - Content Handlers / Content Handlers as File Processors To show you how content handlers work, we'll develop a module with the Perl API that adds a canned footer to all pages in a particular directory. You could use this, for example, to automatically add copyright information and a link back to the home page. Later on, we'll turn this module into a full-featured navigation bar. Figure 4-1. The footer on this page was generated automatically by Apache::Footer. Example 4-1 gives the code for package Apache::Footer; use strict; use Apache::Constants qw(:common); use Apache::File ();
The code begins by declaring its package name and loading various Perl modules that it depends on. The use strict pragma activates Perl checks that prevent us from using global variables before declaring them, disallows the use of function calls without the parentheses, and prevents other unsafe practices. The Apache::Constants module defines constants for the various Apache and HTTP result codes; we bring in only those constants that belong to the frequently used sub handler { my $r = shift; return DECLINED unless $r->content_type() eq 'text/html'; The handler() subroutine does all the work of generating the content. It is roughly divided into three parts. In the first part, it fetches information about the requested file and decides whether it wants to handle it. In the second part, it creates the canned footer dynamically from information that it gleans about the file. In the third part, it rewrites the file to include the footer.
In the first part of the process, the handler retrieves the Apache request object and stores it in my $file = $r->filename; unless (-e $r->finfo) { $r->log_error("File does not exist: $file"); At this point we go ahead and recover the file path, by calling the request object's filename() method. Just because Apache has assigned the document a MIME type doesn't mean that it actually exists or, if it exists, that its permissions allow it to be read by the current process. The next two blocks of code check for these cases. Using the Perl -e file test, we check whether the file exists. If not, we log an error to the server log using the request object's log_error() method and return a result code of NOT_FOUND. This will cause the server to return a page displaying the 404 "Not Found" error (exactly what's displayed is under the control of the ErrorDocument directive). There are several ways to perform file status checks in the Perl API. The simplest way is to recover the file's pathname using the request object's filename() method, and pass the result to the Perl -e file test: unless (-e $r->filename) { $r->log_error("File does not exist: $file");
A more efficient way, however, is to take advantage of the fact that during its path walking operation Apache already performed a system stat() call to collect filesystem information on the file. The resulting status structure is stored in the request object and can be retrieved with the object's finfo() method. So the more efficient idiom is to use the test
Once finfo() is called, the stat() information is stored into the magic Perl file-handle my $modtime = localtime((stat _)[9]);
After performing these tests, we get the file modification time by calling stat(). We can use the my $fh; unless ($fh = Apache::File->new($file)) { $r->log_error("Couldn't open $file for reading: $!"); return SERVER_ERROR; }
At this point, we attempt to open the file for reading using my $footer = <<END; <hr> © 1998 <a href="http://www.ora.com/">O'Reilly & Associates</a><br> <em>Last Modified: $modtime</em> END Having successfully opened the file, we build the footer. The footer in this example script is entirely static, except for the document modification date that is computed on the fly. $r->send_http_header; while (<$fh>) { s!(</BODY>)!$footer$1!oi; } continue { $r->print($_); } The last phase is to rewrite the document. First we tell Apache to send the HTTP header. There's no need to set the content type first because it already has the appropriate value. We then loop through the document looking for the closing </BODY> tag. When we find it, we use a substitution statement to insert the footer in front of it. The possibly modified line is now sent to the browser using the request object's print() method. return OK; } 1;
At the end, we return an OK result code to Apache and end the handler subroutine definition. Like any other
If all this checking for the existence and readability of the file before processing seems a bit pedantic, don't worry. It's actually unnecessary for you to do this. Instead of explicitly checking the file, we could have simply returned my $fh = Apache::File->new($file) || return DECLINED; Doing the tests inside the module this way makes the checks explicit and gives us a chance to intervene to rescue the situation. For example, we might choose to search for a text file of the same name and present it instead. The explicit tests also improve module performance slightly, since the system wastes a small amount of CPU time when it attempts to open a nonexistent file. If most of the files the module serves do exist, however, this penalty won't be significant. Example 4-1. Adding a Canned Footer to HTML Pages package Apache::Footer; # file: Apache/Footer.pm use strict; use Apache::Constants qw(:common); use Apache::File (); sub handler { my $r = shift; return DECLINED unless $r->content_type() eq 'text/html'; my $file = $r->filename; unless (-e $r->finfo) { $r->log_error("File does not exist: $file"); my $modtime = localtime((stat _)[9]); my $fh; unless ($fh = Apache::File->new($file)) { $r->log_error("Couldn't open $file for reading: $!"); return SERVER_ERROR; } my $footer = <<END; <hr> © 1998 <a href=">http://www.ora.com/">O'Reilly & Associates</a><br> $r->send_http_header; while (<$fh>) { s!(</BODY>)!$footer$1!oi; } continue { $r->print($_); } return OK; } 1; __END__
There are several ways to install and use the <Location /footer> SetHandler perl-script PerlHandler Apache::Footer </Location>
If the files were scattered about the document tree, it might be more convenient to map AddType text/html .footer <Files ~ "\.footer$"> SetHandler perl-script PerlHandler Apache::Footer </Files> Note that it's important to associate MIME type text/html with the new extension; otherwise, Apache won't be able to determine its content type during the MIME type checking phase. If your server is set up to allow per-directory access control files to include file information directives, you can place any of these handler directives inside a .htaccess file. This allows you to change handlers without restarting the server. For example, you could replace the <Location> section shown earlier with a .htaccess file in the directory where you want the footer module to be active: SetHandler perl-script PerlHandler Apache::FooterShow Contents Previous Page Next Page Copyright © 1999 by O'Reilly & Associates, Inc. |
HIVE: All information for read only. Please respect copyright! |