Commit 197a0be5 authored by Bill Stoddard's avatar Bill Stoddard
Browse files

Extend mod_headers to support conditional driven Header

add, append and set. Use SetEnvIf to set an envar and conditionally
add/append/set headers based on this envar thusly:

     SetEnvIf TSMyHeader value HAVE_TSMyHeader
     Header add MyHeader "%t %D" env=HAVE_TSMyHeader

If the request contains header "TSMyHeader: value" then header
MyHeader: "t=xxxxxxxxxx D=yyyy" will be sent on the response.

Update mod_headers.html.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@89271 13f79535-47bb-0310-9956-ffa450edef68
parent 4d7de8df
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
Changes with Apache 2.0.19-dev
  *) Extend mod_headers to support conditional driven Header 
     add, append and set. Use SetEnvIf to set an envar and conditionally
     add/append/set headers based on this envar thusly:

     SetEnvIf TSMyHeader value HAVE_TSMyHeader
     Header add MyHeader "%t %D" env=HAVE_TSMyHeader

     If the request contains header "TSMyHeader: value" then header
     MyHeader: "t=xxxxxxxxxx D=yyyy" will be sent on the response.
     [Bill Stoddard]

  *) Extend mod_headers to support using format specifiers on Header
     add, append and set header values. Two format specifiers are supported:

     %t - reports, in UTC microseconds since the epoch, when the
          request was received.

     %D - reports the time, in microseconds, between when the request was 
          received and the response sent. 

     Examples:
     Header add MyHeader "This request served in %D microseconds. %t"

     results in a header being added to the response that looks like this:
     
     MyHeader: This request served in D=5438 microseconds. t=991424704447256

     [Bill Stoddard]

  *) Fix reset_filter().  We need to be careful how we remove filters.
     If we set r->output_filters to NULL, we also have to reset the
+66 −13
Original line number Diff line number Diff line
@@ -152,7 +152,7 @@ the browser, or by Apache input filters to be overridden or modified.
 HREF="directive-dict.html#Syntax"
 REL="Help"
><STRONG>Syntax:</STRONG></A> Header set|append|add
 <EM>header</EM> <EM>value</EM><BR>
 <EM>header</EM> <EM>value</EM> <EM>[env=[!]environment-variable]</EM><BR>
<A
 HREF="directive-dict.html#Syntax"
 REL="Help"
@@ -186,7 +186,8 @@ by the first argument. This can be one of the following values:

<UL>
<LI><STRONG>set</STRONG><BR>
  The response header is set, replacing any previous header with this name
  The response header is set, replacing any previous header with this name.
  The <EM>value</EM> may be a format string.

<LI><STRONG>append</STRONG><BR>
  The response header is appended to any existing header of the same
@@ -206,20 +207,30 @@ by the first argument. This can be one of the following values:

<LI><STRONG>echo</STRONG><BR>
  Request headers with this name are echoed back in the response headers.
  <EM>header</EM> may be a regular expression. For example, the directive
  <P>
  Header echo ^TS*
  <P>
  will cause all request headers that begin with TS to be echoed
  or copied over to the response headers.
  <EM>header</EM> may be a regular expression. 
</UL>

This argument is followed by a header name, which can include the
This argument is followed by a <EM>header</EM> name, which can include the
final colon, but it is not required. Case is ignored for set, append, add
and unset. The header name for echo is case sensitive and may be a
regular expression. For add, append and set a value is given as the third 
argument. If this value contains spaces, it should be surrounded by double 
quotes. For unset and echo, no value should be given.
and unset. The <EM>header</EM> name for echo is case sensitive and may be a
regular expression. 
<P>
add, append and set take a <EM>value</EM> as the third argument. If 
<EM>value</EM> contains spaces, it should be surrounded by doublequotes. 
<EM>value</EM> may be a character string, a string containing format
specifiers or a combination of both. The following format specifiers
are supported in <EM>value</EM>:
<PRE>
%t:	The time the request was received in Universal Coordinated Time
	since the epoch (Jan. 1, 1970) measured in microseconds. The
	value is preceeded by "t=".
%D:     The time from when the request was received to the time the
        headers are sent on the wire. This is a measure of the
	duration of the request. The value is preceeded by "D=".
</PRE>
add, append and set may take an optional <EM>conditional clause</EM>
as the fourth argument. The header action (add, append, set) is 
done only if the <EM>conditional clause</EM> evaluates as TRUE.

<H3>Order of Processing</H3>

@@ -251,7 +262,49 @@ The Header 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>
<HR>
<H2>Examples</H2>
<OL>
<LI>Copy all request headers that begin with "TS" to the response headers:</LI>
<PRE>
   Header echo ^TS*
</PRE>

<LI>Add a header, MyHeader, to the response including a timestamp for when
the request was received and how long it took to begin serving the
request. This header can be used by the client to intuit load on
the server or in isolating bottlenecks between the client and the
server.</LI>
<PRE>
   Header add MyHeader "%D %t"
</PRE>
results in this header being added to the response:
<PRE>
   MyHeader: D=3775428 t=991424704447256
</PRE>
<LI>Say hello to Joe</LI>
<PRE>
   Header add MyHeader "Hello Joe. It took %D microseconds for Apache to serve this request."
