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 2 - A First Module
"Hello World" with the Perl API

Now that you have mod_perl installed, it's time to put the Perl API through its paces.

First you'll need to create a location for your Apache Perl modules to live. If you haven't done so already, create a directory in some convenient place. We suggest creating a lib subdirectory within the server root, and a perl directory within that, making the full location ~www/lib/perl (Unix), or C:\Apache\lib\perl (Win32). Within this directory, create yet another directory for modules that live in the Apache:: namespace (which will be the vast majority of the modules we write), namely ~www/lib/perl/Apache.

You'll now have to tell Apache where to look for these modules. mod_perl uses the same include path mechanism to find required modules that Perl does, and you can modify the default path either by setting the environment variable PERL5LIB to a colon-delimited list of directories to search before Apache starts up or by calling use lib '////' when the interpreter is first launched. The first technique is most convenient to use in conjunction with the PerlSetEnv directive, which sets an environment variable. Place this directive somewhere early in your server configuration file:

 PerlSetEnv PERL5LIB /my/lib/perl:/other/lib/perl

Unfortunately this adds a little overhead to each request. Instead, we recommend creating a Perl startup file that runs the use lib statement. You can configure mod_perl to invoke a startup file of common Perl commands each time the server is launched or restarted. This is the logical place to put the use lib statement. Here's a small startup file to get you started:

#!/usr/local/bin/perl
# modify the include path before we do anything else
BEGIN {
  use Apache ();
  use lib Apache->server_root_relative('lib/perl');
}
# commonly used modules
use Apache::Registry ();
use Apache::Constants();
use CGI qw(-compile :all);
use CGI::Carp ();
# put any other common modules here
# use Apache::DBI ();
# use LWP ();
# use DB_File ();
1;

This example startup file first modifies the include path to point to the location of the Apache Perl module directory. It uses the Apache::server_root_relative() method to turn the relative path into an absolute path that use lib will honor. It then loads up some commonly used libraries, including Apache::Registry (a fast CGI-like environment), Apache::Constants (various constants used by Apache modules), and the CGI and CGI::Carp modules.

If most of your modules are going to use these libraries, loading them once at start-up time makes sense and assures the absolute fastest performance of your modules. Loading less-frequently used libraries should be deferred to the time you actually need them.

Save the startup file to some logical place. We recommend ~www/conf/startup.pl, so that it lives alongside Apache's other configuration files. If you can you should make this file owned and only writable by root (Administrator on Win32 systems). This is because during the server startup phase the code in this file is executed as the super-user, so anyone with write permissions to this file (or the directory that contains it) effectively has super-user privileges.

We'll need to tell Apache to run the startup file at launch time. Open perl.conf (actually, any of the configuration files will do) and add the following lines to the bottom:

PerlRequire          conf/startup.pl
PerlFreshRestart     On

The first directive tells Apache to load and run the startup script when it is first launched. Like other file paths in Apache's configuration files, partial paths are treated as relative to the server root. The second directive tells the server to repeat this process every time it is restarted. This allows changes to the startup script (and other Apache Perl modules) to take effect without bringing the server completely down.

You should now start or restart the server. On Unix platforms, the easiest way to do this is to use the apachectl script located in ~www/bin. The command apachectl graceful will send the server a polite USR1 signal to ask it to restart when it is finished processing all current requests, while apachectl restart will issue the server a more imperative HUP signal to command it to cancel all pending transaction and immediately restart. In either case, the server will be launched if it isn't already running. Users of the Win32 port can restart the server by issuing the command apache-k restart (Versions 1.3.3 and higher). If Apache is installed as a Windows NT service, you may also restart it using the Services control panel or by issuing the commands NET STOP APACHE and NET START APACHE from within a command window.

Watch the server ErrorLog during this process. If there are any errors in the configuration file or the Perl startup file, you'll see messages to that effect. Be particularly alert for messages like "Invalid Command `PerlRequire'." This message means that you haven't actually launched a mod_perl-enabled version of Apache. Are you sure that you launched the new executable?

