home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Apache The Definitive Guide, 3rd EditionApache: The Definitive GuideSearch this book

20.8. Hooks, Optional Hooks, and Optional Functions

In Apache 1.x modules hooked into the appropriate "phases" of the main server by putting functions into appropriate slots in the module structure. This process is known as "hooking." This has been revised in Apache 2.0 — instead a single function is called at startup in each module, and this registers the functions that need to be called. The registration process also permits the module to specify how it should be ordered relative to other modules for each hook. (In Apache 1.x this was only possible for all hooks in a module instead of individually and also had to be done in the configuration file, rather than being done by the module itself.)

This approach has various advantages. First, the list of hooks can be extended arbitrarily without causing each function to have a huge unwieldy list of NULL entries. Second, optional modules can export their own hooks, which are only invoked when the module is present, but can be registered regardless — and this can be done without modification of the core code.

Another feature of hooks that we think is pretty cool is that, although they are dynamic, they are still typesafe — that is, the compiler will complain if the type of the function registered for a hook doesn't match the hook (and each hook can use a different type of function).[69] They are also extremely efficient.

[69]We'll admit to bias here — Ben designed and implemented the hooking mechanisms in Apache 2.0.

So, what exactly is a hook? Its a point at which a module can request to be called. So, each hook specifies a function prototype, and each module can specify one (or more in 2.0) function that gets called at the appropriate moment. When the moment arrives, the provider of the hook calls all the functions in order.[70] It may terminate when particular values are returned — the hook functions can return either "declined" or "ok" or an error. In the first case all are called until an error is returned (if one is, of course); in the second, functions are called until either an error or "ok" is returned. A slight complication in Apache 2.0 is that because each hook function can define the return type, it must also define how "ok," "decline," and errors are returned (in 1.x, the return type was fixed, so this was easier).

[70]Note that the order is determined at runtime in Apache 2.0.

Although you are unlikely to want to define a hook, it is useful to know how to go about it, so you can understand them when you come across them (plus, advanced module writers may wish to define optional hooks or optional functions).

Before we get started, it is worth noting that Apache hooks are defined in terms of APR hooks — but the only reason for that is to provide namespace separation between Apache and some other package linked into Apache that also uses hooks.

20.8.1. Hooks

A hook comes in five parts: a declaration (in a header, of course), a hook structure, an implementation (where the hooked functions get called), a call to the implementation, and a hooked function. The first four parts are all provided by the author of the hook, and the last by its user. They are documented in .../include/ap_config.h. Let's cover them in order. First, the declaration. This consists of the return type, the name of the hook, and an argument list. Notionally, it's just a function declaration with commas in strange places. So, for example, if a hook is going to a call a function that looks like:

int some_hook(int,char *,struct x);

then the hook would be declared like this:

AP_DECLARE_HOOK(int,some_hook,(int,char *,struct x))

Note that you really do have to put brackets around the arguments (even if there's only one) and no semicolon at the end (there's only so much we can do with macros!). This declares everything a module using a hook needs, and so it would normally live in an appropriate header.

The next thing you need is the hook structure. This is really just a place that the hook machinery uses to store stuff. You only need one for a module that provides hooks, even if it provides more than one hook. In the hook structure you provide a link for each hook:

APR_HOOK_STRUCT(
	APR_HOOK_LINK(some_hook)
	APR_HOOK_LINK(some_other_hook)
)

