[Remove entries to the current 2.0 section below, when backported]
+ *) mod_headers: implement "Early" processing option in post_read_request
+ to enable Header and RequestHeader directives to be used to set up
+ testcases for pre-fixups request phases [Nick Kew]
+
*) mod_dir: the trailing-slash behaviour is now configurable using the
DirectorySlash directive. [André Malo]
<?xml version="1.0"?>
<!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
-<!-- $Revision: 1.17 $ -->
+<!-- $Revision: 1.18 $ -->
<!--
Copyright 2002-2004 The Apache Software Foundation
reversed, the MirrorID header is set to "mirror 12".</p>
</section>
+<section id="early"><title>Early and Late Processing</title>
+ <p><module>mod_headers</module> can be applied either early or late
+ in the request. The normal mode is late, when Request Headers are
+ set immediately before running the content generator and Response
+ Headers just as the response is sent down the wire. Always use
+ Late mode in an operational server.</p>
+
+ <p>Early mode is designed as a test/debugging aid for developers.
+ Directives defined using the <code>early</code> keyword are set
+ right at the beginning of processing the request. This means
+ they can be used to simulate different requests and set up test
+ cases, but it also means that headers may be changed at any time
+ by other modules before generating a Response.</p>
+
+ <p>Because early directives are processed before the request path's
+ configuration is traversed, early headers can only be set in a
+ main server or virtual host context. Early directives cannot depend
+ on a request path, so they will fail in contexts such as
+ <code><Directory></code> or <code><Location></code>.</p>
+</section>
+
<section id="examples"><title>Examples</title>
<ol>
<name>RequestHeader</name>
<description>Configure HTTP request headers</description>
<syntax>RequestHeader set|append|add|unset <var>header</var>
-[<var>value</var>] [env=[!]<var>variable</var>]</syntax>
+[<var>value</var>] [early|env=[!]<var>variable</var>]</syntax>
<contextlist><context>server config</context><context>virtual host</context>
<context>directory</context><context>.htaccess</context></contextlist>
<override>FileInfo</override>
<p>The <directive>RequestHeader</directive> directive may be followed by
an additional argument, which may be used to specify conditions under
- which the action will be taken. If the <a href="../env.html">environment
+ which the action will be taken, or may be the keyword <code>early</code>
+ to specify <a href="#early">early processing</a>. If the
+ <a href="../env.html">environment
variable</a> specified in the <code>env=<var>...</var></code> argument
exists (or if the environment variable does not exist and
<code>env=!<var>...</var></code> is specified) then the action specified
by the <directive>RequestHeader</directive> directive will take effect.
Otherwise, the directive will have no effect on the request.</p>
- <p>The <directive>RequestHeader</directive> directive is processed
+ <p>Except in <a href="#early">early</a> mode, the
+ <directive>RequestHeader</directive> directive is processed
just before the request is run by its handler in the fixup phase.
This should allow headers generated by the browser, or by Apache
input filters to be overridden or modified.</p>
<name>Header</name>
<description>Configure HTTP response headers</description>
<syntax>Header [<var>condition</var>] set|append|add|unset|echo
-<var>header</var> [<var>value</var>] [env=[!]<var>variable</var>]</syntax>
+<var>header</var> [<var>value</var>] [early|env=[!]<var>variable</var>]</syntax>
<contextlist><context>server config</context><context>virtual host</context>
<context>directory</context><context>.htaccess</context></contextlist>
<override>FileInfo</override>
</note>
<p>The <directive>Header</directive> directive may be followed by an
- additional argument, which may be used to specify conditions
- under which the action will be taken. If the <a
- href="../env.html">environment variable</a> specified in the
+ an additional argument, which may be used to specify conditions under
+ which the action will be taken, or may be the keyword <code>early</code>
+ to specify <a href="#early">early processing</a>. If the
+ <a href="../env.html">environment variable</a> specified in the
<code>env=<var>...</var></code> argument exists (or if the environment
variable does not exist and <code>env=!<var>...</var></code> is specified)
then the action specified by the <directive>Header</directive> directive
will take effect. Otherwise, the directive will have no effect
on the request.</p>
- <p>The <directive>Header</directive> directives are processed just
+ <p>Except in <a href="#early">early</a> mode, the
+ <directive>Header</directive> directives are processed just
before the response is sent to the network. These means that it is
possible to set and/or override most headers, except for those headers
added by the header filter.</p>
char *arg;
} format_tag;
+/* 'Magic' condition_var value to run action in post_read_request */
+static const char* condition_early = "early";
/*
* There is one "header_entry" per Header/RequestHeader config directive
*/
/* Handle the envclause on Header */
if (envclause != NULL) {
- if (strncasecmp(envclause, "env=", 4) != 0) {
- return "error: envclause should be in the form env=envar";
+ if (strcasecmp(envclause, "early") == 0) {
+ condition_var = condition_early;
}
- if ((envclause[4] == '\0')
- || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
- return "error: missing environment variable name. "
- "envclause should be in the form env=envar ";
+ else {
+ if (strncasecmp(envclause, "env=", 4) != 0) {
+ return "error: envclause should be in the form env=envar";
+ }
+ if ((envclause[4] == '\0')
+ || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
+ return "error: missing environment variable name. "
+ "envclause should be in the form env=envar ";
+ }
+ condition_var = envclause + 4;
}
- condition_var = envclause + 4;
}
if ((colon = ap_strchr_c(hdr, ':'))) {
}
static void do_headers_fixup(request_rec *r, apr_table_t *headers,
- apr_array_header_t *fixup)
+ apr_array_header_t *fixup, int early)
{
int i;
for (i = 0; i < fixup->nelts; ++i) {
header_entry *hdr = &((header_entry *) (fixup->elts))[i];
+ const char *envar = hdr->condition_var;
+ /* ignore early headers in late calls */
+ if (!early && (envar == condition_early)) {
+ continue;
+ }
+ /* ignore late headers in early calls */
+ else if (early && (envar != condition_early)) {
+ continue;
+ }
/* Have any conditional envar-controlled Header processing to do? */
- if (hdr->condition_var) {
- const char *envar = hdr->condition_var;
+ else if (envar && !early) {
if (*envar != '!') {
if (apr_table_get(r->subprocess_env, envar) == NULL)
continue;
"headers: ap_headers_output_filter()");
/* do the fixup */
- do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err);
- do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out);
+ do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
+ do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out, 0);
/* remove ourselves from the filter chain */
ap_remove_output_filter(f);
* Add any header fields defined by "Header always" to r->err_headers_out.
* Server-wide first, then per-directory to allow overriding.
*/
- do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err);
+ do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
/*
* We've done our bit; remove ourself from the filter chain so there's
/* do the fixup */
if (dirconf->fixup_in->nelts) {
- do_headers_fixup(r, r->headers_in, dirconf->fixup_in);
+ do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 0);
+ }
+
+ return DECLINED;
+}
+static apr_status_t ap_headers_early(request_rec *r)
+{
+ headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
+ &headers_module);
+
+ /* do the fixup */
+ if (dirconf->fixup_in->nelts) {
+ do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 1);
+ }
+ if (dirconf->fixup_err->nelts) {
+ do_headers_fixup(r, r->err_headers_out, dirconf->fixup_err, 1);
+ }
+ if (dirconf->fixup_out->nelts) {
+ do_headers_fixup(r, r->headers_out, dirconf->fixup_out, 1);
}
return DECLINED;
ap_hook_insert_error_filter(ap_headers_insert_error_filter,
NULL, NULL, APR_HOOK_LAST);
ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST);
+ ap_hook_post_read_request(ap_headers_early, NULL, NULL, APR_HOOK_FIRST);
}
module AP_MODULE_DECLARE_DATA headers_module =