</PRE>
results in this header being added to the response:
<PRE>
   MyHeader: Hello Joe. It took D=3775428 microseconds for Apache to serve this request.
</PRE>

<LI>Conditionally send MyHeader on the response if and only if header
"MyRequestHeader" is present on the request. This is useful for
constructing headers in response to some client stimulus. Note that
this example requires the services of the mod_setenvif module.</LI>
<PRE>
   SetEnvIf MyRequestHeader value HAVE_MyRequestHeader<BR>
   Header add MyHeader "%D %t mytext" env=HAVE_MyRequestHeader
</PRE> 
If the header "MyRequestHeader: value" is present on the HTTP request, the response
will contain the following header:
<PRE>
   MyHeader: D=3775428 t=991424704447256 mytext
</PRE>
</OL>
<!--#include virtual="footer.html" -->
</BODY>
</HTML>
+51 −8
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ typedef struct {
    char *header;
    apr_array_header_t *ta;   /* Array of format_tag structs */
    regex_t *regex;
    const char *condition_var;
} header_entry;

/* echo_do is used for Header echo to iterate through the request headers*/
@@ -338,15 +339,16 @@ static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s
/* handle RequestHeader and Header directive */
static const char *header_inout_cmd(hdr_inout inout, cmd_parms *cmd, void *indirconf,
                              const char *action, const char *inhdr,
                              const char *value)
                              const char *value, const char* envclause)
{
    headers_conf *dirconf = indirconf;
    const char *condition_var;
    char *colon;
    char *hdr = apr_pstrdup(cmd->pool, inhdr);
    header_entry *new;
    server_rec *s = cmd->server;
    headers_conf *serverconf = ap_get_module_config(s->module_config,
                                                    &headers_module);
    char *colon;

    if (cmd->path) {
        new = (header_entry *) apr_array_push((hdr_in == inout) ? dirconf->fixup_in : dirconf->fixup_out);
@@ -389,20 +391,47 @@ static const char *header_inout_cmd(hdr_inout inout, cmd_parms *cmd, void *indir
    else if (!value)
        return "header requires three arguments";

    /* Handle the envclause on Header */
    if (envclause != NULL) {
        if (inout != hdr_out) {
            return "error: envclause (env=...) only valid on Header directive";
        }
	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 = apr_pstrdup(cmd->pool, &envclause[4]);
    }
    
    if ((colon = strchr(hdr, ':')))
        *colon = '\0';

    new->header = hdr;
    new->condition_var = condition_var;

    return parse_format_string(cmd->pool, new, value);
}

/* Handle Header directive */
static const char *header_cmd(cmd_parms *cmd, void *indirconf,
                              const char *action, const char *inhdr,
                              const char *value)
                              const char *args)
{
    return header_inout_cmd(hdr_out, cmd, indirconf, action, inhdr, value);
    char *s;
    const char *action;
    const char *hdr;
    const char *val;
    const char *envclause;

    s = apr_pstrdup(cmd->pool, args);
    action = ap_getword_conf(cmd->pool, &s);
    hdr = ap_getword_conf(cmd->pool, &s);
    val = *s ? ap_getword_conf(cmd->pool, &s) : NULL;
    envclause = *s ? ap_getword_conf(cmd->pool, &s) : NULL;

    return header_inout_cmd(hdr_out, cmd, indirconf, action, hdr, val, envclause);
}

/* handle RequestHeader directive */
@@ -410,7 +439,7 @@ static const char *request_header_cmd(cmd_parms *cmd, void *indirconf,
                              const char *action, const char *inhdr,
                              const char *value)
{
    return header_inout_cmd(hdr_in, cmd, indirconf, action, inhdr, value);
    return header_inout_cmd(hdr_in, cmd, indirconf, action, inhdr, value, NULL);
}

/*
@@ -458,6 +487,20 @@ static void do_headers_fixup(request_rec *r, hdr_inout inout,

    for (i = 0; i < fixup->nelts; ++i) {
        header_entry *hdr = &((header_entry *) (fixup->elts))[i];

        /* Have any conditional envar-controlled Header processing to do? */
        if (hdr->condition_var) {
            const char *envar = hdr->condition_var;
            if (*envar != '!') {
                if (apr_table_get(r->subprocess_env, envar) == NULL)
                    continue;
            }
            else {
                if (apr_table_get(r->subprocess_env, &envar[1]) != NULL)
                    continue;
            }
        }

        switch (hdr->action) {
        case hdr_add:
            apr_table_addn(headers, hdr->header, process_tags(hdr, r));
@@ -536,8 +579,8 @@ static apr_status_t ap_headers_fixup(request_rec *r)
                                        
static const command_rec headers_cmds[] =
{
    AP_INIT_TAKE23("Header", header_cmd, NULL, OR_FILEINFO,
                   "an action, header and value"),
    AP_INIT_RAW_ARGS("Header", header_cmd, NULL, OR_FILEINFO,
                   "an action, header and value followed by optional env clause"),
    AP_INIT_TAKE23("RequestHeader", request_header_cmd, NULL, OR_FILEINFO,
                   "an action, header and value"),
    {NULL}