Index: mod_headers.c =================================================================== --- mod_headers.c (revision 471291) +++ mod_headers.c (working copy) @@ -25,8 +25,8 @@ * * Syntax is: * - * Header action header value - * RequestHeader action header value + * Header [condition] set|append|add|unset|echo header [value] [ early | env=[!]variable[~regex] | hdr=[!]name[~regex] ] + * RequestHeader set|append|add|unset header [value] [early | env=[!]variable | env=variable[~regex] | hdr=[!]name | hdr=name[~regex] ] * * Where action is one of: * set - set this header, replacing any old value @@ -35,9 +35,46 @@ * append - append this text onto any existing header of this same * unset - remove this header * - * Where action is unset, the third argument (value) should not be given. + * When action is unset, the third argument (value) should not be given. * The header name can include the colon, or not. * + * The value may optionally contain these format specifiers: + * %% The percent sign + * %T The time the request was received in Universal Coordinated Time since the + * epoch (Jan. 1, 1970) measured in microseconds. Formatted as t={time} + * %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. Formatted as D={duration} + * %t Same as %T except the time is preceded by t=. + * %u Same as %T, but formatted per RFC822. e.g. Sun, 05 Nov 2006 16:24:01 GMT + * %v server (not virtual) hostname + * %h hostname (from URL or Host header) + * %D Same as %d except the duration is preceded by D=. + * %{FOOBAR}e The contents of the environment variable FOOBAR. + * %{FOOBAR}s The contents of the SSL environment variable FOOBAR, if mod_ssl is enabled. + * + * The Header directive may be followed by an optional condition argument which must be either: + * + * the string "early" + * -or- + * a string starting with "env=" or "env=!" or "hdr=" or "hdr=!" to specify a test which determines if the action is taken. + * + * If the environment variable specified in the env=... argument exists (or if the environment variable does not + * exist with env=!... is specified) then the action specified by the Header directive will take effect. Otherwise, + * the directive will have no effect on the request. + * + * If the header specified by hdr=... exists (or if the header does not exists with hdr=!...) then the action specified + * by the Header directive will take effect. Otherwise, the directive will have no effect on the request. Request + * headers are examined when this argument is used in a RequestHeader directive, response headers are examined when this + * argument is used in a Header directive. + * + * When the env= or hdr= form of this argument is used, it may be further qualified by appending ~regex, a regular + * expression which must match the environment variable's value (for env=) or the header's value (for hdr=). + * + * e.g. Header add MyHeader "%D, %t, someText" env=myVariable~[a-z]_test + * This will add a new header only if the environment variable myVariable exists and its value + * contains any lowercase letter followed by _test. If this condition is true, the new header will be added as: + * MyHeader: D=3775428, t=991424704447256, someText + * * The Header and RequestHeader directives can only be used where allowed * by the FileInfo override. * @@ -118,8 +155,10 @@ hdr_actions action; const char *header; apr_array_header_t *ta; /* Array of format_tag structs */ - ap_regex_t *regex; + ap_regex_t *regex; /* regex for echo or edit */ const char *condition_var; + char condition_type; /* 'e' = environment var, 'h' = header */ + ap_regex_t *cregex; /* regex for condition argument */ const char *subs; } header_entry; @@ -156,10 +195,32 @@ return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT, (apr_time_now() - r->request_time)); } +static const char *header_request_raw_duration(request_rec *r, char *a) +{ + return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, + (apr_time_now() - r->request_time)); +} static const char *header_request_time(request_rec *r, char *a) { return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time); } +static const char *header_request_utime(request_rec *r, char *a) +{ char *tim = apr_palloc(r->pool, APR_RFC822_DATE_LEN); + apr_rfc822_date(tim, r->request_time); + return tim; +} +static const char *header_request_raw_time(request_rec *r, char *a) +{ + return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, r->request_time); +} +static const char *header_request_server(request_rec *r, char *a) +{ + return apr_psprintf(r->pool, "%s", r->server->server_hostname); +} +static const char *header_request_host(request_rec *r, char *a) +{ + return apr_psprintf(r->pool, "%s", r->hostname); +} /* unwrap_header returns HDR with any newlines converted into * whitespace if necessary. */ @@ -376,8 +437,12 @@ { headers_conf *dirconf = indirconf; const char *condition_var = NULL; + char condition_type = 'e'; const char *colon; header_entry *new; + char *regstr; + char *last; + apr_array_header_t *fixup = (cmd->info == &hdr_in) ? dirconf->fixup_in : (cmd->info == &hdr_err) @@ -453,30 +518,46 @@ return "Header requires three arguments"; /* Handle the envclause on Header */ + new->condition_var = NULL; if (envclause != NULL) { if (strcasecmp(envclause, "early") == 0) { condition_var = condition_early; } - else { - if (strncasecmp(envclause, "env=", 4) != 0) { - return "error: envclause should be in the form env=envar"; + else { + if (strncasecmp(envclause, "env=", 4) != 0 && strncasecmp(envclause, "hdr=", 4) != 0) { + return "Header condition should be in the form env=name[~regex] or hdr=name[~regex]"; } if ((envclause[4] == '\0') || ((envclause[4] == '!') && (envclause[5] == '\0'))) { - return "error: missing environment variable name. " - "envclause should be in the form env=envar "; + return "Header missing condition variable name. " + "Condition should be in the form env=name[~regex] or hdr=name[~regex]"; } condition_var = envclause + 4; + /* type is 'e' for env, 'h' for header */ + condition_type = apr_tolower(envclause[0]); } - } + /* a condition, maybe with a regex */ + if (condition_var) { + new->condition_var = apr_strtok(apr_pstrdup(cmd->pool, condition_var), "~", &last); + regstr = apr_strtok(NULL, "~", &last); + if (regstr) { + new->cregex = ap_pregcomp(cmd->pool, regstr, 0); + if (new->cregex == NULL) { + return "Header condition regex could not be compiled"; + } + } + else { + new->cregex = NULL; + } + new->condition_type = condition_type; + } + } if ((colon = ap_strchr_c(hdr, ':'))) { hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr); } new->header = hdr; - new->condition_var = condition_var; - return parse_format_string(cmd->pool, new, value); } @@ -580,6 +661,8 @@ for (i = 0; i < fixup->nelts; ++i) { header_entry *hdr = &((header_entry *) (fixup->elts))[i]; const char *envar = hdr->condition_var; + apr_table_t *condition_table = r->subprocess_env; + if (hdr->condition_type == 'h') condition_table = headers; /* ignore early headers in late calls */ if (!early && (envar == condition_early)) { @@ -592,11 +675,14 @@ /* Have any conditional envar-controlled Header processing to do? */ else if (envar && !early) { if (*envar != '!') { - if (apr_table_get(r->subprocess_env, envar) == NULL) + val = apr_table_get(condition_table, envar); + if (val == NULL) continue; - } + if (hdr->cregex && ap_regexec(hdr->cregex, val, 0, NULL, 0) ) + continue; + } else { - if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) + if (apr_table_get(condition_table, &envar[1]) != NULL) continue; } } @@ -759,8 +845,13 @@ format_tag_hash = apr_hash_make(p); register_format_tag_handler("D", (const void *)header_request_duration); register_format_tag_handler("t", (const void *)header_request_time); + register_format_tag_handler("d", (const void *)header_request_raw_duration); + register_format_tag_handler("T", (const void *)header_request_raw_time); + register_format_tag_handler("u", (const void *)header_request_utime); register_format_tag_handler("e", (const void *)header_request_env_var); register_format_tag_handler("s", (const void *)header_request_ssl_var); + register_format_tag_handler("v", (const void *)header_request_server); + register_format_tag_handler("h", (const void *)header_request_host); return OK; }