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 10 - C API Reference Guide, Part I
The Table API

In this section...

Introduction
The table and table_entry Data Types
Creating and Copying Tables
Getting and Setting Table Values
Other Table Functions

Introduction

   Show Contents   Go to Top   Previous Page   Next Page

Apache provides a general API for creating and maintaining lookup tables. Apache tables are ubiquitous, used for everything from storing the current request's outgoing HTTP headers to maintaining the list of environment variables passed to subprocesses.

Tables are similar to Perl hashes in that they are lists of key/value pairs. However, unlike a Perl hash, keys are case-insensitive, and a single key may correspond to a list of several values.2 In addition, Apache table keys and values are always strings; arbitrary data types cannot be used.

The table and table_entry Data Types

   Show Contents   Go to Top   Previous Page   Next Page

Currently, a table is an Apache array containing array elements of the table_entry data type (defined in include/alloc.h):

typedef struct {
    char *key;   /* the key */
   char *val;   /* the value */
} table_entry;

When fetching or setting the value of a key, Apache searches for the key using a simple linear search. Since most tables are short, this usually doesn't impose a significant overhead. You will usually not want to access the table_entry directly, but use API function calls to manipulate the keys and values for you. If you do read directly from the table_entry, a note in the include file indicates that you should check the key for null. This is because the table_entry may be made part of a more sophisticated hash table in the future.

The table structure itself is a private data type intended to be accessed via an opaque table*. If you want to peek at its definition, you can find it in include/alloc.c. It is equally straightforward:

struct table {
    array_header a;
#ifdef MAKE_TABLE_PROFILE
   void *creator;
#endif
};

The MAKE_TABLE_PROFILE define is part of Apache's debugging code and is usually undefined, so table is really just an array header.

Creating and Copying Tables

   Show Contents   Go to Top   Previous Page   Next Page

If you need a table of key/value pairs that is private to your own module, you can use these API routines to create it. You can either create a new empty table or start with one that is already defined and make a copy of it. These functions are defined in the include/alloc.h file, which is automatically included when you bring in include/httpd.h.

table *ap_make_table (pool *p, int nelts)

ap_make_table() creates a new empty table, given a resource pool pointer and an estimate of the number of elements you expect to add. If the nelts argument is nonzero, that number of table_entrytables will be pre-allocated for efficiency. Regardless of its initial size, the table grows as necessary to accommodate new entries and table merging operations.

Accessitable *my_table = ap_make_table(p, 25);

table *ap_copy_table (pool *p, const table *t)

This function takes a resource pool and an existing table and makes a replica of the table, returning a pointer to the copy. You can then change the contents of the copy without modifying the original. In this example, we make a copy of the headers_in table:

table *my_headers = ap_copy_table(r->pool, r->headers_in);

Getting and Setting Table Values

   Show Contents   Go to Top   Previous Page   Next Page

These routines allow you to add new entries to the table, to change existing ones, and to retrieve entries.

const char *ap_table_get (const table *t, const char *key)

Given a table pointer and a key, ap_table_get() returns the value of the entry at that key as a char*. If there are multiple values for that particular key, the function will only return the first one it finds, which will be the first entry added.

In this example, we recover the string value of the incoming User-agent header:

const char *ua = ap_table_get(r->headers_in, "User-agent");

To iterate through the multiple values for the same key, use the ap_table_do() function described later in this section.

void ap_table_set (table *t, const char *key, const char *val)

ap_table_set() sets the entry named by key to the character string in val. If an entry with the same key already exists, its value is replaced. Otherwise, a new entry is created. If more than one entry has the same key, the extraneous ones are deleted, making the key single-valued.

Internally, Apache calls ap_pstrdup() on the key and the value and stores copies of them in the table. This means that you are able to change or dispose of the original variables without worrying about disrupting the table.

Here's an example of using this function to set the outgoing headers field Location to the string http://www.modperl.com/. Because Location is a single-valued field, ap_table_set() is the correct call to use:

ap_table_set(r->headers_out, "Location", "http://www.modperl.com/");

void ap_table_setn (table *t, const char *key, const char *val)

This function behaves the same as ap_table_set(), but the character strings for key and val are not copied with ap_pstrdup(). You must ensure that the strings remain valid for the lifetime of the table. The previous example is a good candidate for ap_table_setn(), as it uses static strings for both the key and value.

void ap_table_add (table *t, const char *key, const char *val)

This function is similar to ap_table_set(), but existing entries with the same key are not replaced. Instead, the new entry is added to the end of the list, making the key multivalued.

Internally, Apache calls ap_pstrdup() on the key and the value, allowing you to change or dispose of the original variables without worrying about disrupting the table.

This example adds several Set-cookie fields to the outgoing HTTP headers table:

for(i=0; cookies[i]; i++) {
    ap_table_add(r->headers_out, "Set-cookie", cookies[i]);
}

void ap_table_addn (table *t, const char *key, const char *val)

This function behaves like ap_table_add(), but key and val are not duplicated before storing them into the table. This function saves a little time and memory if you are working with static strings.

void ap_table_merge (table *t, const char *key, const char *val)

ap_table_merge() merges a new key value into the existing entry by appending it to what's already there. This is used for comma-delimited header fields such as Content-language. For example, this series of calls will result in a value of en, fr,sp in the Content-language field:

ap_table_merge(r->headers_out, "Content-language", "en");
ap_table_merge(r->headers_out, "Content-language", "fr");
ap_table_merge(r->headers_out, "Content-language", "sp");

Like ap_table_set(), the key and value are copied using ap_pstrdup() before moving them into the table.

void ap_table_mergen (table *t, const char *key, const char *val)

This function is the same as ap_table_merge, but the key and val arguments are not copied with ap_pstrdup() before entering them into the table.

void ap_table_unset (table *t, const char *key)

ap_table_unset() deletes all entries having the indicated key. This example removes the Referer field from the incoming headers, possibly in preparation for making an anonymous proxy request (see Chapter 7):

ap_table_unset(r->headers_in, "Referer");

void ap_table_do (int (*comp)(void *, const char *, const char *),

void *rec, const table *t,...);
ap_table_get() and ap_table_getn() work well for single-valued keys, but there are a few instances in which keys are not unique. To access all the values of these keys, you will have to use ap_table_do() to iterate over the table.

As its prototype indicates, this function is more complicated than the ones we've seen before. The function's first argument is a pointer to a callback function that will be called during the iteration process. The second argument is a void * that can be used to pass some arbitrary information to the callback. The third argument is the table * itself. This is followed by a variable number of char * key arguments, terminated by a null. ap_ table_do() will iterate over the table, invoking the callback routine only when a table entries' key matches a key in the given list. If no keys are given, the function will invoke the callback routine for all of the table entries.

The callback function should have this function prototype:

int callback(void *rec, const char *key, const char *value);

The first argument corresponds to the void * argument passed to ap_table_do(), and the second and third arguments are the key and value of the current table entry. The callback should do whatever work it needs to do (for example, copying the value into an Apache array), and return a true value. The callback can return 0 in order to abort ap_table_do() prematurely.

Here's a callback that simply prints out the key name and value without performing further processing:

static int header_trace(void *data, const char *key, const char *val)
{
   request_rec *r = (request_rec *)data;
   ap_rprintf(r, "Header Field `%s' == `%s'\n", key, val);
   return TRUE;
}

Here's how the callback can be used to print out the contents of the outgoing headers table:

ap_table_do(header_trace, r, r->headers_out, NULL);

And in this example, the callback is only invoked for the Content-type and Content-length fields:

ap_table_do(header_trace, (void*)r, r->headers_out,
            "Content-type", "Content-length", NULL);
   Show Contents   Go to Top   Previous Page   Next Page
Copyright © 1999 by O'Reilly & Associates, Inc.