From: Nick Kew Date: Thu, 7 Dec 2006 23:23:40 +0000 (+0000) Subject: Backport R452330: header editing in mod_headers X-Git-Tag: 2.2.4~66 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=34e2eb98662587c42eaf73a5767f6318e594f3ab;p=thirdparty%2Fapache%2Fhttpd.git Backport R452330: header editing in mod_headers +1 niq, chrisd, wrowe git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@483732 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 4ff7a57d193..d82024644b0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -*- coding: utf-8 -*- Changes with Apache 2.2.4 + *) mod_headers: support regexp-based editing of HTTP headers [Nick Kew] + * mod_proxy: Add explicit flushing feature. When Servlet container sends AJP body message with size 0, this means that Servlet container has asked for an explicit flush. diff --git a/STATUS b/STATUS index 8177b57a9a6..c2d387163f6 100644 --- a/STATUS +++ b/STATUS @@ -255,12 +255,6 @@ PATCHES PROPOSED TO BACKPORT FROM TRUNK: Trunk version works +1: trawick, jim, wrowe - * mod_headers: support regexp-based manipulation - http://svn.apache.org/viewvc?view=rev&revision=452330 - +1: niq, chrisd, wrowe - chrisd: Note that the docs need to be back-ported, and - new_features_2_4.xml revised too. - * mod_ldap: Better detection and clean up of ldap connection that have been terminated by the ldap server. http://svn.apache.org/viewvc?view=rev&revision=472633 diff --git a/docs/manual/mod/mod_headers.xml b/docs/manual/mod/mod_headers.xml index 7491245aa8f..c370737d62e 100644 --- a/docs/manual/mod/mod_headers.xml +++ b/docs/manual/mod/mod_headers.xml @@ -144,20 +144,29 @@ is available only in Apache 2.0 MyHeader: D=3775428 t=991424704447256 mytext +
  • Enable DAV to work with Apache running HTTP through SSL hardware + (problem description) by replacing https: with + http: in the Destination header: + + RequestHeader edit Destination ^https: http: early + +
  • + RequestHeader Configure HTTP request headers -RequestHeader set|append|add|unset header -[value] [early|env=[!]variable] +RequestHeader set|append|add|unset|edit header +[value] [replacement] [early|env=[!]variable] server configvirtual host directory.htaccess FileInfo -

    This directive can replace, merge or remove HTTP request +

    This directive can replace, merge, change or remove HTTP request headers. The header is modified just before the content handler is run, allowing incoming headers to be modified. The action it performs is determined by the first argument. This can be one @@ -186,18 +195,28 @@ is available only in Apache 2.0

    The request header of this name is removed, if it exists. If there are multiple headers of the same name, all will be removed. value must be omitted.
    + +
    edit
    +
    If this request header exists, its value is transformed according + to a regular expression + search-and-replace. The value argument is a regular expression, and the replacement + is a replacement string, which may contain backreferences.

    This argument is followed by a header name, which can include the final colon, but it is not required. Case is ignored. For add, append and - set a value is given as the third argument. If + set a value is given as the third argument. If a value contains spaces, it should be surrounded by double quotes. For unset, no value should be given. value may be a character string, a string containing format specifiers or a combination of both. The supported format specifiers are the same as for the Header, - please have a look there for details.

    + please have a look there for details. For edit both + a value and a replacement are required, and are + a regular expression and a + replacement string respectively.

    The RequestHeader directive may be followed by an additional argument, which may be used to specify conditions under @@ -221,7 +240,7 @@ is available only in Apache 2.0 Header Configure HTTP response headers -Header [condition] set|append|add|unset|echo +Header [condition] set|append|add|unset|echo|edit header [value] [early|env=[!]variable] server configvirtual host directory.htaccess @@ -271,6 +290,13 @@ is available only in Apache 2.0 response headers. header may be a regular expression. value must be omitted. + +

    edit
    +
    If this request header exists, its value is transformed according + to a regular expression + search-and-replace. The value argument is a regular expression, and the replacement + is a replacement string, which may contain backreferences.

    This argument is followed by a header name, which @@ -322,6 +348,10 @@ is available only in Apache 2.0 more efficient than %s.

    +

    For edit there is both a value argument + which is a regular expression, + and an additional replacement string.

    +

    The Header directive may be followed by an an additional argument, which may be used to specify conditions under which the action will be taken, or may be the keyword early diff --git a/modules/metadata/mod_headers.c b/modules/metadata/mod_headers.c index bbeb146124d..34174a1809c 100644 --- a/modules/metadata/mod_headers.c +++ b/modules/metadata/mod_headers.c @@ -89,7 +89,8 @@ typedef enum { hdr_set = 's', /* set (replace old value) */ hdr_append = 'm', /* append (merge into any old value) */ hdr_unset = 'u', /* unset header */ - hdr_echo = 'e' /* echo headers from request to response */ + hdr_echo = 'e', /* echo headers from request to response */ + hdr_edit = 'r' /* change value by regexp */ } hdr_actions; /* @@ -119,6 +120,7 @@ typedef struct { apr_array_header_t *ta; /* Array of format_tag structs */ ap_regex_t *regex; const char *condition_var; + const char *subs; } header_entry; /* echo_do is used for Header echo to iterate through the request headers*/ @@ -348,6 +350,7 @@ static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s /* No string to parse with unset and echo commands */ if (hdr->action == hdr_unset || + hdr->action == hdr_edit || hdr->action == hdr_echo) { return NULL; } @@ -368,7 +371,8 @@ static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd, const char *action, const char *hdr, const char *value, - const char* envclause) + const char *subs, + const char *envclause) { headers_conf *dirconf = indirconf; const char *condition_var = NULL; @@ -392,10 +396,29 @@ static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd, new->action = hdr_unset; else if (!strcasecmp(action, "echo")) new->action = hdr_echo; + else if (!strcasecmp(action, "edit")) + new->action = hdr_edit; else - return "first argument must be 'add', 'set', 'append', 'unset' or " - "'echo'."; + return "first argument must be 'add', 'set', 'append', 'unset', " + "'echo' or 'edit'."; + if (new->action == hdr_edit) { + if (subs == NULL) { + return "Header edit requires a match and a substitution"; + } + new->regex = ap_pregcomp(cmd->pool, value, AP_REG_EXTENDED); + if (new->regex == NULL) { + return "Header edit regex could not be compiled"; + } + new->subs = subs; + } + else { + /* there's no subs, so envclause is really that argument */ + if (envclause != NULL) { + return "Too many arguments to directive"; + } + envclause = subs; + } if (new->action == hdr_unset) { if (value) { if (envclause) { @@ -465,6 +488,7 @@ static const char *header_cmd(cmd_parms *cmd, void *indirconf, const char *hdr; const char *val; const char *envclause; + const char *subs; action = ap_getword_conf(cmd->pool, &args); if (cmd->info == &hdr_out) { @@ -478,6 +502,7 @@ static const char *header_cmd(cmd_parms *cmd, void *indirconf, } hdr = ap_getword_conf(cmd->pool, &args); val = *args ? ap_getword_conf(cmd->pool, &args) : NULL; + subs = *args ? ap_getword_conf(cmd->pool, &args) : NULL; envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL; if (*args) { @@ -485,7 +510,7 @@ static const char *header_cmd(cmd_parms *cmd, void *indirconf, " has too many arguments", NULL); } - return header_inout_cmd(cmd, indirconf, action, hdr, val, envclause); + return header_inout_cmd(cmd, indirconf, action, hdr, val, subs, envclause); } /* @@ -512,6 +537,26 @@ static char* process_tags(header_entry *hdr, request_rec *r) } return str ? str : ""; } +static const char *process_regexp(header_entry *hdr, const char *value, + apr_pool_t *pool) +{ + unsigned int nmatch = 10; + ap_regmatch_t pmatch[10]; + const char *subs; + char *ret; + int diffsz; + if (ap_regexec(hdr->regex, value, nmatch, pmatch, 0)) { + /* no match, nothing to do */ + return value; + } + subs = ap_pregsub(pool, hdr->subs, value, nmatch, pmatch); + diffsz = strlen(subs) - (pmatch[0].rm_eo - pmatch[0].rm_so); + ret = apr_palloc(pool, strlen(value) + 1 + diffsz); + memcpy(ret, value, pmatch[0].rm_so); + strcpy(ret + pmatch[0].rm_so, subs); + strcat(ret, value + pmatch[0].rm_eo); + return ret; +} static int echo_header(echo_do *v, const char *key, const char *val) { @@ -528,7 +573,9 @@ static int echo_header(echo_do *v, const char *key, const char *val) static void do_headers_fixup(request_rec *r, apr_table_t *headers, apr_array_header_t *fixup, int early) { + echo_do v; int i; + const char *val; for (i = 0; i < fixup->nelts; ++i) { header_entry *hdr = &((header_entry *) (fixup->elts))[i]; @@ -568,14 +615,18 @@ static void do_headers_fixup(request_rec *r, apr_table_t *headers, apr_table_unset(headers, hdr->header); break; case hdr_echo: - { - echo_do v; v.r = r; v.hdr = hdr; apr_table_do((int (*) (void *, const char *, const char *)) echo_header, (void *) &v, r->headers_in, NULL); break; - } + case hdr_edit: + val = apr_table_get(headers, hdr->header); + if (val != NULL) { + apr_table_setn(headers, hdr->header, + process_regexp(hdr, val, r->pool)); + } + break; } } }