Once you have the declaration and the hook structure, you need an implementation for the hook — this calls all the functions registered for the hook and handles their return values. The implementation is actually provided for you by a macro, so all you have to do is invoke the macro somewhere in your source (it can't be implemented generically because each hook can have different arguments and return types). Currently, there are three different ways a hook can be implemented — all of them, however, implement a function called ap_run_name( ). If it returns no value (i.e., it is a void function), then implement it as follows:

AP_IMPLEMENT_HOOK_VOID(some_hook,(char *a,int b),(a,b))

The first argument is the name of the hook, and the second is the declaration of the hook's arguments. The third is how those arguments are used to call a function (that is, the hook function looks like void some_hook(char *a,int b) and calling it looks like some_hook(a,b)). This implementation will call all functions registered for the hook.

If the hook returns a value, there are two variants on the implementation — one calls all functions until one returns something other than "ok" or "decline" (returning something else normally signifies an error, which is why we stop at that point). The second runs functions until one of them returns something other than "decline." Note that the actual values of "ok" and "decline" are defined by the implementor and will, of course, have values appropriate to the return type of the hook. Most functions return int s and use the standard values OK and DECLINE as their return values. Many return an HTTP error value if they have an error. An example of the first variant is as follows:

AP_IMPLEMENT_HOOK_RUN_ALL(int,some_hook,(int x),(x),OK,DECLINE)

The arguments are, respectively, the return type of the hook, the hook's name, the arguments it takes, the way the arguments are used in a function call, the "ok" value, and the "decline" value. By the way, the reason this is described as "run all" rather than "run until the first thing that does something other than OK or DECLINE" is that the normal (i.e., nonerror) case will run all the registered functions.

The second variant looks like this:

AP_IMPLEMENT_HOOK_RUN_FIRST(char *,some_hook,(int k,const char *s),(k,s),NULL)

The arguments are the return type of the hook, the hook name, the hook's arguments, the way the arguments are used, and the "decline" value.

The final part is the way you register a function to be called by the hook. The declaration of the hook defines a function that does the registration, called ap_hook_name( ). This is normally called by a module from its hook-registration function, which, in turn, is pointed at by an element of the module structure. This function always takes four arguments, as follows:

ap_hook_some_hook(my_hook_function,pre,succ,APR_HOOK_MIDDLE);

Note that since this is not a macro, it actually has a semicolon at the end! The first argument is the function the module wants called by the hook. One of the pieces of magic that the hook implementation does is to ensure that the compiler knows the type of this function, so if it has the wrong arguments or return type, you should get an error. The second and third arguments are NULL-terminated arrays of module names that must precede or follow (respectively) this module in the order of registered hook functions. This is to provide fine-grained control of execution order (which, in Apache 1.x could only be done in a very ham-fisted way). If there are no such constraints, then NULL can be passed instead of a pointer to an empty array. The final argument provides a coarser mechanism for ordering — the possibilities being APR_HOOK_FIRST, APR_HOOK_MIDDLE, and APR_HOOK_LAST. Most modules should use APR_HOOK_MIDDLE. Note that this ordering is always overridden by the finer-grained mechanism provided by pre and succ.

You might wonder what kind of hooks are available. Well, a list can be created by running the Perl script .../support/list_hooks.pl. Each hook should be documented in the online Apache documentation.

20.8.2. Optional Hooks

Optional hooks are almost exactly like standard hooks, except that they have the property that they do not actually have to be implemented — that sounds a little confusing, so let's start with what optional hooks are used for, and all will be clear. Consider an optional module — it may want to export a hook, but what happens if some other module uses that hook and the one that exports it is not present? With a standard hook Apache would just fail to build. Optional hooks allow you to export hooks that may not actually be there at runtime. Modules that use the hooks work fine even when the hook isn't there — they simply don't get called. There is a small runtime penalty incurred by optional hooks, which is the main reason all hooks are not optional.

An optional hook is declared in exactly the same way as a standard hook, using AP_DECLARE_HOOK as shown earlier.

There is no hook structure at all; it is maintained dynamically by the core. This is less efficient than maintaining the structure, but is required to make the hooks optional.

The implementation differs from a standard hook implementation, but only slightly — instead of using AP_IMPLEMENT_HOOK_RUN_ALL and friends, you use AP_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL and so on.

Registering to use an optional hook is again almost identical to a standard hook, except you use a macro to do it: instead of ap_hook_name(...) you use AP_OPTIONAL_HOOK(name,...). Again, this is because of their dynamic nature.

The call to your hook function from an optional hook is the same as from a standard one — except that it may not happen at all, of course!

20.8.3. Optional Hook Example

Here's a complete example of an optional hook (with comments following after the lines to which they refer). This can be found in .../modules/experimental. It comprises three files, mod_optional_hook_export.h, mod_optional_hook_export.c, and mod_optional_hook_import.c. What it actually does is call the hook, at logging time, with the request string as an argument.

First we start with the header, mod_optional_hook_export.h.

#include "ap_config.h"

This header declares the various macros needed for hooks.

AP_DECLARE_HOOK(int,optional_hook_test,(const char *))

Declare the optional hook (i.e., a function that looks like int optional_hook_test(const char *)). And that's all that's needed in the header.

Next is the implementation file, mod_optional_hook_export.c.

#include "httpd.h"
#include "http_config.h"
#include "mod_optional_hook_export.h"
#include "http_protocol.h"

Start with the standard includes — but we also include our own declaration header (although this is always a good idea, in this case it is a requirement, or other things won't work).

AP_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(int,optional_hook_test,(const char *szStr),
                                   (szStr),OK,DECLINED)

Then we go to the implementation of the optional hook — in this case it makes sense to call all the hooked functions, since the hook we are implementing is essentially a logging hook. We could have declared it void, but even logging can go wrong, so we give the opportunity to say so.

static int ExportLogTransaction(request_rec *r)
{
    return ap_run_optional_hook_test(r->the_request);
}

This is the function that will actually run the hook implementation, passing the request string as its argument.

static void ExportRegisterHooks(apr_pool_t *p)
{
    ap_hook_log_transaction(ExportLogTransaction,NULL,NULL,APR_HOOK_MIDDLE);
}

Here we hook the log_transaction hook to get hold of the request string in the logging phase (this is, of course, an example of the use of a standard hook).

module optional_hook_export_module =
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ExportRegisterHooks
};

Finally, the module structure — the only thing we do in this module structure is to add hook registration.

Finally, an example module that uses the optional hook, optional_hook_import.c.

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "mod_optional_hook_export.h"

Again, the standard stuff, but also the optional hooks declaration (note that you always have to have the code available for the optional hook, or at least its header, to build with).

static int ImportOptionalHookTestHook(const char *szStr)
{
    ap_log_error(APLOG_MARK,APLOG_ERR,OK,NULL,"Optional hook test said: %s",
                 szStr);

    return OK;
}

This is the function that gets called by the hook. Since this is just a test, we simply log whatever we're given. If optional_hook_export.c isn't linked in, then we'll log nothing, of course.

static void ImportRegisterHooks(apr_pool_t *p)
{
    AP_OPTIONAL_HOOK(optional_hook_test,ImportOptionalHookTestHook,NULL,
                     NULL,APR_HOOK_MIDDLE);
}

Here's where we register our function with the optional hook.

module optional_hook_import_module=
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ImportRegisterHooks
};

