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.
Copyright © 2003 O'Reilly & Associates. All rights reserved.