Header Parser
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 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