It is also possible for modules to
be configured on a per-directory, per-URL, or per-file basis. Again,
each module optionally creates its own per-directory configuration
(the same structure is used for all three cases). This configuration
is made available to modules either directly (during configuration)
or indirectly (once the server is running), through the
request_rec
structure, which is detailed in the
next section.
Note that the module doesn’t care how the configuration has been set up in terms of servers, directories, URLs, or file matches — the core of the server works out the appropriate configuration for the current request before modules are called by merging the appropriate set of configurations.
The method differs from per-server configuration, so here’s an example, taken this time from the standard module, modules/metadata/mod_expires.c:
typedef struct { int active; char *expiresdefault; apr_table_t *expiresbytype; } expires_dir_config;
First we have a per-directory configuration structure:
static void *create_dir_expires_config(apr_pool_t *p, char *dummy) { expires_dir_config *new = (expires_dir_config *) apr_pcalloc(p, sizeof(expires_dir_config)); new->active = ACTIVE_DONTCARE; new->expiresdefault = ""; new->expiresbytype = apr_table_make(p, 4); return (void *) new; }
This is the function that creates it, which will be linked from the
module structure, as usual. Note that the active
member is set to a default that can’t be set by
directives — this is used later on in the merging function.
static const char *set_expiresactive(cmd_parms *cmd, void *in_dir_config, int arg) { expires_dir_config *dir_config = in_dir_config; /* if we're here at all it's because someone explicitly * set the active flag */ dir_config->active = ACTIVE_ON; if (arg == 0) { dir_config->active = ACTIVE_OFF; }; return NULL; } static const char *set_expiresbytype(cmd_parms *cmd, void *in_dir_config, const char *mime, const char *code) { expires_dir_config *dir_config = in_dir_config; char *response, *real_code; if ((response = check_code(cmd->pool, code, &real_code)) == NULL) { apr_table_setn(dir_config->expiresbytype, mime, real_code); return NULL; }; return apr_pstrcat(cmd->pool, "'ExpiresByType ", mime, " ", code, "': ", response, NULL); } static const char *set_expiresdefault(cmd_parms *cmd, void *in_dir_config, const char *code) { expires_dir_config * dir_config = in_dir_config; char *response, *real_code; if ((response = check_code(cmd->pool, code, &real_code)) == NULL) { dir_config->expiresdefault = real_code; return NULL; }; return apr_pstrcat(cmd->pool, "'ExpiresDefault ", code, "': ", response, NULL); } static const command_rec expires_cmds[] = { AP_INIT_FLAG("ExpiresActive", set_expiresactive, NULL, DIR_CMD_PERMS, "Limited to 'on' or 'off'"), AP_INIT_TAKE2("ExpiresBytype", set_expiresbytype, NULL, DIR_CMD_PERMS, "a MIME type followed by an expiry date code"), AP_INIT_TAKE1("ExpiresDefault", set_expiresdefault, NULL, DIR_CMD_PERMS, "an expiry date code"), {NULL} };
This sets the various options — nothing particularly out of the
ordinary there — but note a few features. First,
we’ve omitted the function check_code(
)
, which does some complicated stuff we
don’t really care about here. Second, unlike
per-server config, we don’t have to find the config
ourselves. It is passed to us as the second argument of each
function — the DIR_CMD_PERMS
(which is
#define
d earlier to be
OR_INDEX
) is what tells the core it is
per-directory and triggers this behavior:
static void *merge_expires_dir_configs(apr_pool_t *p, void *basev, void *addv) { expires_dir_config *new = (expires_dir_config *) apr_pcalloc(p, sizeof(expires_ dir_config)); expires_dir_config *base = (expires_dir_config *) basev; expires_dir_config *add = (expires_dir_config *) addv; if (add->active == ACTIVE_DONTCARE) { new->active = base->active; } else { new->active = add->active; }; if (add->expiresdefault[0] != '\0') { new->expiresdefault = add->expiresdefault; } else { new->expiresdefault = base->expiresdefault; } new->expiresbytype = apr_table_overlay(p, add->expiresbytype, base->expiresbytype); return new; }
Here we have a more complex example of a merging function — the
active
member is set by the overriding config
(here called addv
) if it was set there at all, or
it comes from the base. expiresdefault
is set
similarly but expiresbytype
is the combination of
the two sets:
static int add_expires(request_rec *r) { expires_dir_config *conf; ... conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config, &expires_module);
This code snippet shows how the configuration is found during request processing:
static void register_hooks(apr_pool_t *p) { ap_hook_fixups(add_expires,NULL,NULL,APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA expires_module = { STANDARD20_MODULE_STUFF, create_dir_expires_config, /* dir config creater */ merge_expires_dir_configs, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server configs */ expires_cmds, /* command apr_table_t */ register_hooks /* register hooks */ };
Finally, the hook registration function and module structure link everything together.