21.3. The Module Structure
Now
we will look in detail at each entry in the module
structure. We examine the entries in the order in which they are
used, which is not the order in which they appear in the structure,
and we also show how they are used in the standard Apache modules. We
will also note the differences between versions 1.3 and 2.0 of Apache
as we go along.
| Create Per-Server Config Structure | |
void *module_create_svr_config(pool *pPool, server_rec *pServer)
This structure creates the per-server
configuration structure for the module. It is called once for the
main server and once per virtual host. It allocates and initializes
the memory for the per-server configuration and returns a pointer to
it. pServer points to the
server_rec for the current server. See Example 21-1 (1.3) for an excerpt from
mod_cgi.c.
Example
Example 21-1. mod_cgi.c
#define DEFAULT_LOGBYTES 10385760
#define DEFAULT_BUFBYTES 1024
typedef struct {
char *logname;
long logbytes;
int bufbytes;
} cgi_server_conf;
static void *create_cgi_config(pool *p, server_rec *s)
{
cgi_server_conf *c =
(cgi_server_conf *) ap_pcalloc(p, sizeof(cgi_server_conf));
c->logname = NULL;
c->logbytes = DEFAULT_LOGBYTES;
c->bufbytes = DEFAULT_BUFBYTES;
return c;
}
All this code does is allocate and initialize a copy of
cgi_server_conf, which gets filled in during
configuration.
The only changes for 2.0 in this are that pool
becomes apr_pool_t and ap_pcalloc(
) becomes apr_pcalloc( ).
| Create Per-Directory Config Structure | |
void *module_create_dir_config(pool *pPool,char *szDir)
This structure is called once per
module, with szDir set to NULL,
when the main host's configuration is initialized
and again for each <Directory>,
<Location>, or
<File> section in the Config files
containing a directive from this module, with
szPath set to the directory. Any per-directory
directives found outside <Directory>,
<Location>, or
<File> sections end up in the
NULL configuration. It is also called when
.htaccess files are parsed, with the name of the
directory in which they reside. Because this function is used for
.htaccess files, it may also be called after the
initializer is called. Also, the core caches per-directory
configurations arising from .htaccess files for
the duration of a request, so this function is called only once per
directory with an .htaccess file.
If a module does not support per-directory configuration, any
directives that appear in a <Directory>
section override the per-server configuration unless precautions are
taken. The usual way to avoid this is to set the req
_overrides member appropriately in the command
table — see later in this section.
The purpose of this function is to allocate and initialize the memory
required for any per-directory configuration. It returns a pointer to
the allocated memory. See Example 21-2 (1.3) for an
excerpt from mod_rewrite.c.
Example
Example 21-2. mod_rewrite.c
static void *config_perdir_create(pool *p, char *path)
{
rewrite_perdir_conf *a;
a = (rewrite_perdir_conf *)ap_pcalloc(p, sizeof(rewrite_perdir_conf));
a->state = ENGINE_DISABLED;
a->options = OPTION_NONE;
a->baseurl = NULL;
a->rewriteconds = ap_make_array(p, 2, sizeof(rewritecond_entry));
a->rewriterules = ap_make_array(p, 2, sizeof(rewriterule_entry));
if (path == NULL) {
a->directory = NULL;
}
else {
/* make sure it has a trailing slash */
if (path[strlen(path)-1] == '/') {
a->directory = ap_pstrdup(p, path);
}
else {
a->directory = ap_pstrcat(p, path, "/", NULL);
}
}
return (void *)a;
}
This function allocates memory for a rewrite_
perdir_conf structure (defined elsewhere in
mod_rewrite.c) and initializes it. Since this
function is called for every <Directory>
section, regardless of whether it contains any rewriting directives,
the initialization makes sure the engine is disabled unless
specifically enabled later.
The only changes for 2.0 in this are that pool
becomes apr_pool_t and ap_pcalloc(
) becomes apr_pcalloc( ).
int module_pre_config(apr_pool_t *pconf,apr_pool_t *plog,apr_pool_t *ptemp)
This is nominally
called
before configuration starts, though in practice the directory and
server creators are first called once each (for the default server
and directory). A typical use of this function is, naturally enough,
for initialization. Example 21-3 shows what
mod_headers.c uses to initialize a hash.
Example
Example 21-3. mod_headers.c
static void register_format_tag_handler(apr_pool_t *p, char *tag,
void *tag_handler, int def)
{
const void *h = apr_palloc(p, sizeof(h));
h = tag_handler;
apr_hash_set(format_tag_hash, tag, 1, h);
}
static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
format_tag_hash = apr_hash_make(p);
register_format_tag_handler(p, "D", (void*) header_request_duration, 0);
register_format_tag_handler(p, "t", (void*) header_request_time, 0);
register_format_tag_handler(p, "e", (void*) header_request_env_var, 0);
return OK;
}
void *module_merge_server(pool *pPool, void *base_conf, void *new_conf)
Once the Config
files
have been read, this function is called once for each virtual host,
with base_conf pointing to the main
server's configuration (for this module) and
new_conf pointing to the virtual
host's configuration. This gives you the opportunity
to inherit any unset options in the virtual host from the main server
or to merge the main server's entries into the
virtual server, if appropriate. It returns a pointer to the new
configuration structure for the virtual host (or it just returns
new_conf, if appropriate).
It is possible that future changes to Apache will allow merging of
hosts other than the main one, so don't rely on
base_conf pointing to the main server. See Example 21-4 (1.3) for an excerpt from
mod_cgi.c.
Example
Example 21-4. mod_cgi.c
static void *merge_cgi_config(pool *p, void *basev, void *overridesv)
{
cgi_server_conf *base = (cgi_server_conf *) basev, *overrides = (cgi_server_conf *)
overridesv;
return overrides->logname ? overrides : base;
}
Although this example is exceedingly trivial, a per-server merger
can, in principle, do anything a per-directory merger
does — it's just that in most cases it makes
more sense to do things per-directory, so the interesting examples
can be found there. This example does serve to illustrate a point of
confusion — often the overriding configuration is called
overrides (or some variant thereof), which to our
ears implies the exact opposite precedence to that desired.
Again, the only change in 2.0 is that pool has
become apr_pool_t.
void *module_dir_merge(pool *pPool, void *base_conf, void *new_conf)
Like the per-server merger, this
is called once for each virtual host (not for each directory). It is
handed the per-server document root per-directory Config (that is,
the one that was created with a NULL directory
name).
Whenever a request is processed, this function merges all relevant
<Directory> sections and then merges
.htacess files (interleaved, starting at the
root and working downward), then <File> and
<Location> sections, in that order.
Unlike the per-server merger, per-directory merger is called as the
server runs, possibly with different combinations of directory,
location, and file configurations for each request, so it is
important that it copies the configuration (in
new_conf) if it is going to change it.
Now the reason we chose mod_rewrite.c for the
per-directory creator becomes apparent, as it is a little more
interesting than most. See Example 21-5.
Example
Example 21-5. mod_rewrite.c
static void *config_perdir_merge(pool *p, void *basev, void *overridesv)
{
rewrite_perdir_conf *a, *base, *overrides;
a = (rewrite_perdir_conf *)pcalloc(p, sizeof(rewrite_perdir_conf));
base = (rewrite_perdir_conf *)basev;
overrides = (rewrite_perdir_conf *)overridesv;
a->state = overrides->state;
a->options = overrides->options;
a->directory = overrides->directory;
a->baseurl = overrides->baseurl;
if (a->options & OPTION_INHERIT) {
a->rewriteconds = append_arrays(p, overrides->rewriteconds,
base->rewriteconds);
a->rewriterules = append_arrays(p, overrides->rewriterules,
base->rewriterules);
}
else {
a->rewriteconds = overrides->rewriteconds;
a->rewriterules = overrides->rewriterules;
}
return (void *)a;
}
As you can see, this merges the configuration from the base
conditionally, depending on whether the new configuration specified
an INHERIT option.
Once more, the only change in 2.0 is that pool has
become apr_pool_t. See Example 21-6 for an excerpt from
mod_env.c.
Example 21-6. mod_env.c
static void *merge_env_dir_configs(pool *p, void *basev, void *addv)
{
env_dir_config_rec *base = (env_dir_config_rec *) basev;
env_dir_config_rec *add = (env_dir_config_rec *) addv;
env_dir_config_rec *new =
(env_dir_config_rec *) ap_palloc(p, sizeof(env_dir_config_rec));
table *new_table;
table_entry *elts;
array_header *arr;
int i;
const char *uenv, *unset;
new_table = ap_copy_table(p, base->vars);
arr = ap_table_elts(add->vars);
elts = (table_entry *)arr->elts;
for (i = 0; i < arr->nelts; ++i) {
ap_table_setn(new_table, elts[i].key, elts[i].val);
}
unset = add->unsetenv;
uenv = ap_getword_conf(p, &unset);
while (uenv[0] != '\0') {
ap_table_unset(new_table, uenv);
uenv = ap_getword_conf(p, &unset);
}
new->vars = new_table;
new->vars_present = base->vars_present || add->vars_present;
return new;
}
This function creates a new configuration into which it then copies
the base vars table (a table of
environment variable names and values). It then runs through the
individual entries of the addv
vars table, setting them in the new table. It does
this rather than use overlay_tables( ) because
overlay_tables( ) does not deal with duplicated
keys. Then the addv
configuration's unsetenv (which
is a space-separated list of environment variables to unset) unsets
any variables specified to be unset for addv
's server.
The 2.0 version of this function has a number of alterations, but on
close inspection is actually very much the same, allowing for
differences in function names and some rather radical restructuring:
static void *merge_env_dir_configs(apr_pool_t *p, void *basev, void *addv)
{
env_dir_config_rec *base = basev;
env_dir_config_rec *add = addv;
env_dir_config_rec *res = apr_palloc(p, sizeof(*res));
const apr_table_entry_t *elts;
const apr_array_header_t *arr;
int i;
res->vars = apr_table_copy(p, base->vars);
res->unsetenv = NULL;
arr = apr_table_elts(add->unsetenv);
elts = (const apr_table_entry_t *)arr->elts;
for (i = 0; i < arr->nelts; ++i) {
apr_table_unset(res->vars, elts[i].key);
}
arr = apr_table_elts(add->vars);
elts = (const apr_table_entry_t *)arr->elts;
for (i = 0; i < arr->nelts; ++i) {
apr_table_setn(res->vars, elts[i].key, elts[i].val);
}
return res;
}
command_rec aCommands[]
This structure points to an array of
directives that configure the module. Each entry names a directive,
specifies a function that will handle the command, and specifies
which AllowOverride directives must be in force
for the command to be permitted. Each entry then specifies how the
directive's arguments are to be parsed and supplies
an error message in case of syntax errors (such as the wrong number
of arguments, or a directive used where it shouldn't
be).
The definition of
command_rec can be found in
http_config.h:
typedef struct command_struct {
const char *name; /* Name of this command */
const char *(*func)( ); /* Function invoked */
void *cmd_data; /* Extra data, for functions that
* implement multiple commands...
*/
int req_override; /* What overrides need to be allowed to
* enable this command
*/
enum cmd_how args_how; /* What the command expects as arguments */
const char *errmsg; /* 'usage' message, in case of syntax errors */
} command_rec;
Note that in 2.0 this definition is still broadly correct, but
there's also a variant for compilers that allow
designated initializers to permit the type-safe initialization of
command_rec s.
cmd_how is defined as
follows:
enum cmd_how {
RAW_ARGS, /* cmd_func parses command line itself */
TAKE1, /* one argument only */
TAKE2, /* two arguments only */
ITERATE, /* one argument, occurring multiple times
* (e.g., IndexIgnore)
*/
ITERATE2, /* two arguments, 2nd occurs multiple times
* (e.g., AddIcon)
*/
FLAG, /* One of 'On' or 'Off' */
NO_ARGS, /* No args at all, e.g. </Directory> */
TAKE12, /* one or two arguments */
TAKE3, /* three arguments only */
TAKE23, /* two or three arguments */
TAKE123, /* one, two, or three arguments */
TAKE13 /* one or three arguments */
};
These options determine how the function func is
called when the matching directive is found in a Config file, but
first we must look at one more structure,
cmd_parms:
typedef struct {
void *info; /* Argument to command from cmd_table */
int override; /* Which allow-override bits are set */
int limited; /* Which methods are <Limit>ed */
configfile_t *config_file; /* Config file structure from pcfg_openfile( ) */
ap_pool *pool; /* Pool to allocate new storage in */
struct pool *temp_pool; /* Pool for scratch memory; persists during
* configuration, but wiped before the first
* request is served...
*/
server_rec *server; /* Server_rec being configured for */
char *path; /* If configuring for a directory,
* pathname of that directory.
* NOPE! That's what it meant previous to the
* existance of <Files>, <Location> and regex
* matching. Now the only usefulness that can
* be derived from this field is whether a command
* is being called in a server context (path == NULL)
* or being called in a dir context (path != NULL).
*/
const command_rec *cmd; /* configuration command */
const char *end_token; /* end token required to end a nested section */
void *context; /* per_dir_config vector passed
* to handle_command */
} cmd_parms;
This structure is filled in and passed to the function associated
with each directive. Note that cmd_parms.info is
filled in with the value of command_rec.cmd_data,
allowing arbitrary extra information to be passed to the function.
The function is also passed its per-directory configuration
structure, if there is one, shown in the following function
definitions as mconfig. The per-server
configuration can be accessed by a call similar to:
ap_get_module_config(parms->server->module_config, &module_struct)
replacing module_struct with your own
module's module structure. Extra
information may also be passed, depending on the value of
args_how :
- RAW_ARGS
-
func(cmd_parms *parms, void *mconfig, char *args)
args is simply the rest of the line (that is,
excluding the directive).
- NO_ARGS
-
func(cmd_parms *parms, void *mconfig)
- TAKE1
-
func(cmd_parms *parms, void *mconfig, char *w)
w is the single argument to the directive.
- TAKE2, TAKE12
-
func(cmd_parms *parms, void *mconfig, char *w1, char
*w2)
w1 and w2 are the two arguments
to the directive. TAKE12 means the second argument
is optional. If absent, w2 is
NULL.
- TAKE3, TAKE13, TAKE23, TAKE123
-
func(cmd_parms *parms, void *mconfig, char *w1, char *w2,
char *w3)
w1, w2, and
w3 are the three arguments to the directive.
TAKE13, TAKE23, and
TAKE123 mean that the directive takes one or
three, two or three, and one, two, or three arguments, respectively.
Missing arguments are NULL.
- ITERATE
-
func(cmd_parms *parms, void *mconfig, char *w)
func is called repeatedly, once for each argument
following the directive.
- ITERATE2
-
func(cmd_parms *parms, void *mconfig, char *w1, char
*w2)
There must be at least two arguments. func is
called once for each argument, starting with the second. The first is
passed to func every time.
- FLAG
-
func(cmd_parms *parms, void *mconfig, int f)
The argument must be either On or
Off. If On, then
f is nonzero; if Off,
f is zero.
In 2.0 each of the previous has its own macro to define it, to allow
for type-safe initialization where supported by the compiler entries.
So instead of directly using the flag ITERATE, for
example, you would instead use the macro
AP_INIT_ITERATE to fill in the
command_rec structure.
req_override can be any combination of the
following (OR ed together):
#define OR_NONE 0
#define OR_LIMIT 1
#define OR_OPTIONS 2
#define OR_FILEINFO 4
#define OR_AUTHCFG 8
#define OR_INDEXES 16
#define OR_UNSET 32
#define ACCESS_CONF 64
#define RSRC_CONF 128
#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES)
2.0 adds one extra option: #define EXEC_ON_READ 256 /**< force directive to execute a command
which would modify the configuration (like including
another file, or IFModule */
This flag defines the circumstances under which a directive is
permitted. The logical AND of this field and the
current override state must be nonzero for the directive to be
allowed. In configuration files, the current override state is:
RSRC_CONF|OR_OPTIONS|OR_FILEINFO|OR_INDEXES
when outside a <Directory> section, and it
is:
ACCESS_CONF|OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES
when inside a <Directory> section. In .htaccess files, the state is determined by
the AllowOverride directive. See Example 21-7 (1.3) for an excerpt from
mod_mime.c.
Example
Example 21-7. mod_mime.c
static const command_rec mime_cmds[] =
{
{"AddType", add_type, NULL, OR_FILEINFO, ITERATE2,
"a mime type followed by one or more file extensions"},
{"AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2,
"an encoding (e.g., gzip), followed by one or more file extensions"},
{"AddCharset", add_charset, NULL, OR_FILEINFO, ITERATE2,
"a charset (e.g., iso-2022-jp), followed by one or more file extensions"},
{"AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2,
"a language (e.g., fr), followed by one or more file extensions"},
{"AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2,
"a handler name followed by one or more file extensions"},
{"ForceType", ap_set_string_slot_lower,
(void *)XtOffsetOf(mime_dir_config, type), OR_FILEINFO, TAKE1,
"a media type"},
{"RemoveHandler", remove_handler, NULL, OR_FILEINFO, ITERATE,
"one or more file extensions"},
{"RemoveEncoding", remove_encoding, NULL, OR_FILEINFO, ITERATE,
"one or more file extensions"},
{"RemoveType", remove_type, NULL, OR_FILEINFO, ITERATE,
"one or more file extensions"},
{"SetHandler", ap_set_string_slot_lower,
(void *)XtOffsetOf(mime_dir_config, handler), OR_FILEINFO, TAKE1,
"a handler name"},
{"TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1,
"the MIME types config file"},
{"DefaultLanguage", ap_set_string_slot,
(void*)XtOffsetOf(mime_dir_config, default_language), OR_FILEINFO, TAKE1,
"language to use for documents with no other language file extension" },
{NULL}
};
Note the use of set_string_slot( ). This
standard function uses the offset defined in
cmd_data, using XtOffsetOf to
set a char* in the per-directory configuration of
the module. See Example 21-8 (2.0)
for an excerpt from mod_mime.c.
Example 21-8. mod_mime.c
static const command_rec mime_cmds[] =
{
AP_INIT_ITERATE2("AddCharset", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, charset_type), OR_FILEINFO,
"a charset (e.g., iso-2022-jp), followed by one or more file extensions"),
AP_INIT_ITERATE2("AddEncoding", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, encoding_type), OR_FILEINFO,
"an encoding (e.g., gzip), followed by one or more file extensions"),
AP_INIT_ITERATE2("AddHandler", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, handler), OR_FILEINFO,
"a handler name followed by one or more file extensions"),
AP_INIT_ITERATE2("AddInputFilter", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, input_filters), OR_FILEINFO,
"input filter name (or ; delimited names) followed by one or more file extensions"),
AP_INIT_ITERATE2("AddLanguage", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, language_type), OR_FILEINFO,
"a language (e.g., fr), followed by one or more file extensions"),
AP_INIT_ITERATE2("AddOutputFilter", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, output_filters), OR_FILEINFO,
"output filter name (or ; delimited names) followed by one or more file extensions"),
AP_INIT_ITERATE2("AddType", add_extension_info,
(void *)APR_XtOffsetOf(extension_info, forced_type), OR_FILEINFO,
"a mime type followed by one or more file extensions"),
AP_INIT_TAKE1("DefaultLanguage", ap_set_string_slot,
(void*)APR_XtOffsetOf(mime_dir_config, default_language), OR_FILEINFO,
"language to use for documents with no other language file extension"),
AP_INIT_ITERATE("MultiviewsMatch", multiviews_match, NULL, OR_FILEINFO,
"NegotiatedOnly (default), Handlers and/or Filters, or Any"),
AP_INIT_ITERATE("RemoveCharset", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, charset_type), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_ITERATE("RemoveEncoding", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, encoding_type), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_ITERATE("RemoveHandler", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, handler), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_ITERATE("RemoveInputFilter", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, input_filters), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_ITERATE("RemoveLanguage", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, language_type), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_ITERATE("RemoveOutputFilter", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, output_filters), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_ITERATE("RemoveType", remove_extension_info,
(void *)APR_XtOffsetOf(extension_info, forced_type), OR_FILEINFO,
"one or more file extensions"),
AP_INIT_TAKE1("TypesConfig", set_types_config, NULL, RSRC_CONF,
"the MIME types config file"),
{NULL}
};
As you can see, this uses the macros to initialize the structure.
Also note that set_string_slot( ) has become
ap_set_string_slot( ).
void module_init(server_rec *pServer, pool *pPool) [1.3]
int module_post_config(apr_pool_t *pPool, apr_pool_t *pLog, apr_pool_t *pTemp,
server_rec *pServer) [2.0]
In 1.3 this is the init hook, but in 2.0 it has
been renamed, more accurately, to post_config.
In 2.0 the three pools provided are, in order,
pPool, a pool that lasts until the configuration
is changed, corresponding to pPool in 1.3;
pLog, a pool that is cleared after each read of
the configuration file (remembering it is read twice for each
reconfiguration) intended for log files; and
ptemp, a temporary pool that is cleared after
configuration is complete (and perhaps more often than that).
This function
is called after the server configuration files have been read but
before any requests are handled. Like the configuration functions, it
is called each time the server is reconfigured, so care must be taken
to make sure it behaves correctly on the second and subsequent calls.
This is the last function to be called before Apache forks the
request-handling children. pServer is a pointer to
the server_rec for the main host.
pPool is a pool that persists
until the server is reconfigured. Note that, at least in the current
version of Apache:
pServer->server_hostname
may not yet be initialized. If the module is going to add to the
version string with ap_add_version_component( ),
then this is a good place to do it.
It is possible to iterate through all the server configurations by
following the next member of
pServer, as in the following:
for( ; pServer ; pServer=pServer->next)
;
See Example 21-9 (1.3) for an excerpt from
mod_mime.c.
Example
Example 21-9. mod_mime.c
#define MIME_HASHSIZE (32)
#define hash(i) (ap_tolower(i) % MIME_HASHSIZE)
static table *hash_buckets[MIME_HASHSIZE];
static void init_mime(server_rec *s, pool *p)
{
configfile_t *f;
char l[MAX_STRING_LEN];
int x;
char *types_confname = ap_get_module_config(s->module_config, &mime_module);
if (!types_confname)
types_confname = TYPES_CONFIG_FILE;
types_confname = ap_server_root_relative(p, types_confname);
if (!(f = ap_pcfg_openfile(p, types_confname))) {
ap_log_error(APLOG_MARK, APLOG_ERR, s,
"could not open mime types log file %s.", types_confname);
exit(1);
}
for (x = 0; x < MIME_HASHSIZE; x++)
hash_buckets[x] = ap_make_table(p, 10);
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
const char *ll = l, *ct;
if (l[0] == '#')
continue;
ct = ap_getword_conf(p, &ll);
while (ll[0]) {
char *ext = ap_getword_conf(p, &ll);
ap_str_tolower(ext); /* ??? */
ap_table_setn(hash_buckets[hash(ext[0])], ext, ct);
}
}
ap_cfg_closefile(f);
}
The same function in mod_mime.c uses a hash
provided by APR instead of building its own, as shown in Example 21-10 (2.0).
Example 21-10. mod_mime.c
static apr_hash_t *mime_type_extensions;
static int mime_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
ap_configfile_t *f;
char l[MAX_STRING_LEN];
const char *types_confname = ap_get_module_config(s->module_config, &mime_module);
apr_status_t status;
if (!types_confname)
types_confname = AP_TYPES_CONFIG_FILE;
types_confname = ap_server_root_relative(p, types_confname);
if ((status = ap_pcfg_openfile(&f, ptemp, types_confname)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
"could not open mime types config file %s.", types_confname);
return HTTP_INTERNAL_SERVER_ERROR;
}
mime_type_extensions = apr_hash_make(p);
while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
const char *ll = l, *ct;
if (l[0] == '#')
continue;
ct = ap_getword_conf(p, &ll);
while (ll[0]) {
char *ext = ap_getword_conf(p, &ll);
ap_str_tolower(ext); /* ??? */
apr_hash_set(mime_type_extensions, ext, APR_HASH_KEY_STRING, ct);
}
}
ap_cfg_closefile(f);
return OK;
}
static void
module_child_init(server_rec *pServer,pool *pPool)
An Apache server may consist of many processes (on Unix, for example)
or a single process with many threads (on Win32) or, in the future, a
combination of the two. module_child_init( ) is
called once for each instance of a heavyweight process, that is,
whatever level of execution corresponds to a separate address space,
file handles, etc. In the case of Unix, this is once per child
process, but on Win32 it is called only once in total,
not once per thread. This is because threads
share address space and other resources. There is not currently a
corresponding per-thread call, but there may be in the future. There
is a corresponding call for child exit, described later in this
chapter.
See Example 21-11 (1.3) for an excerpt from
mod_unique_id.c.
Example
Example 21-11. mod_unique_id.c
static void unique_id_child_init(server_rec *s, pool *p)
{
pid_t pid;
#ifndef NO_GETTIMEOFDAY
struct timeval tv;
#endif
pid = getpid( );
cur_unique_id.pid = pid;
if (cur_unique_id.pid != pid) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, s,
"oh no! pids are greater than 32-bits! I'm broken!");
}
cur_unique_id.in_addr = global_in_addr;
#ifndef NO_GETTIMEOFDAY
if (gettimeofday(&tv, NULL) == -1) {
cur_unique_id.counter = 0;
}
else {
cur_unique_id.counter = tv.tv_usec / 10;
}
#else
cur_unique_id.counter = 0;
#endif
cur_unique_id.pid = htonl(cur_unique_id.pid);
cur_unique_id.counter = htons(cur_unique_id.counter);
}
mod_unique_id.c 's purpose in
life is to provide an ID for each request that is unique across all
web servers everywhere (or, at least at a particular site). To do
this, it uses various bits of uniqueness, including the process ID of
the child and the time at which it was forked, which is why it uses
this hook.
The same function in 2.0 is a little simpler, because APR takes away
the platform dependencies:
static void unique_id_child_init(apr_pool_t *p, server_rec *s)
{
pid_t pid;
apr_time_t tv;
pid = getpid( );
cur_unique_id.pid = pid;
if ((pid_t)cur_unique_id.pid != pid) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, 0, s,
"oh no! pids are greater than 32-bits! I'm broken!");
}
cur_unique_id.in_addr = global_in_addr;
tv = apr_time_now( );
cur_unique_id.counter = (unsigned short)(tv % APR_USEC_PER_SEC / 10);
cur_unique_id.pid = htonl(cur_unique_id.pid);
cur_unique_id.counter = htons(cur_unique_id.counter);
}
static int module_post_read_request(request_rec *pReq)
This function is called immediately after the request headers have
been read or, in the case of an internal redirect, synthesized. It is
not called for subrequests. It can return OK,
DECLINED, or a status code. If something other
than DECLINED is returned, no further modules are
called. This can be used to make decisions based purely on the header
content. Currently, the only standard Apache module to use this hook
is the proxy module.
See Example 21-12 for an excerpt from
mod_proxy.c.
Example
Example 21-12. mod_proxy.c
static int proxy_detect(request_rec *r)
{
void *sconf = r->server->module_config;
proxy_server_conf *conf;
conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
if (conf->req && r->parsed_uri.scheme) {
/* but it might be something vhosted */
if (!(r->parsed_uri.hostname
&& !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
&& ap_matches_request_vhost(r, r->parsed_uri.hostname,
r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
r->proxyreq = STD_PROXY;
r->uri = r->unparsed_uri;
r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
}
}
/* We need special treatment for CONNECT proxying: it has no scheme part */
else if (conf->req && r->method_number == M_CONNECT
&& r->parsed_uri.hostname
&& r->parsed_uri.port_str) {
r->proxyreq = STD_PROXY;
r->uri = r->unparsed_uri;
r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
r->handler = "proxy-server";
}
return DECLINED;
}
This code checks for a request that includes a hostname that does
not match the current virtual host (which, since
it will have been chosen on the basis of the hostname in the request,
means it doesn't match any virtual host) or a
CONNECT method (which only proxies use). If either
of these conditions are true, the handler is set to
proxy-server, and the filename is set to
proxy:uri so that the
later phases will be handled by the proxy module.
Apart from minor differences in naming of constants, this function is
identical in 2.0.
int module_quick_handler(request_rec *r, int lookup_uri)
This function is intended to
provide
content from a URI-based cache. If lookup_uri is
set, then it should simply return OK if the URI
exists, but not provide the content.
The only example of this in 2.0 is in an experimental module,
mod_cache.c, as shown in Example 21-13.
Example
Example 21-13. mod_cache.c
static int cache_url_handler(request_rec *r, int lookup)
{
apr_status_t rv;
const char *cc_in, *pragma, *auth;
apr_uri_t uri = r->parsed_uri;
char *url = r->unparsed_uri;
apr_size_t urllen;
char *path = uri.path;
const char *types;
cache_info *info = NULL;
cache_request_rec *cache;
cache_server_conf *conf =
(cache_server_conf *) ap_get_module_config(r->server->module_config,
&cache_module);
if (r->method_number != M_GET) return DECLINED;
if (!(types = ap_cache_get_cachetype(r, conf, path))) {
return DECLINED;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"cache: URL %s is being handled by %s", path, types);
urllen = strlen(url);
if (urllen > MAX_URL_LENGTH) {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"cache: URL exceeds length threshold: %s", url);
return DECLINED;
}
if (url[urllen-1] == '/') {
return DECLINED;
}
cache = (cache_request_rec *) ap_get_module_config(r->request_config,
&cache_module);
if (!cache) {
cache = ap_pcalloc(r->pool, sizeof(cache_request_rec));
ap_set_module_config(r->request_config, &cache_module, cache);
}
cache->types = types;
cc_in = apr_table_get(r->headers_in, "Cache-Control");
pragma = apr_table_get(r->headers_in, "Pragma");
auth = apr_table_get(r->headers_in, "Authorization");
if (conf->ignorecachecontrol_set == 1 && conf->ignorecachecontrol == 1 &&
auth == NULL) {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"incoming request is asking for a uncached version of %s,
but we know better and are ignoring it", url);
}
else {
if (ap_cache_liststr(cc_in, "no-store", NULL) ||
ap_cache_liststr(pragma, "no-cache", NULL) || (auth != NULL)) {
/* delete the previously cached file */
cache_remove_url(r, cache->types, url);
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"cache: no-store forbids caching of %s", url);
return DECLINED;
}
}
rv = cache_select_url(r, cache->types, url);
if (DECLINED == rv) {
if (!lookup) {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"cache: no cache - add cache_in filter and DECLINE");
ap_add_output_filter("CACHE_IN", NULL, r, r->connection);
}
return DECLINED;
}
else if (OK == rv) {
if (cache->fresh) {
apr_bucket_brigade *out;
conn_rec *c = r->connection;
if (lookup) {
return OK;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"cache: fresh cache - add cache_out filter and "
"handle request");
ap_run_insert_filter(r);
ap_add_output_filter("CACHE_OUT", NULL, r, r->connection);
out = apr_brigade_create(r->pool, c->bucket_alloc);
if (APR_SUCCESS != (rv = ap_pass_brigade(r->output_filters, out))) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
"cache: error returned while trying to return %s "
"cached data",
cache->type);
return rv;
}
return OK;
}
else {
if (lookup) {
return DECLINED;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, r->server,
"cache: stale cache - test conditional");
if (ap_cache_request_is_conditional(r)) {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0,
r->server,
"cache: conditional - add cache_in filter and "
"DECLINE");
ap_add_output_filter("CACHE_IN", NULL, r, r->connection);
return DECLINED;
}
else {
if (info && info->etag) {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0,
r->server,
"cache: nonconditional - fudge conditional "
"by etag");
apr_table_set(r->headers_in, "If-None-Match", info->etag);
}
else if (info && info->lastmods) {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0,
r->server,
"cache: nonconditional - fudge conditional "
"by lastmod");
apr_table_set(r->headers_in,
"If-Modified-Since",
info->lastmods);
}
else {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0,
r->server,
"cache: nonconditional - no cached "
"etag/lastmods - add cache_in and DECLINE");
ap_add_output_filter("CACHE_IN", NULL, r, r->connection);
return DECLINED;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0,
r->server,
"cache: nonconditional - add cache_conditional and"
" DECLINE");
ap_add_output_filter("CACHE_CONDITIONAL",
NULL,
r,
r->connection);
return DECLINED;
}
}
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR, rv,
r->server,
"cache: error returned while checking for cached file by "
"%s cache",
cache->type);
return DECLINED;
}
}
This is quite complex, but interesting — note the use of filters
both to fill the cache and to generate the cached content for cache
hits.
int module_translate(request_rec *pReq)
This function's task is to
translate the URL in a request into a filename. The end result of its
deliberations should be placed in
pReq->filename. It should return
OK, DECLINED, or a status code.
The first module that doesn't return
DECLINED is assumed to have done the job, and no
further modules are called. Since the order in which modules are
called is not defined, it is a good thing if the URLs handled by the
modules are mutually exclusive. If all modules return
DECLINED, a configuration error has occurred.
Obviously, the function is likely to use the per-directory and
per-server configurations (but note that at this stage, the
per-directory configuration refers to the root configuration of the
current server) to determine whether it should handle the request, as
well as the URL itself (in pReq->uri). If a
status is returned, the appropriate headers for the response should
also be set in pReq->headers_out.
Naturally enough, Example 21-14 (1.3 and 2.0) comes
from mod_alias.c:
Example
Example 21-14. mod_alias.c
static char *try_alias_list(request_rec *r, array_header *aliases, int doesc, int *status)
{
alias_entry *entries = (alias_entry *) aliases->elts;
regmatch_t regm[10];
char *found = NULL;
int i;
for (i = 0; i < aliases->nelts; ++i) {
alias_entry *p = &entries[i];
int l;
if (p->regexp) {
if (!ap_regexec(p->regexp, r->uri, p->regexp->re_nsub + 1, regm, 0)) {
if (p->real) {
found = ap_pregsub(r->pool, p->real, r->uri,
p->regexp->re_nsub + 1, regm);
if (found && doesc) {
found = ap_escape_uri(r->pool, found);
}
}
else {
/* need something non-null */
found = ap_pstrdup(r->pool, "");
}
}
}
else {
l = alias_matches(r->uri, p->fake);
if (l > 0) {
if (doesc) {
char *escurl;
escurl = ap_os_escape_path(r->pool, r->uri + l, 1);
found = ap_pstrcat(r->pool, p->real, escurl, NULL);
}
else
found = ap_pstrcat(r->pool, p->real, r->uri + l, NULL);
}
}
if (found) {
if (p->handler) { /* Set handler, and leave a note for mod_cgi */
r->handler = p->handler;
ap_table_setn(r->notes, "alias-forced-type", r->handler);
}
*status = p->redir_status;
return found;
}
}
return NULL;
}
static int translate_alias_redir(request_rec *r)
{
void *sconf = r->server->module_config;
alias_server_conf *serverconf =
(alias_server_conf *) ap_get_module_config(sconf, &alias_module);
char *ret;
int status;
if (r->uri[0] != '/' && r->uri[0] != '\0')
return DECLINED;
if ((ret = try_alias_list(r, serverconf->redirects, 1, &status)) != NULL) {
if (ap_is_HTTP_REDIRECT(status)) {
/* include QUERY_STRING if any */
if (r->args) {
ret = ap_pstrcat(r->pool, ret, "?", r->args, NULL);
}
ap_table_setn(r->headers_out, "Location", ret);
}
return status;
}
if ((ret = try_alias_list(r, serverconf->aliases, 0, &status)) != NULL) {
r->filename = ret;
return OK;
}
return DECLINED;
}
First of all, this example tries to match a
Redirect directive. If it does, the
Location header is set in
headers_out, and REDIRECT is
returned. If not, it translates into a filename. Note that it may
also set a handler (in fact, the only handler it can possibly set is
cgi-script, which it does if the alias was
created by a ScriptAlias directive). An
interesting feature is that it sets a note for
mod_cgi.c, namely
alias-forced-type. This is used by
mod_cgi.c to determine whether the CGI script is
invoked via a ScriptAlias, in which case
Options ExecCGI is not
needed.[78] For completeness, here is the code from
mod_cgi.c that makes the test:
int is_scriptaliased (request_rec *r)
{
char *t = table_get (r->notes, "alias-forced-type");
return t && (!strcmp (t, "cgi-script"));
}
An Interjection
At this point, the filename is known as well as the URL, and Apache
reconfigures itself to hand subsequent module functions the relevant
per-directory configuration (actually composed of all matching
directory, location, and file configurations, merged with each other
via the per-directory merger, in that order).[79]
int module_map_to_storage(request_rec *r)
This function allows
modules
to set the request_rec's
per_dir_config according to their own view of the
world, if desired. It is also used to respond to contextless requests
(such as TRACE). It should return
DONE or an HTTP return code if
a contextless request was fulfilled, OK if the
module mapped it, or DECLINED if not. The core
will handle this by doing a standard directory walk on the filename
if no other module does. See Example 21-15.
Example
Example 21-15. http_protocol.c
AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
{
int rv;
apr_bucket_brigade *b;
header_struct h;
if (r->method_number != M_TRACE) {
return DECLINED;
}
/* Get the original request */
while (r->prev) {
r = r->prev;
}
if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) {
return rv;
}
ap_set_content_type(r, "message/http");
/* Now we recreate the request, and echo it back */
b = apr_brigade_create(r->pool, r->connection->bucket_alloc);
apr_brigade_putstrs(b, NULL, NULL, r->the_request, CRLF, NULL);
h.pool = r->pool;
h.bb = b;
apr_table_do((int (*) (void *, const char *, const char *))
form_header_field, (void *) &h, r->headers_in, NULL);
apr_brigade_puts(b, NULL, NULL, CRLF);
ap_pass_brigade(r->output_filters, b);
return DONE;
}
This is the code that handles the TRACE method.
Also, the following is from mod_proxy.c:
static int proxy_map_location(request_rec *r)
{
int access_status;
if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
return DECLINED;
/* Don't let the core or mod_http map_to_storage hooks handle this,
* We don't need directory/file_walk, and we want to TRACE on our own.
*/
if ((access_status = proxy_walk(r))) {
ap_die(access_status, r);
return access_status;
}
return OK;
}
int module_header_parser(request_rec *pReq)
This routine is similar in intent to the
post_read_request phase. It can return
OK, DECLINED, or a status code.
If something other than DECLINED is returned, no
further modules are called. The intention was to make decisions based
on the headers sent by the client. However, its use has (in most
cases) been superseded by post_read_request. Since
it occurs after the per-directory configuration merge has been done,
it is useful in some cases.
The only standard module that uses it is
mod_setenvif.c, as shown in Example 21-16.
Example
Example 21-16. mod_setenvif.c
static int match_headers(request_rec *r)
{
sei_cfg_rec *sconf;
sei_entry *entries;
table_entry *elts;
const char *val;
int i, j;
int perdir;
char *last_name;
perdir = (ap_table_get(r->notes, SEI_MAGIC_HEIRLOOM) != NULL);
if (! perdir) {
ap_table_set(r->notes, SEI_MAGIC_HEIRLOOM, "post-read done");
sconf = (sei_cfg_rec *) ap_get_module_config(r->server->module_config,
&setenvif_module);
}
else {
sconf = (sei_cfg_rec *) ap_get_module_config(r->per_dir_config,
&setenvif_module);
}
entries = (sei_entry *) sconf->conditionals->elts;
last_name = NULL;
val = NULL;
for (i = 0; i < sconf->conditionals->nelts; ++i) {
sei_entry *b = &entries[i];
/* Optimize the case where a bunch of directives in a row use the
* same header. Remember we don't need to strcmp the two header
* names because we made sure the pointers were equal during
* configuration.
*/
if (b->name != last_name) {
last_name = b->name;
switch (b->special_type) {
case SPECIAL_REMOTE_ADDR:
val = r->connection->remote_ip;
break;
case SPECIAL_REMOTE_HOST:
val = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME);
break;
case SPECIAL_REMOTE_USER:
val = r->connection->user;
break;
case SPECIAL_REQUEST_URI:
val = r->uri;
break;
case SPECIAL_REQUEST_METHOD:
val = r->method;
break;
case SPECIAL_REQUEST_PROTOCOL:
val = r->protocol;
break;
case SPECIAL_NOT:
val = ap_table_get(r->headers_in, b->name);
if (val == NULL) {
val = ap_table_get(r->subprocess_env, b->name);
}
break;
}
}
/*
* A NULL value indicates that the header field or special entity
* wasn't present or is undefined. Represent that as an empty string
* so that REs like "^$" will work and allow envariable setting
* based on missing or empty field.
*/
if (val == NULL) {
val = "";
}
if (!ap_regexec(b->preg, val, 0, NULL, 0)) {
array_header *arr = ap_table_elts(b->features);
elts = (table_entry *) arr->elts;
for (j = 0; j < arr->nelts; ++j) {
if (!strcmp(elts[j].val, "!")) {
ap_table_unset(r->subprocess_env, elts[j].key);
}
else {
ap_table_setn(r->subprocess_env, elts[j].key, elts[j].val);
}
}
}
}
return DECLINED;
}
Interestingly, this module hooks both
post_read_request and
header_parser to the same function, so it can set
variables before and after the directory merge. (This is because
other modules often use the environment variables to control their
function.)
The function doesn't do anything particularly
fascinating, except a rather dubious use of the notes table in the
request record. It uses a note SEI_MAGIC_HEIRLOOM
to tell it whether it's in the
post_read_request or the
header_parser (by virtue of
post_read_request coming first); in our view it
should simply have hooked two different functions and passed a flag
instead. The rest of the function simply checks various fields in the
request to, and conditionally sets environment variables for,
subprocesses.
This function is virtually identical in both 1.3 and 2.0
int module_check_access(request_rec *pReq)
This routine checks access, in the allow/deny
sense. It can return OK ,
DECLINED, or a status code. All modules are called
until one of them returns something other than
DECLINED or OK. If all modules
return DECLINED, it is considered a configuration
error. At this point, the URL and the filename (if relevant) are
known, as are the client's address, user agent, and
so forth. All of these are available through pReq.
As long as everything says DECLINED or
OK, the request can proceed.
The only example available in the standard modules is,
unsurprisingly, from mod_access.c. See Example 21-17 for an excerpt from
mod_access.c.
Example
Example 21-17. mod_access.c
static int find_allowdeny(request_rec *r, array_header *a, int method)
{
allowdeny *ap = (allowdeny *) a->elts;
int mmask = (1 << method);
int i;
int gothost = 0;
const char *remotehost = NULL;
for (i = 0; i < a->nelts; ++i) {
if (!(mmask & ap[i].limited))
continue;
switch (ap[i].type) {
case T_ENV:
if (ap_table_get(r->subprocess_env, ap[i].x.from)) {
return 1;
}
break;
case T_ALL:
return 1;
case T_IP:
if (ap[i].x.ip.net != INADDR_NONE
&& (r->connection->remote_addr.sin_addr.s_addr
& ap[i].x.ip.mask) == ap[i].x.ip.net) {
return 1;
}
break;
case T_HOST:
if (!gothost) {
remotehost = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_DOUBLE_REV);
if ((remotehost == NULL) || is_ip(remotehost))
gothost = 1;
else
gothost = 2;
}
if ((gothost == 2) && in_domain(ap[i].x.from, remotehost))
return 1;
break;
case T_FAIL:
/* do nothing? */
break;
}
}
return 0;
}
static int check_dir_access(request_rec *r)
{
int method = r->method_number;
access_dir_conf *a =
(access_dir_conf *)
ap_get_module_config(r->per_dir_config, &access_module);
int ret = OK;
if (a->order[method] == ALLOW_THEN_DENY) {
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
}
else if (a->order[method] == DENY_THEN_ALLOW) {
if (find_allowdeny(r, a->denys, method))
ret = FORBIDDEN;
if (find_allowdeny(r, a->allows, method))
ret = OK;
}
else {
if (find_allowdeny(r, a->allows, method)
&& !find_allowdeny(r, a->denys, method))
ret = OK;
else
ret = FORBIDDEN;
}
if (ret == FORBIDDEN
&& (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client denied by server configuration: %s",
r->filename);
}
return ret;
}
Pretty straightforward stuff. in_ip( ) and
in_domain( ) check whether an IP address or domain
name, respectively, match the IP or domain of the client.
The only difference in 2.0 is that the return value
FORBIDDEN has become
HTTP_FORBIDDEN.
int module_check_user_id(request_rec *pReq)
This function is responsible for acquiring and
checking a user ID. The user ID should be stored in
pReq->connection->user. The function should
return OK, DECLINED, or a
status code. Of particular interest is
HTTP_UNAUTHORIZED (formerly known as
AUTH_REQUIRED), which should be returned if the
authorization fails (either because the user agent presented no
credentials or because those presented were not correct). All modules
are polled until one returns something other than
DECLINED. If all decline, a configuration error is
logged, and an error is returned to the user agent. When
HTTP_UNAUTHORIZED is returned, an appropriate
header should be set to inform the user agent of the type of
credentials to present when it retries. Currently, the appropriate
header is WWW-Authenticate (see the HTTP 1.1
specification for details). Unfortunately, Apache's
modularity is not quite as good as it might be in this area. So this
hook usually provides alternate ways of accessing the user/password
database, rather than changing the way authorization is actually
done, as evidenced by the fact that the protocol side of
authorization is currently dealt with in
http_protocol.c, rather than in the module. Note
that this function checks the validity of the username and password
and not whether the particular user has permission to access the URL.
An obvious user of this hook is mod_auth.c, as
shown in Example 21-18.
Example
Example 21-18. mod_auth.c
static int authenticate_basic_user(request_rec *r)
{
auth_config_rec *sec =
(auth_config_rec *) ap_get_module_config(r->per_dir_config, &auth_module);
conn_rec *c = r->connection;
const char *sent_pw;
char *real_pw;
char *invalid_pw;
int res;
if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
return res;
if (!sec->auth_pwfile)
return DECLINED;
if (!(real_pw = get_pw(r, c->user, sec->auth_pwfile))) {
if (!(sec->auth_authoritative))
return DECLINED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not found: %s", c->user, r->uri);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
invalid_pw = ap_validate_password(sent_pw, real_pw);
if (invalid_pw != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s: authentication failure for \"%s\": %s",
c->user, r->uri, invalid_pw);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
return OK;
}
This function is essentially the same for 2.0, except that
AUTH_REQUIRED has become
HTTP_UNAUTHORIZED.
int
module_check_auth(request_rec *pReq)
This hook is called to check whether the authenticated user (found in
pReq->connection->user) is permitted to
access the current URL. It normally uses the per-directory
configuration (remembering that this is actually the combined
directory, location, and file configuration) to determine this. It
must return OK, DECLINED, or a
status code. Again, the usual status to return is
HTTP_UNAUTHORIZED if access is denied, thus giving
the user a chance to present new credentials. Modules are polled
until one returns something other than DECLINED.
Again, the natural example to use is from
mod_auth.c, as shown in Example 21-19.
Example
Example 21-19. mod_auth.c
int check_user_access (request_rec *r) {
auth_config_rec *sec =
(auth_config_rec *)ap_get_module_config (r->per_dir_config, &auth_module);
char *user = r->connection->user;
int m = r->method_number;
int method_restricted = 0;
register int x;
char *t, *w;
table *grpstatus;
array_header *reqs_arr = requires (r);
require_line *reqs;
if (!reqs_arr)
return (OK);
reqs = (require_line *)reqs_arr->elts;
if(sec->auth_grpfile)
grpstatus = groups_for_user (r->pool, user, sec->auth_grpfile);
else
grpstatus = NULL;
for(x=0; x < reqs_arr->nelts; x++) {
if (! (reqs[x].method_mask & (1 << m))) continue;
method_restricted = 1;
t = reqs[x].requirement;
w = getword(r->pool, &t, ' ');
if(!strcmp(w,"valid-user"))
return OK;
if(!strcmp(w,"user")) {
while(t[0]) {
w = getword_conf (r->pool, &t);
if(!strcmp(user,w))
return OK;
}
}
else if(!strcmp(w,"group")) {
if(!grpstatus)
return DECLINED; /* DBM group? Something else? */
while(t[0]) {
w = getword_conf(r->pool, &t);
if(table_get (grpstatus, w))
return OK;
}
}
}
if (!method_restricted)
return OK;
note_basic_auth_failure (r);
return AUTH_REQUIRED;
}
Again, this function is essentially the same in 2.0.
int module_type_checker(request_rec *pReq)
At this stage, we have almost finished
processing the request. All that is left to decide is who actually
handles it. This is done in two stages: first, by converting the URL
or filename into a MIME type or handler string, language, and
encoding; and second, by calling the appropriate function for the
type. This hook deals with the first part. If it generates a MIME
type, it should be stored in
pReq->content_type. Alternatively, if it
generates a handler string, it should be stored in
pReq->handler. The languages go in
pReq->content_languages, and the encoding in
pReq->content_encoding. Note that there is no
defined way of generating a unique handler string. Furthermore,
handler strings and MIME types are matched to the request handler
through the same table, so the handler string should probably not be
a MIME type.[80]
One obvious place that this must go on is in
mod_mime.c. See Example 21-20.
Example
Example 21-20. mod_mime.c
int find_ct(request_rec *r)
{
char *fn = strrchr(r->filename, '/'.;
mime_dir_config *conf =
(mime_dir_config *)ap_get_module_config(r->per_dir_config, &mime_module);
char *ext, *type, *orighandler = r->handler;
if (S_ISDIR(r->finfo.st_mode)) {
r->content_type = DIR_MAGIC_TYPE;
return OK;
}
if(fn == NULL) fn = r->filename;
/* Parse filename extensions, which can be in any order */
while ((ext = getword(r->pool, &fn, '.')) && *ext) {
int found = 0;
/* Check for Content-Type */
if ((type = table_get (conf->forced_types, ext))
|| (type = table_get (hash_buckets[hash(*ext)], ext))) {
r->content_type = type;
found = 1;
}
/* Check for Content-Language */
if ((type = table_get (conf->language_types, ext))) {
r->content_language = type;
found = 1;
}
/* Check for Content-Encoding */
if ((type = table_get (conf->encoding_types, ext))) {
if (!r->content_encoding)
r->content_encoding = type;
else
r->content_encoding = pstrcat(r->pool, r->content_encoding,
", ", type, NULL);
found = 1;
}
/* Check for a special handler, but not for proxy request */
if ((type = table_get (conf->handlers, ext)) && !r->proxyreq) {
r->handler = type;
found = 1;
}
/* This is to deal with cases such as foo.gif.bak, which we want
* to not have a type. So if we find an unknown extension, we
* zap the type/language/encoding and reset the handler.
*/
if (!found) {
r->content_type = NULL;
r->content_language = NULL;
r->content_encoding = NULL;
r->handler = orighandler;
}
}
/* Check for overrides with ForceType/SetHandler */
if (conf->type && strcmp(conf->type, "none"))
r->content_type = pstrdup(r->pool, conf->type);
if (conf->handler && strcmp(conf->handler, "none"))
r->handler = pstrdup(r->pool, conf->handler);
if (!r->content_type) return DECLINED;
return OK;
}
Another example can be found in
mod_negotiation.c, but it is rather more
complicated than is needed to illustrate the point.
Although the 2.0 version of the example is rather different, the
differences aren't really because of changes in the
hook and are more concerned with the complication of determining MIME
types with filters in place, so we won't bother to
show the 2.0 version here.
int module_fixups(request_rec *pReq)
Nearly there! This is your last chance to do anything that might be
needed before the request is finally handled. At this point, all
processing that is going to be done before the request is handled has
been completed, the request is going to be satisfied, and all that is
left to do is anything the request handler won't do.
Examples of what you might do here include setting environment
variables for CGI scripts, adding headers to
pReq->header_out, or even setting something to
modify the behavior of another module's handler in
pReq->notes. Things you probably
shouldn't do at this stage are many, but, most
importantly, you should leave anything security-related alone,
including (but certainly not limited to) the URL, the filename, and
the username. Most modules won't use this hook
because they do their real work elsewhere.
As an example, we will set the environment variables for a shell
script. Example 21-21 shows where
it's done in mod_env.c.
Example
Example 21-21. mod_env.c
static int fixup_env_module(request_rec *r)
{
table *e = r->subprocess_env;
env_dir_config_rec *sconf = ap_get_module_config(r->per_dir_config,
&env_module);
table *vars = sconf->vars;
if (!sconf->vars_present)
return DECLINED;
r->subprocess_env = ap_overlay_tables(r->pool, e, vars);
return OK;
}
Notice that this doesn't directly set the
environment variables; that would be pointless because a
subprocess's environment variables are created anew
from pReq->subprocess_env. Also notice that, as
is often the case in computing, considerably more effort is spent in
processing the configuration for mod_env.c than
is spent at the business end.
handler_rec aModuleHandlers[]; [1.3]
The definition of a handler_rec can be found in
http_config.h (1.3):
typedef struct {
char *content_type;
int (*handler)(request_rec *);
} handler_rec;
In 2.0, the handlers are simply registered with a hook in the usual
way and are responsible for checking the content type (or anything
else they want to check) in the hook.
Finally, we are ready to handle the request. The core now searches
through the modules' handler entries, looking for an
exact match for either the handler type or the MIME type, in that
order (that is, if a handler type is set, that is used; otherwise,
the MIME type is used). When a match is found, the corresponding
handler function is called. This will do the actual business of
serving the user's request. Often you
won't want to do this, because
you'll have done the work of your module earlier,
but this is the place to run your Java, translate to Swedish, or
whatever you might want to do to serve actual content to the user.
Most handlers either send some kind of content directly (in which
case, they must remember to call ap_send_http_header(
) before sending the content) or use one of the internal
redirect methods (e.g., internal_redirect( )).
mod_status.c only implements a handler; Example 21-22 (1.3) shows the handler's
table.
Example
Example 21-22. mod_status.c
handler_rec status_handlers[] =
{
{ STATUS_MAGIC_TYPE, status_handler },
{ "server-status", status_handler },
{ NULL }
};
We don't show the actual handler here, because
it's big and boring. All it does is trawl through
the scoreboard (which records details of the various child processes)
and generate a great deal of HTML. The user invokes this handler with
either a SetHandler or an
AddHandler; however, since the handler makes no
use of a file, SetHandler is the more natural way
to do it. Notice the reference to
STATUS_MAGIC_TYPE. This is a
"magic"; MIME type — the use of
which is now deprecated — but we must retain it for backward
compatibility in this particular module.
The same example in 2.0 has a hook instead of an array of
handler_rec s:
static void register_hooks(apr_pool_t *p)
{
ap_hook_handler(status_handler, NULL, NULL, APR_HOOK_MIDDLE);
...
}
and, as discussed, status_handler( ) checks the
content type itself:
static int status_handler(request_rec *r)
{
...
if (strcmp(r->handler, STATUS_MAGIC_TYPE) &&
strcmp(r->handler, "server-status")) {
return DECLINED;
}
...
int module_logger(request_rec *pRec)
Now that the request has been processed and
the dust has settled, you may want to log the request in some way.
Here's your chance to do that. Although the core
stops running the logger function as soon as a module returns
something other than OK or
DECLINED, that is rarely done, as there is no way
to know whether another module needs to log something.
Although
mod_log_agent.c is more or less out of date since
mod_log_config.c was introduced, it makes a
nice, compact example. See Example 21-23.
Example
Example 21-23. mod_log_agent.c
int agent_log_transaction(request_rec *orig)
{
agent_log_state *cls = ap_get_module_config (orig->server->module_config,
&agent_log_module);
char str[HUGE_STRING_LEN];
char *agent;
request_rec *r;
if(cls->agent_fd <0)
return OK;
for (r = orig; r->next; r = r->next)
continue;
if (*cls->fname == '\0'. /* Don't log agent */
return DECLINED;
agent = table_get(orig->headers_in, "User-Agent");
if(agent != NULL)
{
sprintf(str, "%s\n", agent);
write(cls->agent_fd, str, strlen(str));
}
return OK;
}
This is not a good example of programming practice. With its
fixed-size buffer str, it leaves a gaping security
hole. It wouldn't be enough simply to split the
write into two parts to avoid this problem.
Because the log file is shared among all server processes, the
write must be atomic, or the log file could get
mangled by overlapping write s.
mod_log_config.c carefully avoids this problem.
Unfortunately, mod_log_agent.c has been axed in
2.0; but if it were still there, it would look pretty much the same.
void
child_exit(server_rec *pServer,pool *pPool) [1.3]
This function is called immediately before a particular child exits.
See Child Initialization; earlier
in this chapter, for an explanation of what
"child"; means in this context.
Typically, this function will be used to release resources that are
persistent between connections, such as database or file handles.
In 2.0 there is no child_exit hook — instead
one registers a cleanup function with the pool passed in the
init_child hook.
See Example 21-24 for an excerpt from
mod_log_config.c.
Example
Example 21-24. mod_log_config.c
static void flush_all_logs(server_rec *s, pool *p)
{
multi_log_state *mls;
array_header *log_list;
config_log_state *clsarray;
int i;
for (; s; s = s->next) {
mls = ap_get_module_config(s->module_config, &config_log_module);
log_list = NULL;
if (mls->config_logs->nelts) {
log_list = mls->config_logs;
}
else if (mls->server_config_logs) {
log_list = mls->server_config_logs;
}
if (log_list) {
clsarray = (config_log_state *) log_list->elts;
for (i = 0; i < log_list->nelts; ++i) {
flush_log(&clsarray[i]);
}
}
}
}
This routine is only used when BUFFERED_LOGS is
defined. Predictably enough, it flushes all the buffered logs, which
would otherwise be lost when the child exited.
In 2.0, the same function is used, but it is registered via the
init_child hook:
static void init_child(apr_pool_t *p, server_rec *s)
{
#ifdef BUFFERED_LOGS
/* Now register the last buffer flush with the cleanup engine */
apr_pool_cleanup_register(p, s, flush_all_logs, flush_all_logs);
#endif
}
 |  |  | | 21.2. Status Codes |  | 21.4. A Complete Example |
Copyright © 2003 O'Reilly & Associates. All rights reserved.
|