Now that everything's configured properly, you can write a module using the Apache Perl API. Example 2-1 gives a basic one named Apache::Hello for you to try out:

Example 2-1. A First Apache Perl Module 

package Apache::Hello;
# File: Apache/Hello.pm
use strict;
use Apache::Constants qw(:common);
sub handler {
   my $r = shift;
   $r->content_type('text/html');
   $r->send_http_header;
   my $host = $r->get_remote_host;
   $r->print(<<END);
<HTML>
<HEAD>
<TITLE>Hello There</TITLE>
</HEAD>
<BODY>
<H1>Hello $host</H1>
Who would take this book seriously if the first example didn't 
say "hello world"? </BODY> </HTML> END return OK; } 1;

We'll go into the details in later chapters, but essentially, this module contains the definition for a single subroutine named handler(). When the time comes, Apache will invoke handler() to handle the request, passing it an Apache request object stored in the variable $r. The request object is the primary interface between subroutine and server.

Using methods provided by the request object, our module first sets the MIME content type of the outgoing data to text/html and then sends the HTTP headers by calling send_http_header(). It retrieves the DNS name of the remote host by making another call to the request object and incorporates this value into a short HTML page that it sends to the browser by calling the request object's print() method. At the end of the subroutine, the module returns a value of OK (defined in the library module Apache::Constants) to signal to Apache that execution was successful.

To install this module, save it as ~www/lib/perl/Apache/Hello.pm (C:\Apache\lib\perl\Apache\Hello.pm on Win32 systems). This makes it accessible to mod_perl. The next step is to associate the module with a URI by mapping it to a portion of your document tree.6 The simplest way to do this is by adding an Apache <Location> directive to perl.conf (or any of the other configuration files, for that matter). This entry will do the trick:

<Location /hello/world>
 SetHandler  perl-script
 PerlHandler Apache::Hello
</Location>

The first directive, SetHandler perl-script, tells Apache to invoke mod_perl to handle the phase of the HTTP transaction that produces the content of the page. The second directive, PerlHandler Apache::Hello, tells mod_perl to load the Apache::Hello module and execute its handler() subroutine. Without this directive, you would get a "File not found" error. The URI specified in <Location> can be any arbitrary path on your system and doesn't (and probably shouldn't) refer to a real file already in the document tree. If there already is a physical document at that location, the Perl module will supersede it.

You will have to restart the server again in order to have the new <Location> section take effect. Later we will discuss how to install new modules without restarting the server. Fire up your favorite browser and fetch the URI /hello/world. You should be greeted by the page shown in Figure 2-1.

Figure 2-1. Apache::Hello results

If you get a server error of some sort, don't despair. Look in the server error log for helpful messages from the Perl interpreter. They may be bare messages, or if you are loading CGI::Carp in the Perl startup file, they may be preceded by a timestamp and -e in the filename field, indicating that the error occurred within a Perl eval() statement. Most of what mod_perl does occurs within the context of an eval().

Most commonly you'll see messages about syntax errors. Fix the errors, restart the server, and try again. If you get messages about not being able to find the Apache::Hello module, most likely your include path is screwed up. Check that the Perl startup script is setting the include path correctly, that Apache/Hello.pm is installed in the correct subdirectory, and that the permissions of Hello.pm and all its parent directories give the Apache server user read access. Then restart the server and try again.

These are the basic steps for creating and installing a module using the Apache Perl API. Later chapters will give you a more in-depth understanding of what's going on here and how you can take advantage of it to do wonderful stuff.

Footnotes

6 In the context of incoming Apache requests, we use "URI" (Uniform Resource Identifier) rather than "URL" (Uniform Resource Locator) throughout this book. URI is the more general term, so it can refer to partial documents as well as to fully qualified URLs. The main reason, however, is that URI is used in the Apache online documentation and in the names of API function calls, and who are we to buck tradition?

   Show Contents   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.