`
`piijj.-,-,'•-_^ '.'•T (cid:127) -^'".i'"
`
`m
`
`; •• r-x^^^.;-. :• \i
`
`i/5^
`
`>%;•
`
`-/yp^ &;
`
`Modu
`
`Perl and C
`
`O'REILLY
`
`Lincoln Stein & Doug MacEachern
`
`1
`
`AT&T - Exhibit 1008
`
`
`
`Writing Apache Modules
`with Perl and C
`
`Lincoln Stein and Doug MacEachern
`
`Beijing - Cambridge - Famlaam - Kéiln - Sebastopol - Toleyo
`
`O’RE|LLY°
`
`2
`
`2
`
`
`
`Writing Apache Modules with Perl and B
`hy Lincoln Stein and Doug .\rIaeF.a<:l1ern
`
`Copyifiglit CC) 1999 O‘Reilly e\' Associates, Inc. All rights reserx ed.
`Printecl in the United States of America.
`
`Ptihlished hy O’Reilly & Associates, Inc., 101 Morris Street, Sehrtstopol, CA 95472.
`
`Editor: Linda Mui
`
`Production Editor: Melanie Wzing
`
`Printing History:
`
`March 1999:
`
`First Edition.
`
`The association between the image of a white-tailed eagle and the topic of Apache modules
`is a trademark oi'O’Reilly & Associates, Inc. Nutshell Handbook, the Nutshell I-iandhook logo,
`and the O‘Reilly logo are registered trademarks ol’ O’Reilly & Associates, Inc. Many of the
`designations used by manufacturers and sellers to distinguish their products are claimed as
`trademarks. Where those designations appear in this hook, and O’Reilly & Associates. Inc.
`was aware of a trademark claim, the designations have been printed in caps or initial caps.
`
`\\’fhile every precaution has been taken in the preparation ofthis hook, the publisher /,1.‘-2SL!I]1C.‘a‘
`no responsibility for errors or omissions, or for damages resulting from the use of the
`information contained herein.
`
`ISBN: 978-1565-92567-(J
`ILSII
`
`|2(il1-1 I-U4]
`
`3
`
`
`
`V
`
`1)., mi’: c:I_1c:prer.'
`. Cm1fentHci.ml1ers as
`,.--,-,_r(, P7‘ocessors
`
`. Vr'rfua!1)ocmnents
`
`.
`
`serif-recrfon
`
`. Processing Irzput
`
`. ,-ipctCbe:.'Regrlstrjt
`.
`Iczndlfirg Errors
`2/.-m:1'nt'7'zg Content
`
`Hcmrtle-rs
`
`. Method Handlers
`
`7/1t t H017’!
`
`This chapter is about writing content handlers for the Apache response phase,
`when the contents of the page are actually produced. In this chapter you'll learn
`how to produce dynamic pages from thin air, how to modify real documents on
`the fly to produce effects like server—side includes, and how Apache interacts with
`the MIiv[F,—typing system to select which handler to invoke.
`
`to using the Apache Perl API exclusively for
`Starting with this chapter we shift
`code examples and function prototypes. The Perl AP] covers the majority of what
`C programmers need to use the Glanguage AP]. What’s missing are various mem-
`ory management functions that are essential to C programmers but irrelevant in
`Perl. If you are a C programmer, just have patience and the missing pieces will be
`filled in eventually. In the meantime, follow along with the Perl examples and
`
`enjoy yourself. Maybe you’ll even become a convert.
`
`Content Handlers as File Processors
`
`Early web servers were designed as engines for transmitting physical files from the
`host machine to the browser. Even though Apache does much more,
`the file-
`oriented legacy still
`remains. Files can be sent
`to the browser unmodified or
`passed through content handlers to transform them in various ways before send-
`
`ing them on to the browser. Even though many of the documents that you pro-
`
`duce with modules have no corresponcling physical files, some parts of Apache
`
`still behave as if they did.
`
`translation
`is passed through any URI
`When Apache receives a request, the URI
`handlers that may be installed (see Chapter 7. Other Reqtiesr Pbases, for informa-
`tion on how to roll your own),
`transforming it
`into a file path. The mool_czlz'as
`translation handler (compiled in by default) will first process any Al-m'.s‘, SCripfAZias,
`
`4
`
`
`
`
`
`86 Chapter 4: Content Hcmdlers
`
`Redirect, or other nzon.’_ctf:fczs directives. If none applies, the br1;D_core default trans-
`lator will simply prepend the Doctrmemffeoot directory to the beginning of the URI.
`
`Next, Apache attempts to divide the file path into two parts: a “filename” part
`which usually (but not always) corresponds to a physical file on the host’s filesys-
`tem, and an “additional path information” part corresponding to additional stuff
`that follows the filename. Apache divides the path using a very simple-minded
`algorithm. It steps through the path components from left to right until
`it finds
`something that doesn’t correspond to a directory on the host machine. The part of
`the path up to and including this component becomes the filename, and every-
`thing that’s left over becomes the additional path information.
`
`Consider a site with a document root of /bome/www that has just received a
`
`request for URI /abt:/n.'e_/'/gbi. The way Apache splits the file path into filename and
`path information parts depends on what directories it finds in the document root:
`
`Physical Directory
`/!.2rJrn.e/www
`
`| Translated Filename
`/borne/wwttt/cu'JC
`
`Additional Path Information
`/cI.'ej7glJi
`
`/home/www/ctbc
`
`/bome/tttwttfictbc/def
`
`/gbi
`
`
`
`
`
`
`
`/home/www/abc/de/'
`
`/bome/www/ctbe/ale/Kg/J1'
`
`
`
`/br)me/tttuzttmbc/cieflgbi
`
`//Jome/wtuttfictlztc/dcflglsti
`
`empty
`
`
`e mpty
`
`Note that the presence of any actual files in the path is irrelevant to this process.
`The division between the filename and the path information dep~‘ends only on
`
`what directories are present.
`
`it determines what MIME
`Once Apache has decided where the file is in the path,
`type it might be. This is again one of the places where you can intervene to alter
`the process with a custom type handler. The default type handler (moa.’_-mime) just
`compares the filename’s extension to a table of MIME types. If there’s a match, this
`becomes the MIME type. If no match is found, then the MIME type is undefined.
`Again, note that
`this mapping from filename to MIME type occurs even when
`there’s no actual file there.
`
`There are two special cases. If the last component of the filename happens to be a
`physical directory, then Apache internally assigns it a “magic" MIME type, defined
`by the DIR_MAGIC_TYPE constant as bt‘tpd/unix-directory. This is used by the
`directory module to generate automatic directory listings. The second special case
`occurs when you have the optional m0a'Hmz'me_magz'c module installed and the
`file actually exists. In this case Apache will peek at the first few bytes of the file’s
`contents to determine what type of file it might be. Chapter 7' shows you how to
`write your own MIME type checker handlers to implement more sophisticated
`
`MIME type determination schemes.
`
`
`
`h-T-‘l~n.v-:I»:';_t::.'«3.«r.
`
`5
`
`
`
`7
`
`Coflrerrt Iilcmdlers as File [’roce.x'so-rs
`
`87
`
`After Apache has determined the name and type of the file referenced by the lllll,
`i[ decides what to do about it. One way is to use information hard—wired into the
`modules static data structures. The :nodule’s handle-r_rec table, which we
`describe in detail in Chapter ll"), CAP! Re/erertce Gtride, Part I, declares the mod-
`ule’s willingness to handle one or more tu;t§_{ic MIME types and associates a con-
`[ent handler with each one. For example,
`the mod_cgt' module associates MIME
`type 5rj)])z.'t'ccm'o'rz/:c—i9trg0a’—cgf with its
`cgf_/.m1m’fei~() handler subroutine. When
`Apaclie detects that a filename is of type crpp.lr'ecm'orrKr—bItp.cl—.:'gif it
`invokes egL'_
`]g(1i’£6.l/8?'(_) and passes it inl’ormation about the file. A module can also declare it.s
`desire to handle an ordinary MIME type. such as £'l'Cl’(30/c]llfCrl?!I'?l‘l(3. or even a wild-
`Cdrd type, such as video/*. In this case, all requests for [Fills with matching MIME
`types will be passed through the modules content handler unless some other
`module registers a more specific type.
`
`Newer modules use a more flexible method in which content handlers are associ-
`ated with files at runtime using explicit names. When this method is used,
`the
`
`module declares one or more content handler names in its handler_rec array
`instead of", or in addition to,
`l\/llMF.
`types. Some examples of content handler
`names you might have seen include cgi—5ci-apt, seruer—:frt_./O, sert.»ei'—pmsed,
`r'map¢/2'19,
`and pe7‘l—5crr;DI. Handler names can be associated with files using either Add]-Ian"
`dler‘ or 5etHcmdZer directives. Aa.'dHa'rza.’lei-‘ associates a handler with a particular
`
`file extension. For example, a typical configttration file will contain this line to
`associate sbrml files with the server-side include handler:
`
`Addfiandler server—parsed .shtm1
`
`Now, the server-parseci handler defined by rrroc2.’_:'nclude will be called on to pro-
`cess all files ending in “.shtm1" regardless of their MIME type.
`
`SeIHcmdl.’er is used within <Directo2y>, <f.occm'on>, and <F:'/'es> sections to associ-
`
`ate a particular handler with an entire section of the site's URI space. In the two
`examples that follow, the <L0ccm'on> section attaches the se-ruer—par5ea’ method to
`all files within the virtual directory /sbrml, while the <Files> section attaches i'map—
`
`flle to all files that begin with the prefix “map—”:
`
`<Location /shtml>
`SetHand1er server—parsed
`</Location>
`
`<Files map—*>
`SetHandler imap—file
`</Files>
`
`the Acicihlczrzcilei" and Set!-Imrcller directives are not actually imple-
`Surprisingly,
`mented in the Apache core. They are implemented by the standard mod_actz'orts
`
`6
`
`
`
`
`
`(Jrapter 4: (jrmtent Haridlem
`88
`4a______4,______c_______.________d_____________,____._____s._______#__________H
`
`module, which is compiled into the server by del’aul1. In Chapter 7, we show how
`to reimplement mocI'_ctcti'o'rzs using the Perl API.
`
`to use explicitly named content handlers in your modules
`You'll probably want
`rather than hardcoded MIME types. Explicit handler names malte configuration
`iiies cleaner and easier to understand. Plus, you don't have to invent a new magic
`i\/IIMl*". t.ype every time you add a handler.
`
`Things are slightly dil"t'erent for moa'_pen’ users because two directives are needed
`to assign a content handler to a directory or tile. The reason For this is that the
`only real content handler defined by moc£_pen'
`is its internal pe-n’—sc:rtTpt handler.
`You use SetHan.dler to assign per1—sc1‘i'p! the respmisibility for a directory or partial
`URI, and then Lise a Pen.’Hcmdler directive to tell the pen.’—scri?pt handler which Perl
`module to execute. Directories supervised by Perl AP! content handlers will look
`
`something like this:
`
`<Location /graph>
`Setflandler per1—script
`PerlHandler Apaehe::Graph
`</Location>
`
`Don't tiy to assign per!'—5crt'pr to a file extension using something like AddHa.nd1er
`perl—seript .131;
`this is generally useless because you’d need to set PerlHcm—
`dler too. If you'd like to associate a Perl content handler with an extension, you
`should use the <Fz'les> directive. Here’s an example:
`
`<Files ~ "\.graph$">
`SetHandler per1»script
`Per1Handler Apache::Graph
`4/Fi1es>
`
`There is no U2'zSetHand!ei' directive to undo the effects of SeIHcma.'ler. However,
`should you ever need to restore a subdirectory‘s handler to the default, you can
`do it with the directive Setj-Iandler defau1t:—handler, as follows:
`
`<Location /graph/tutorial>
`SetHandler default—handler
`</Location>
`
`Adding oz Canned Footer to Pages
`
`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 iiitoi‘1i121tioi1 and a link back to
`the home page. Later on, well turn this module into a l"ull—t’eatured navigation bar.
`
`Example 4-1 gives the code for Apache.-.-Footer, and Figure 4-—1 shows a screenshot
`of it in action. Since this is our first substantial module, we‘ll step through the code
`
`section by section.
`
`7
`
`
`
`V
`
`Content" Hcmdlers as File Processors
`
`File
`
`Edit View
`
`Go
`
`Bookmarks Options
`
`Directory
`
`Window
`
`Reload lxsaci !t1t—atces 0 en... "Print... Fin
`Ed't
`Wu!-Jackl Ftrwartil Home’
`l__.......-E__| __JP _
`_ .
`,..'_l
`l
`.
`‘
`i
`E
`Location: ttp : //l ocal hostffoote rfdemo. html
`
`This page contains a" canned footer
`
`The two lines at the bottom of this page aren't in the original source. code, but were
`added by .€=.::acl1e::l7oor.:'-.r
`
`© 1998 -.'."'Rt-iii}! 3.: A.\‘soc.iates
`Last Moe’! ted: Wedzlpr 8 07:48:04 1998
`
`Fz'gu.-re 4-1.
`
`'Ill)tJ_fl)()!(:‘I‘ on Ibis page ZU'.CIS generatecl cl-Ltftjfficllfccilb.’ by Apache.-:Foo!ei'.
`
`package Apache: :Foot:er;
`
`use strict;
`
`use Apache: :Constants qwtzcommonl;
`use Apache::File (l;
`
`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
`Apczc/ye.-.-Cortsianrs module defines constants for the various Apache and H’I”I‘P
`result codes; we bring in only those constants that belong to the frequently used
`.-common? set. Apczcbe.-.-File defines methods that are useful for manipulating files.
`
`sub handler {
`my $r : shift;
`return DECLINED unless Sr->content_type() eq ‘text/html‘;
`
`The bcma.’/er() 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 inforination 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 $r. Next
`it calls the requests cortIenI_rype() method to retrieve its
`MIME type. Unless the document is of type text/brml, the handler stops here and
`returns a DECLINED result code to the server. This tells Apache to pass the
`
`8
`
`
`
`
`
`90 Chapter‘ 4: Content .Hrmdlers
`
`document on to any other handlers that have declared their willingness to handle
`this type of document. In most cases, this means that the document or image will
`he passed through to the browser in the usual way.
`
`
`
`my Sfile = $r—>filename;
`
`{
`unless (ee Sr—>finfo)
`$re>1og_error(“File does not exist: Sfile");
`return NOT_FOUND;
`
`} u
`
`}
`
`nless (rr _)
`
`{
`
`$r—>log_error("File permissions deny access: Sfile");
`return FORBIDDEN;
`
`At this point we go ahead and recover the file path, by calling the request 0bject’s
`fl'lerta:tne() 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 objects i.'og_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 Error-
`Docmnenl 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 filemzme() method,
`and pass the result to the Perl -2 file test:
`
`unless (—e $r~>fi1ename)
`
`{
`
`$r~>log_error("File does not exist: Sfile“);
`return NOT_FOUND;
`
`}
`
`A more efficient way, however, is to take advantage of the fact that during its path
`walking operation Apache already performed a system smr() call to collect filesys-
`tem information on the file. The resulting status structure is stored in the request
`object and can be retrieved with the objects ‘/IrTry"0() method. So the more efficient
`idiom is to use the test —e $r—>finfo.
`I
`
`file-
`the 5tat() information is stored into the magic Perl
`Once fz'rzfo() is called,
`handle _ and can be used for subsequent file testing and statO operations, saving
`even more CPU time. Using the _ filehandie, we next test that the file is readable
`by the current process and return FORBIDDEN if this isn’t the case. This displays a
`405 “Forbidden" error.
`
`my $modtime = loealtime((stat _)[9]);
`
`9
`
`
`
`(.0H“;-,1-,v,.r. 1m-rzctlers as File Processors
`
`9!
`
`Wm. performing these tests, we get the file moclillcation time by calling sIat(). We
`in L193 the _ filehandle here too, avoiding the overhead of repeating the stat()
`‘M
`.y_;1L.[n call. The modification time is passed to the burlt—m Perl 1oca.r.’rzme() func-
`s -
`_
`mm U) eonvert it into a ht1man—readable string.
`my Sfhi
`{
`unless (Sfh = Apache::File+>new{$file)l
`$rA>log_error("Couldn't open $file for reading: $1");
`return SERVEILERROR;
`
`}
`
`Ar this point, we attempt to open the file for reading using Apache.-.~File’s nc-'w(')
`method. For the most part, Apache.-.-File acts just like Perl's IO.-.-File object—oriented
`[/0 package, returning a filehandle on success or nrzdcg/" on failure. Since we've
`already handled the two failure modes that we know how to deal with, we return
`a result code of SERVER_ERROR if the open is unsuccesslul. This immediately
`;1h()t‘[S all processing of the document and causes Apache to display a 500 “Inter-
`nal Server Error” message.
`
`my Sfooter = <<END;
`
`<hr>
`
`© 1998 <a href=“http://www.ora.com/”>O‘Rei11y & 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 rnodification date that is
`computed on the fly.
`
`$r—>send_http_header;
`
`{
`while (<$fh>)
`s1(</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
`
`looking for the closing
`appropriate value. We then loop through the document
`</l3OD‘Y'> 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 pr-*z.'m() method.
`
`return OK;
`
`10
`
`10
`
`
`
`
`
`92 Chapter 4: Content Handlers
`
`
`
`At the end, we return an OK result code to Apache and end the handler subrou-
`tine definition. Like any other .pm file, the module itself must end by returning a
`true value (usually 1) to signal Perl that it compiled correctly.
`
`lx
`llu
`l
`
`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 DECLINED if
`the attempt to open the file failed. Apache would then pass the URI to the default
`file handler which will perform its own checks and display the appropriate error
`messages. Therefore we could have replaced the file tests with the single line:‘
`
`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.
`
`
`
`
`
`,_:__....i..,._.........._._...._._.........._...._._...._.._.,._,_,.,.__...,.,,........-4......-.s..........,.....,,.._._-__
`
`
`
`
`
`
`
`Example 4-]. Adding oz Cmmed Footer to H771/II. Pages
`
`package Apache::Footer;
`# file: Apache/Footer.pm
`
`use strict;
`use Apache::Constants qw(:comon);
`use Apache::File ();
`
`sub handler {
`
`my Sr = shift;
`return DECLINED unless $r—>content_type() eq ‘text/html';
`
`my $fi1e = $r—>fi1ename;
`
`unless (—e $r—>finfo)
`
`{
`
`$r—>1og_error(“File does not exist: Sfile");
`return NOT_FOUND;
`
`} u
`
`{
`nless (-r _)
`$r->log_error("File permissions deny access: $fi1e");
`return FORBIDDEN ;
`
`} m
`
`y smodtime = 1oca1time((stat _)[9]);
`
`my $fh;
`{
`unless ($fh = Apache::File->new($file))
`$r—>log_error("Couldn't open $file for reading: $2");
`return SERVER_ERROR;
`
`11
`
`11
`
`
`
`1i'\.'m7'zple 4-1. .4ctcl'z'i-rig oi (,'cn'mea.' 1'?'rm!er Io lI'!_ila’1.l’c£gc.x' (C()I1HI1Ii'(’(U
`my Sfooter = <<END;
`4'11-">
`&Copy; 1998 <a href=">http://www.0ra.C0m/“>O'Rei1ly & Associates</a><br>
`<em>Last Modified: $m0dtime</em>
`END
`
`Sr->send_http_header;
`
`while (<$fh>)
`
`t
`
`s!{</BODY>)!$footer$1loi;
`} continue [
`$r—>print($_);
`
`} r
`
`eturn OK;
`
`END _
`
`} 1
`
`;
`
`There are several ways to install and use the Apcmbe.-.-Footer content handler. If all
`the files that needed footers were _<_=,atl1ei‘ed in one place in the directory tree, you
`would probably want to attach xlpczc/2e.~.-Footertr) that location:
`
`<Location /footer>
`
`SetHandler per1~script
`Perlfiandler Apache::Footer
`</Location>
`
`If the files were scattered about the document tree, it might be more convenient to
`map Apache.-.-Footer to a unique iilename extension, such as footer: To achieve
`this, the following directives would stiliice:
`
`.footer
`AddType text/html
`<Fi1es ~ "\.footer$“>
`
`SetHandler perl—seript
`Perlfiandler Apache::Footer
`</Fi1es>
`
`Note that it's important to associate MIME type text‘/btrnl with the new extension;
`otherwise, Apache won’t be able to determine its content type during the MIME
`
`type checl<ing 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 brac-
`cess file. This allows you to change handlers without restarting the server. For
`example, you could replace the <£ocaH'o1'z> section shown earlier with a Jalaccess
`file in the directory where you want the footer module to be active:
`
`Setflandler perl—script
`Per1Handler Apache::Footer
`
`12
`
`12
`
`
`
`
`94
`Cbczpter 4." Content 1—Irmd1'ers
`
`A Server-Sz'cz’e Include System
`
`The obvious limitation of the Apac/:1e:.-Footer example is that
`
`the footer text
`
`is
`
`hardeoded into the code. Changing the footer becomes a nontrivial task, and using
`
`different footers for various parts of the site becomes impractical. A much more
`flexible solution is provided by Vivek Khera’s Apacl9e:.-5mzdwr'cb module. This
`module “sandwiches" HTML pages between canned headers and footers that are
`
`determined by runtime configuration directives. The Apache.-.-Sandrutcb module
`also avoids the overhead of parsing the request document; it simply uses the sub-
`
`request mechanism to send the header, body, and footer files in sequence.
`
`than Apache.-.-Srmdwicb by using server-side
`We can provide more power
`includes. Sewer—side includes are small snippets of code embedded within HTML
`
`in the standard server—side includes that are imple-
`comments. For example,
`mented in Apache, you can insert the current time and date into the page with a
`comment that looks like this:
`
`Today is <!——#echo var="DATEfiLOCAL"——>.
`
`In this section, we Lise mod_pen.'
`
`to develop our own system of server—side
`
`includes, using a simple but extensible scheme that lets you add new types of
`includes at a moment’s whim. The basic idea is that HTML authors will create files
`
`that contain comments of this form:
`
`<!--#DIREC'TIE PARAM1 PARAM2 PARAM3 PARAM4.. .-—>
`
`A directive name consists of any sequence of alphanumeric characters or under—
`
`scores. This is followed by a series of optional parameters, separated by spaces or
`
`commas.
`
`’arameters that contain whitespace must be enclosed in single or dou-
`
`ble quotes in shell command style. Backslash escapes also work in the expected
`manner.
`
`The directives themselves are not hardcoded into the module but are instead
`
`dynamically loaded from one or more configuration files created by the site
`administrator. This allows the administrator to create a standard menu of includes
`
`that are available to the site’s HTML authors. Each directive is a short Perl subrou-
`
`tine. A simple directive looks like this one:
`
`sub HELLO { “Hello Worldl“:
`
`}
`
`This defines a subroutine named HELLOO that returns the string “Hello World!" /\
`
`document can now include the string in its text with a comment formatted like this
`one:
`
`I said <1-—#HLLO——>
`
`A more complex subroutine will need access to the Apache object and the server-
`
`side include parameters. To accommodate this, the Apache object is passed as the
`
`first function argument, and the server—side include parameters,
`
`if any,
`
`follow.
`
`13
`
`13
`
`
`
`
`
`Cgflfeilf Handlers as File PI”'0CBSS0l‘S 95
`
`Here’s a function definition that returns any field from the incoming request’s
`HTTP header, using the Apache object's /9ecm,’er'_m() method:
`
`sub HTTP_HEADER {
`my (Sr,$field) = @_;
`$r—>header_in($field);
`
`}
`
`‘xx’/ith this subroutine definition in place, HTML authors can insert the [fser—Agr3m
`
`iield into their document using a comment like this one:
`
`You are using the browser <!—— #HTTP_HEADER User—Agent ——>_
`
`Example 4-2 shows an HTML file that uses a few of these includes, and Figure 4-2
`shows what the page looks like after processing.
`
`Exmnple 4-2. /in HTML File 7736!! U.\'es Exiertdea’ .S‘ei‘iier—_S‘i'cfe lnclmles
`
`<html> <head> <title>Server—Side Includes</title></head>
`(body bgcolor=white>
`<h1>server—Side Inc1udes</h1>
`This is some straight text.<p>
`
`This is a "<!—— #HELLO ——>"
`
`include <p>
`
`The file size is <strong><!—— #FSIZE ——></strong>, and it was
`last modified on <i—— #MODTIME %x ——><p>
`
`Today is <!—— #DATE "%A,
`
`in <em>anno domini</em> %Y“——>.<p>
`
`The user agent is <em><!~~#HTTP_HEADER User—Agent——></em>.<p>
`
`Oops: <!——#OOPS O——><p>
`
`Here is an included file:
`<pre>
`<!——#INCLUDE /include.txt l——>
`
`</pre>
`
`<!——fiFOOTER——>
`
`</body> </html>
`
`Implementing this type of server—side include system might seem to be something
`of a challenge, but in fact the code is surprisingly compact (Example 4-3). This
`module is named Apache.-.-E551, for “extensible serve1'—side includes.”
`
`Again, we’ll step through the code one section at a time.
`
`package Apache::ESSI;
`
`use strict;
`
`use Apache::Constants qw(:comon);
`use Apache::File ();
`use Text::ParseWords qw(quoteword5);
`my (%MODIFIED, %SU'BSTITUTION);
`
`14
`
`14
`
`
`
`
`
` 96 Chapter 4': Contem Hmzrliers
`
`e: Server-Side Includes -
`Netsc
`Baal-crnarks Options Directory Window
`Edit View (in
`
`iiarunmii Home}
`Edit]
`Reload] mad ll'i't(i{_[('ESl 0pen...l PrinL.;|
`
`File
`
`Bankl
`
`.1
`
`I-‘It
`
`
`
`Location:
`.. _.-..,,->,e. .._
`
`oca'|hast;'test.ehtml
`,-,o....._..._..._.._.._..........-.
`
`.._..
`
`Serve-1‘— Side Includes
`
`This is some straight text.
`
`This is a "Hello World!" include.
`
`Thefilesizeis59?bytes,anditwaslastmodifiedonU4./18;"98
`
`Today is Saturday, in arms domains" 1998.
`
`The user ag-_ent is Mazaffas/3.01Go!d(X3l,'f;Lemax 2. 0.33 .1585).
`
`Oops: flffegczi r:£e'vz'sz'c:rz fiy zero at/zzsrlfzome/immu/cor1fi’essz'.defs (me 45, chzuaflr 24. j
`
`Hereisanineludedfile:
`
`7 —»
`
`
`
`i III 1
`
`\
`
`I
`
`
`
`the quick brown fox jumps over the sleeping dog
`
`J
`to 1993 -_?_i?gilli3 Associates
`Last iivforlzfieti: Sm.‘ Apr E6’ 0?:50:57 1998‘
`-. __.._____._._.__:_+.,..........__._.-.,‘#.__._..______. .w.7 ,, __ e-._.__,,,,,,,.
`
`'
`
`.3-R;
`
`F£gr,r.i'e 4-2. /1 pugegeizenzited by /lj.'J6£C/J8.‘.'}L'55I
`
`We start as before by declaring the package name and loading various Perl libraiy
`modules. in addilion to the modules that we loaded in the Apache.-.-Footer‘ exznn—
`ple, we import the quoIewom's() function from the standard Perl Text.-.-Parsell’/ora’5
`module. This routine provides command shell—~like parsing of strings that coniain
`quote marks and backslash escapes. We also define two lexical variables,
`%MODIFIED and %SUBSTITUTION, which are global to the package.
`
`sub handler {
`
`my $r = shift;
`return DECLINED;
`$r—>cor1t;ent_type() eq ‘text/html'
`my $fh 2 Apache::E'ile—>new($r—>filename)
`return DECLINED;
`my Ssub = read_definit:ions($r)
`|
`i return SERV'E:R_ERROR;
`$ 1: —> ser1d_ht tp_header;
`$r->print($sub->($r, $fh)):
`return OK ;
`
`15
`
`15
`
`
`
`C0f£f8fll'1'1'(£?’l(UG?'S as File Processors
`
`‘)7
`
`in the /lpcrcbe;-/*'oorer example,
`subroutine is quite short. As
`The f.?C£i’iC£'f€‘.*‘(V)‘
`,{7gti'idler(') starts by examining the content type of the document being requested
`and declines to handle requests for non—i‘l'l'l‘vlL documents. The handler recovers
`the file's physical path by calling the request object's _/i‘i’ei'icime() method and
`attempts to open it. if the file open fails, the handler again returns an error code oi’
`DECLINED. This avoids Apcidie.-:Fooi'ei"‘s tedious checking of the file's existence
`and access permissions, at the cost of some efficiency every time a nonexistent file
`
`is requested.
`
`Once the file is opened, we call an internal function named reaci'_a’cf/iirii'ii'oris().
`This function reads the server—side includes configuration file and generates an
`;1n()l'1}’ITl()Ll.‘~i subroutine to do the actual processing of the document.
`If an error
`occurs while processing the configuration file,
`'I'(36£Cf__£fi:f/fl’!.fff()}1S() returns irrza,'cff
`and we return SE:RVER__ERROR in order to abort the transaction. Otherwise, we
`send the I-ITTP header and invoke the anonymous subroutine to perform the sub-
`
`stitutions on the contents of the file. The result of invoking the subroutine is sent
`to the client using the request objects prmr() method, and we return a result code
`of OK to indicate that everything went smoothly.
`
`sub reac1_deEiniti0ns {
`my Sr = shift;
`my Sdef 2 $r~>c3ir_c:onfig( 'ESSIDefs' );
`return unless Sdef;
`return unless —e
`($def = $r—>server__root_relat;ive{$def));
`
`Most of the interesting work occurs in i'ecic£_dc{/ii'1i£ioiis(). The idea here is to read
`the server—side include definitions, compile them, and then Lise them to generate
`
`in order to avoid
`an anonymous subroutine that does the actual substitutions.
`recompiling this subroutine unnecessarily, we cache its code reference in the
`package variable %SUBS'I‘ITUTION and reuse it if we can.
`
`subroutine begins by retrieving the path to the file that
`The f€CI6?L6fE"fl!'??l.[;f0T!$()
`contains the server—side include definitions. This information is contained in a per-
`
`in the configura—
`tlirectoiy configuration variable named ESSIDefs, which is set
`tion file using the PerlSerl/‘ctr directive and retrieved within the handler with the
`request objects a’i'r_co'rifz'g() method (see the end of the example for a representa-
`tive configuration file entry).
`If,
`for some reason,
`this variable isn‘t present, we
`return imdef Like other Apache configuration files, we allow this file to be speci-
`fied as either an absolute path or a partial path relative to the server root. We pass
`the path to the request object’s serue7;rooi_r‘e!at:.'ue(_) method. This convenient
`function prepends the server root
`to relative paths and leaves absolute paths
`alone. W/e next check that the file exists using the -9 file test operator and return
`
`Lmdefif not.
`
`return $SUBSTITUTION{$def} if $MODIFIED{$def} && SMODII-‘IED{$def} <= —M
`
`;
`
`16
`
`16
`
`
`
`
`
`98 C'l.mpter 4'.‘ Content l—L:mcl!e2's
`
`Having recovered the name of the definitions file, we next check the cache to see
`
`whether the subroutine definitions are already cached and,
`
`if so, whether the file
`
`hasn’t changed since the code was compiled and cached. We use two hashes for
`
`this purpose. The %SUBSTI'I'UTION array holds the compiled code and %I~/IODIFIED
`contains the modification date of the definition file the last time it was compiled.
`
`Both hashes are indexed by the definition file's path, allowing the module to han-
`dle the case in which several server—side include definition files are used for differ-
`
`ent parts of the document tree. if the modification time listed in %MODIFIED is less
`than or equal
`to the definition file’s current modification date, we return the
`cached subroutine.
`
`: ", #_PACKAGE__, $def;
`my $pae]-zage = join " :
`Spackage =~ tr/a—zA—ZD~9_/_/C;
`
`The next two lines are concerned with finding a unique namespace in which to
`compile the server—side include functions. Putting the functions in their own
`namespace decreases the chance that function side effects will have unwanted
`effects elsewhere in the module. We take the easy way out here by using the path
`to the definition file to synthesize a package name, which we store in a variable
`
`named $package.
`
`eval "package Spaekage; do ‘$def"';
`if($@l
`{
`
`$r—>log_error("Eva1 of $def did not return true: $@“);
`return;
`
`}
`
`We then invoke ez2al() to compile the subroutine definitions into the newly cho-
`sen namespace. We use the package declaration to set the namespace and do to
`load and run the definitions file. We use do here rather than the more common
`
`require because do unconditionally recompiles code files even if they have been
`loaded previously. If the em! was unsuccessful, we log an error and return imdejf
`
`$SUBS‘I‘ITUTION{$def} = sub {
`do_substitutions ($paekage, @_) ,-
`
`};
`
`$MODIFIED{$def} : —M $def;
`return $SUBSTITUTION[$def};
`
`# store modification date
`
`}
`
`Before we exit
`
`2“(2‘6lC?L6!‘E_’f1"."i7..'.-1'.fO1’I5(), we create a new anon