And finally, the module structure, once more with only the hook registration function in it.

20.8.4. Optional Functions

For much the same reason as optional hooks are desirable, it is also nice to be able to call a function that may not be there. You might think that DSOs provide the answer,[71] and you'd be half right. But they don't quite, for two reasons — first, not every platform supports DSOs, and second, when the function is not missing, it may be statically linked. Forcing everyone to use DSOs for all modules just to support optional functions is going too far. Particularly since we have a better plan!

[71]Dynamic Shared Objects — i.e., shared libraries, or DLLs in Windows parlance.

An optional function is pretty much what it sounds like. It is a function that may turn out, at runtime, not to be implemented (or not to exist at all, more to the point). So, there are five parts to an optional function: a declaration, an implementation, a registration, a retrieval, and a call. The export of the optional function declares it:

APR_DECLARE_OPTIONAL_FN(int,some_fn,(const char *thing))

This is pretty much like a hook declaration: you have the return type, the name of the function, and the argument declaration. Like a hook declaration, it would normally appear in a header.

Next it has to be implemented:

int some_fn(const char *thing)
{
    /* do stuff */
}

Note that the function name must be the same as in the declaration.

The next step is to register the function (note that optional functions are a bit like optional hooks in a distorting mirror — some parts switch role from the exporter of the function to the importer, and this is one of them):

