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
Chaining Content Handlers

In this section...

Introduction
Simple Case of Stacked Handlers
Coordinating Stacked Handlers
Stacked Handler Pipelining
Other Types of Stacked Handlers

Introduction

   Show Contents   Go to Top   Previous Page   Next Page

The C-language Apache API only allows a single content handler to completely process a request. Several handlers may be given a shot at it, but the first one to return an OK status will terminate the content handling phase of the transaction.

There are times when it would be nice to chain handlers into a pipeline. For example, one handler could add canned headers and footers to the page, another could correct spelling errors, while a third could add trademark symbols to all proprietary names. Although the native C API can't do this yet,3 the Perl API can, using a technique called "stacked handlers."

It is actually quite simple to stack handlers. Instead of declaring a single module or subroutine in the PerlHandler directive, you declare several. Each handler will be called in turn in the order in which it was declared. The exception to this rule is if one of the handlers in the series returns an error code (anything other than OK, DECLINED, or DONE). Handlers can adjust the stacking order themselves, or even arrange to process each other's output.

Simple Case of Stacked Handlers

   Show Contents   Go to Top   Previous Page   Next Page

Example 4-20 gives a very simple example of a stack of three content handlers. It's adapted slightly from the mod_perl manual page. For simplicity, all three handlers are defined in the same file, and are subroutines named header(), body(), and footer(). As the names imply, the first handler is responsible for the top of the page (including the HTTP header), the second is responsible for the middle, and the third for the bottom.

A suitable configuration section looks like this:

PerlModule My
<Location /My>
 SetHandler perl-script
 PerlHandler My::header My::body My::footer
</Location>

We first load the whole module into memory using the PerlModule directive. We then declare a URI location /My and assign the perl-script handler to it. Perl in turn is configured to run the My::header, My::body, and My::footer subroutines by passing them as arguments to a PerlHandler directive. In this case, the /My location has no corresponding physical directory, but there's no reason that it couldn't.

After bringing in the OK constant from Apache::Constants, we define the subroutines header(), body(), and footer(). header() sets the document's content type to plain text, sends the HTTP header, and prints out a line at the top of the document. body() and footer() both print out a line of text to identify themselves. The resulting page looks like this:

header text
body text
footer text

Example 4-20.A Simple Stacked Handler 

package My;
use strict;
use Apache::Constants 'OK';
sub header {
  my $r = shift;
  $r->content_type('text/plain');
  $r->send_http_header;
  $r->print("header text\n");
  OK;
}
sub body {
  my $r = shift;
  $r->print("body text\n");
  OK;
}
sub footer {
  my $r = shift;
  $r->print("footer text\n");
  OK;
}
1;

Coordinating Stacked Handlers

   Show Contents   Go to Top   Previous Page   Next Page

Stacked handlers often have to coordinate their activities. In the example of the previous section, the header() handler must be run before either of the other two in order for the HTTP header to come out correctly. Sometimes it's useful to make the first handler responsible for coordinating the other routines rather than relying on the configuration file. The request object's push_handlers() method will help you do this.

push_handlers() takes two arguments: a string representing the phase to handle, and a reference to a subroutine to handle that phase. For example, this code fragment will arrange for the footer() subroutine to be the next content handler invoked:

$r->push_handlers(PerlHandler => \&footer);

With this technique, we can rewrite the previous example along the lines shown in Example 4-21. In the revised module, we declare a subroutine named handler() that calls push_handlers() three times, once each for the header, body, and footer of the document. It then exits. The other routines are unchanged.

The revised configuration file entry looks like this:

<Location /MyChain>
 SetHandler perl-script
 PerlHandler My::Chain
</Location>

Because we followed the mod_perl convention of naming the first handler subroutine handler(), there's now no need for a PerlModule statement to load the module into memory.

Example 4-21. Coordinated Stacked Handlers

package My::Chain;
use strict;
use Apache::Constants 'OK';
sub handler {
   my $r = shift;
   for my $cv (\&header, \&body, \&footer) { 
$r->push_handlers(PerlHandler => $cv); } OK; }
sub header {
   my $r = shift;
   $r->content_type('text/plain');
   $r->send_http_header;
   $r->print("header text\n");
   OK;
}
sub body {
    my $r = shift;
   $r->print("body text\n");
   OK;
}
sub footer {
   my $r = shift;
   $r->print("footer text\n");
   OK;
}
1;
__END__
   Show Contents   Go to Top   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.