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 9 - Perl API Reference Guide / Other Core Perl API Classes
The Apache::Table Class

The HTTP message protocol is simple largely because of its consistent use of the key/value paradigm in its request and response header fields. Because much of an external module's work is getting and setting these header fields, Apache provides a simple yet powerful interface called the table structure. Apache tables are keyed case-insensitive lookup tables. API function calls allow you to obtain the list of defined keys, iterate through them, get the value of a key, and set key values. Since many HTTP header fields are potentially multivalued, Apache also provides functionality for getting, setting, and merging the contents of multivalued fields.

The following five C data structures are implemented as tables. This list is likely to grow in the future.

  • headers_in
  • headers_out
  • err_headers_out
  • notes
  • subprocess_env

As discussed in "The Apache Request Object," the Perl API provides five method calls, named headers_in(), headers_out(), err_headers_out(), notes(), and subprocess_env(), that retrieve these tables. The Perl manifestation of the Apache table API is the Apache::Table class. It provides a TIEHASH interface that allows transparent access to its methods via a tied hash reference, as well as API methods that can be called directly.

The TIEHASH interface is easy to use. Simply call one of the methods listed earlier in a scalar context to return a tied hash reference. For example:

my $table = $r->headers_in;

The returned object can now be used to get and set values in the headers_in table by treating it as an ordinary hash reference, but the keys are looked up case-insensitively. Examples:

my $type = $table->{'Content-type'};
my $type = $table->{'CONTENT-TYPE'};  # same thing
$table->{'Expires'} = 'Sat, 08 Aug 1998 01:39:20 GMT';

If the field you are trying to access is multivalued, then the tied hash interface suffers the limitation that fetching the key will only return the first defined value of the field. You can get around this by using the object-oriented interface to access the table (we show an example of this later) or by using the each operator to access each key and value sequentially. The following code snippet shows one way to fetch all the Set-cookie fields in the outgoing HTTP header:

while (my($key, $value) = each %{$r->headers_out}) {
  push @cookies, $value if lc($key) eq 'set-cookie';
}

When you treat an Apache::Table object as a hash reference, you are accessing its internal get() and set() methods (among others) indirectly. To gain access to the full power of the table API, you can invoke these methods directly by using the method call syntax.

Here is the list of publicly available methods in Apache::Table, along with brief examples of usage:

add()

The add() method will add a key/value pair to the table. Because Apache tables can contain multiple instances of a key, you may call add() multiple times with different values for the same key. Instead of the new value of the key replacing the previous one, it will simply be appended to the list. This is useful for multivalued HTTP header fields such as Set-Cookie. The outgoing HTTP header will contain multiple instances of the field.

my $out = $r->headers_out;
for my $cookie (@cookies) {
   $out->add("Set-cookie" => $cookie);
}

Another way to add multiple values is to pass an array reference as the second argument. This code has the same effect as the previous example:

my $out = $r->headers_out;
$out->add("Set-cookie" => \@cookies);

clear()

This method wipes the current table clean, discarding its current contents. It's unlikely that you would want to perform this on a public table, but here's an example that clears the notes table:

$r->notes->clear;

do()

This method provides a way to iterate through an entire table item by item. Pass it a reference to a code subroutine to be called once for each table entry. The subroutine should accept two arguments corresponding to the key and value, respectively, and should return a true value. The routine can return a false value to terminate the iteration prematurely.

This example dumps the contents of the headers_in field to the browser:

$r->headers_in->do(sub {
                      my($key, $value) = @_;
                      $r->print("$key => $value\n");
                      1;
                   });

For another example of do(), see Example 7-12, where we use it to transfer the incoming headers from the incoming Apache request to an outgoing LWP HTTP::Request object.

get()

Probably the most frequently called method, the get() function returns the table value at the given key. For multivalued keys, get() implements a little syntactic sugar. Called in a scalar context, it returns the first value in the list. Called in an array context, it returns all values of the multivalued key.

my $ua      = $r->headers_in->get('User-agent');
my @cookies = $r->headers_in->get('Cookie');

get() is the underlying method that is called when you use the tied hash interface to retrieve a key. However, the ability to fetch a multivalued key as an array is only available when you call get() directly using the object-oriented interface.

merge()

merge() behaves like add(), except that each time it is called the new value is merged into the previous one, creating a single HTTP header field containing multiple comma-delimited values.

In the HTTP protocol, a comma-separated list of header values is equivalent to the same values specified by repeated header lines. Some buggy clients may not accept merged headers, however. In this case, it is worthwhile to control the merging explicitly and avoid merging headers that cause trouble (like Set-cookie).

merge() works like add(). You can either merge a series of entries one at a time:

my @languages = qw(en fr de);
foreach (@languages) {
 $r->headers_out->merge("Content-language" => $_);
}

or merge a bunch of entries in a single step by passing an array reference:

$r->headers_out->merge("Content-language" => \@languages);

new()

The new() method is available to create an Apache::Table object from scratch. It requires an Apache object to allocate the table and, optionally, the number of entries to initially allocate. Note that just like the other Apache::Table objects returned by API methods, references cannot be used as values, only strings.

my $tab = Apache::Table->new($r); #default, allocates 10 entries
my $tab = Apache::Table->new($r, 20); #allocate 20 entries

set()

set() takes a key/value pair and updates the table with it, creating the key if it didn't exist before, or replacing its previous value(s) if it did. The resulting header field will be single-valued. Internally this method is called when you assign a value to a key using the tied hash interface.

Here's an example of using set() to implement an HTTP redirect:

$r->headers_out->set(Location => 'http://www.modperl.com/');

unset()

This method can be used to remove a key and its contents. If there are multiple entries with the same key, they will all be removed.

$r->headers_in->unset('Referer');
   Show Contents   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.