APR_REGISTER_OPTIONAL_FN(some_fn);

Again, the function name must be the same as the declaration. This is normally called in the hook registration process.[72]

[72]There is an argument that says it should be called before then, so it can be retrieved during hook registration, but the problem is that there is no "earlier" — that would require a hook!

Next, the user of the function must retrieve it. Because it is registered during hook registration, it can't be reliably retrieved at that point. However, there is a hook for retrieving optional functions (called, obviously enough, optional_fn_retrieve). Or it can be done by keeping a flag that says whether it has been retrieved and retrieving it when it is needed. (Although it is tempting to use the pointer to function as the flag, it is a bad idea — if it is not registered, then you will attempt to retrieve it every time instead of just once). In either case, the actual retrieval looks like this:

APR_OPTIONAL_FN_TYPE(some_fn) *pfn;

pfn=APR_RETRIEVE_OPTIONAL_FN(some_fn);

From there on in, pfn gets used just like any other pointer to a function. Remember that it may be NULL, of course!

20.8.5. Optional Function Example

As with optional hooks, this example consists of three files which can be found in .../modules/experimental: mod_optional_fn_export.c, mod_optional_fn_export.h and mod_optional_fn_import.c. (Note that comments for this example follow the code line(s) to which they refer.)

First the header, mod_optional_fn_export.h:

#include "apr_optional.h"

Get the optional function support from APR.

APR_DECLARE_OPTIONAL_FN(int,TestOptionalFn,(const char *));

And declare our optional function, which really looks like int TestOptionalFn(const char *).

Now the exporting file, mod_optional_fn_export.c:

#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "mod_optional_fn_export.h"

As always, we start with the headers, including our own.

static int TestOptionalFn(const char *szStr)
{
    ap_log_error(APLOG_MARK,APLOG_ERR,OK,NULL,
                 "Optional function test said: %s",szStr);

    return OK;
}

This is the optional function — all it does is log the fact that it was called.

static void ExportRegisterHooks(apr_pool_t *p)
{
    APR_REGISTER_OPTIONAL_FN(TestOptionalFn);
}

During hook registration we register the optional function.

module optional_fn_export_module=
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ExportRegisterHooks
};

And finally, we see the module structure containing just the hook registration function.

Now the module that uses the optional function, mod_optional_fn_import.c:

#include "httpd.h"
#include "http_config.h"
#include "mod_optional_fn_export.h"
#include "http_protocol.h"

These are the headers. Of course, we have to include the header that declares the optional function.

static APR_OPTIONAL_FN_TYPE(TestOptionalFn) *pfn;

We declare a pointer to the optional function — note that the macro APR_OPTIONAL_FN_TYPE gets us the type of the function from its name.

static int ImportLogTransaction(request_rec *r)
{
    if(pfn)
        return pfn(r->the_request);
    return DECLINED;
}

Further down we will hook the log_transaction hook, and when it gets called we'll then call the optional function — but only if its present, of course!

static void ImportFnRetrieve(void)
{
    pfn=APR_RETRIEVE_OPTIONAL_FN(TestOptionalFn);
}

We retrieve the function here — this function is called by the optional_fn_retrieve hook (also registered later), which happens at the earliest possible moment after hook registration.

static void ImportRegisterHooks(apr_pool_t *p)
{
    ap_hook_log_transaction(ImportLogTransaction,NULL,NULL,APR_HOOK_MIDDLE);
    ap_hook_optional_fn_retrieve(ImportFnRetrieve,NULL,NULL,APR_HOOK_MIDDLE);
}

And here's where we register our hooks.

module optional_fn_import_module =
{
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ImportRegisterHooks
};

And, once more, the familiar module structure.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.