Return-Path: Delivered-To: apmail-httpd-dev-archive@www.apache.org Received: (qmail 26577 invoked from network); 24 Sep 2004 14:18:00 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur-2.apache.org with SMTP; 24 Sep 2004 14:18:00 -0000 Received: (qmail 91194 invoked by uid 500); 24 Sep 2004 14:15:39 -0000 Delivered-To: apmail-httpd-dev-archive@httpd.apache.org Received: (qmail 91120 invoked by uid 500); 24 Sep 2004 14:15:38 -0000 Mailing-List: contact dev-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list dev@httpd.apache.org Received: (qmail 90969 invoked by uid 99); 24 Sep 2004 14:15:35 -0000 X-ASF-Spam-Status: No, hits=1.6 required=10.0 tests=DNS_FROM_RFC_ABUSE,DNS_FROM_RFC_POST X-Spam-Check-By: apache.org Received-SPF: neutral (hermes.apache.org: local policy) Received: from [212.180.95.194] (HELO gotham-laptop.axiliance.com) (212.180.95.194) by apache.org (qpsmtpd/0.28) with ESMTP; Fri, 24 Sep 2004 07:15:32 -0700 Received: from gotham-laptop.axiliance.com (gotham-laptop [127.0.0.1]) by gotham-laptop.axiliance.com (8.12.8/8.12.8) with ESMTP id i8OEFV8x012701 for ; Fri, 24 Sep 2004 16:15:31 +0200 Received: (from joke@localhost) by gotham-laptop.axiliance.com (8.12.8/8.12.8/Submit) id i8OEFVad012699 for dev@httpd.apache.org; Fri, 24 Sep 2004 16:15:31 +0200 Subject: About mod_setenvif in general, application to mod_deflate in particular. From: Francois PESCE Reply-To: fp-axiliance@wanadoo.fr To: dev@httpd.apache.org Content-Type: multipart/mixed; boundary="=-xPs1CBrZR6Eeg6kN7/sU" Organization: Axiliance Message-Id: <1096035331.12535.75.camel@gotham-laptop> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.2.2 (1.2.2-5) Date: 24 Sep 2004 16:15:31 +0200 X-Virus-Checked: Checked X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N --=-xPs1CBrZR6Eeg6kN7/sU Content-Type: text/plain Content-Transfer-Encoding: 7bit Hi, Today, mod_setenvif does not provide a way to "set environment variable if" something is interesting in response headers. For example, in a reverse proxy architecture, this feature may allow to gzip/deflate documents according to their Content-Type, and not only to the request-uri or other client-side provided information that may hide the real content of what is requested (documents requested via a web applications are good examples too). I find that the obvious location to put this feature is mod_setenvif because of its name, and of the way it is used now worldwide to set the "no-gzip" env. As I developed this feature (because my company need it anyway) I provide it to you the patch to have some feed-back about it and if your community may want to incorporate it in the httpd source tree. The patch is attached, as well as a commented version to explain what and why I did this or that. - Francois PESCE --=-xPs1CBrZR6Eeg6kN7/sU Content-Disposition: attachment; filename=setenvif_output_header.patch Content-Type: text/plain; name=setenvif_output_header.patch; charset=UTF-8 Content-Transfer-Encoding: quoted-printable --- mod_setenvif.c.orig 2004-09-24 15:20:57.000000000 +0200 +++ mod_setenvif.c 2004-09-24 15:40:59.000000000 +0200 @@ -101,7 +101,8 @@ SPECIAL_REQUEST_URI, SPECIAL_REQUEST_METHOD, SPECIAL_REQUEST_PROTOCOL, - SPECIAL_SERVER_ADDR + SPECIAL_SERVER_ADDR, + SPECIAL_OUTPUT_HEADER }; typedef struct { char *name; /* header name */ @@ -116,6 +117,7 @@ =20 typedef struct { apr_array_header_t *conditionals; + int output_filter_on; /* act as output_filter for headers_out ?= */ } sei_cfg_rec; =20 module AP_MODULE_DECLARE_DATA setenvif_module; @@ -134,6 +136,7 @@ sei_cfg_rec *new =3D (sei_cfg_rec *) apr_palloc(p, sizeof(sei_cfg_rec)= ); =20 new->conditionals =3D apr_array_make(p, 20, sizeof(sei_entry)); + new->output_filter_on =3D 0; return (void *) new; } =20 @@ -154,6 +157,8 @@ =20 a->conditionals =3D apr_array_append(p, base->conditionals, overrides->conditionals); + if(base->output_filter_on || overrides->output_filter_on) + a->output_filter_on =3D 1; return a; } =20 @@ -163,6 +168,7 @@ */ #define ICASE_MAGIC ((void *)(&setenvif_module)) #define SEI_MAGIC_HEIRLOOM "setenvif-phase-flag" +#define SEI_OUTPUT_FILTER_NAME "setenvif-output-filter" =20 static int is_header_regex(apr_pool_t *p, const char* name)=20 { @@ -306,26 +312,6 @@ new->name =3D fname; new->regex =3D regex; new->icase =3D icase; - if ((simple_pattern =3D non_regex_pattern(cmd->pool, regex))) { - new->pattern =3D apr_strmatch_precompile(cmd->pool, - simple_pattern, !icase)= ; - if (new->pattern =3D=3D NULL) { - return apr_pstrcat(cmd->pool, cmd->cmd->name, - " pattern could not be compiled.", NULL= ); - } - new->preg =3D NULL; - } - else { - new->preg =3D ap_pregcomp(cmd->pool, regex, - (REG_EXTENDED | (icase ? REG_ICASE : 0= ))); - if (new->preg =3D=3D NULL) { - return apr_pstrcat(cmd->pool, cmd->cmd->name, - " regex could not be compiled.", NULL); - } - new->pattern =3D NULL; - } - new->features =3D apr_table_make(cmd->pool, 2); - if (!strcasecmp(fname, "remote_addr")) { new->special_type =3D SPECIAL_REMOTE_ADDR; } @@ -344,7 +330,36 @@ else if (!strcasecmp(fname, "server_addr")) { new->special_type =3D SPECIAL_SERVER_ADDR; } - else { + else if (!strcasecmp(fname, "output_header")) { + new->special_type =3D SPECIAL_OUTPUT_HEADER; + sconf->output_filter_on =3D 1; + /* + * The regex is the 3rd word here, because we keep the same sy= ntax + * as for header string, but we added the output_header key wo= rd to + * keep backward compatibility. (to avoid the matching of bot= h + * request and response header on a regex). + * In this case the 2nd param of the directive is the header + * regex/name. + */ + new->name =3D regex; + if (is_header_regex(cmd->pool, new->name)) { + new->pnamereg =3D ap_pregcomp(cmd->pool, new->name, (REG_E= XTENDED | + REG_NOSUB | (icase ? REG_ICASE : 0))); + if (new->pnamereg =3D=3D NULL) + return apr_pstrcat(cmd->pool, cmd->cmd->name, "Header = name + regex could not be compiled.", NUL= L); + } + else { + new->pnamereg =3D NULL; + } + regex =3D ap_getword_conf(cmd->pool, &args); + if (!*regex) { + return apr_pstrcat(cmd->pool, "Missing regular expression = for ", + cmd->cmd->name, NULL); + } + new->regex =3D regex; + } + else { new->special_type =3D SPECIAL_NOT; /* Handle fname as a regular expression. * If fname a simple header string, identify as such @@ -364,6 +379,30 @@ new->pnamereg =3D NULL; } } + /* + * This block must be after the identification of the parameters, = as + * the regex may be in an other position than the second one depen= ds + * of the special type. + */ + if ((simple_pattern =3D non_regex_pattern(cmd->pool, regex))) { + new->pattern =3D apr_strmatch_precompile(cmd->pool, + simple_pattern, !icase)= ; + if (new->pattern =3D=3D NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + " pattern could not be compiled.", NULL= ); + } + new->preg =3D NULL; + } + else { + new->preg =3D ap_pregcomp(cmd->pool, regex, + (REG_EXTENDED | (icase ? REG_ICASE : 0= ))); + if (new->preg =3D=3D NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + " regex could not be compiled.", NULL); + } + new->pattern =3D NULL; + } + new->features =3D apr_table_make(cmd->pool, 2); } else { new =3D &entries[i]; @@ -435,6 +474,95 @@ }; =20 /* + * This routine get from a header (or subprocess_env if no match in header= ), + * the value that correspond to a header name or a header matching regexp. + * (according to a sei_entry). + */ +static void get_header_val(sei_entry *b, apr_table_t *headers, apr_table_t= *subprocess_env, char const **val, request_rec *r) { + const apr_table_entry_t *elts; + /* Matching headers_in against a regex. Iterate through + * the headers until we find a match or run out of + * headers. + */ + const apr_array_header_t *arr =3D apr_table_elts(headers); + int j; + + + if (b->pnamereg) { + elts =3D (const apr_table_entry_t *) arr->elts; + *val =3D NULL; + for (j =3D 0; j < arr->nelts; ++j) { + if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {=20 + *val =3D elts[j].val; + /* + * Jok: either we may break or add a condition NULL + * !=3D val in the for loop cond ? + * or maybe concatene ?!? + */ + } + } + } + else { + /* Not matching against a regex */ + *val =3D apr_table_get(headers, b->name); + if (*val =3D=3D NULL) { + *val =3D apr_table_get(subprocess_env, b->name); + } + } +} + + +/* + * This routine set environment variable according to val. + * Question : why sei_entry's not const ? because ap_regexec is not define= d as + * well... pcreposix does not define the use of the regex as not modifying= it? + */ +static void setenvif_val_match(request_rec *r, const char *val, apr_size_t + val_len, sei_entry *b) { + regmatch_t regm[AP_MAX_REG_MATCH]; + const apr_table_entry_t *elts; + int j; + + /* + * 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 =3D=3D NULL) { + val =3D ""; + val_len =3D 0; + } + + if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) || + (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, re= gm, + 0))) { + const apr_array_header_t *arr =3D apr_table_elts(b->features); + elts =3D (const apr_table_entry_t *) arr->elts; + + for (j =3D 0; j < arr->nelts; ++j) { + if (*(elts[j].val) =3D=3D '!') { + apr_table_unset(r->subprocess_env, elts[j].key); + } + else { + if (!b->pattern) { + char *replaced =3D ap_pregsub(r->pool, elts[j].val, va= l, + AP_MAX_REG_MATCH, regm); + if (replaced) { + apr_table_setn(r->subprocess_env, elts[j].key, + replaced); + } + } + else { + apr_table_setn(r->subprocess_env, elts[j].key, + elts[j].val); + } + } + } + } +} + +/* * This routine gets called at two different points in request processing: * once before the URI has been translated (during the post-read-request * phase) and once after (during the header-parse phase). We use differen= t @@ -447,12 +575,10 @@ { sei_cfg_rec *sconf; sei_entry *entries; - const apr_table_entry_t *elts; const char *val; apr_size_t val_len =3D 0; - int i, j; + int i; char *last_name; - regmatch_t regm[AP_MAX_REG_MATCH]; =20 if (!ap_get_module_config(r->request_config, &setenvif_module)) { ap_set_module_config(r->request_config, &setenvif_module, @@ -466,10 +592,11 @@ } entries =3D (sei_entry *) sconf->conditionals->elts; last_name =3D NULL; - val =3D NULL; + for (i =3D 0; i < sconf->conditionals->nelts; ++i) { sei_entry *b =3D &entries[i]; =20 + val =3D NULL; /* 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 @@ -498,79 +625,73 @@ val =3D r->protocol; break; case SPECIAL_NOT: - if (b->pnamereg) { - /* Matching headers_in against a regex. Iterate throug= h - * the headers_in until we find a match or run out of - * headers. - */ - const apr_array_header_t - *arr =3D apr_table_elts(r->headers_in); - - elts =3D (const apr_table_entry_t *) arr->elts; - val =3D NULL; - for (j =3D 0; j < arr->nelts; ++j) { - if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL,= 0)) {=20 - val =3D elts[j].val; - } - } - } - else { - /* Not matching against a regex */ - val =3D apr_table_get(r->headers_in, b->name); - if (val =3D=3D NULL) { - val =3D apr_table_get(r->subprocess_env, b->name); - } - } + get_header_val(b, r->headers_in, r->subprocess_env, &val, = r); } val_len =3D val ? strlen(val) : 0; } =20 - /* - * A NULL value indicates that the header field or special entity - * wasn't present or is undefined. Represent that as an empty str= ing - * so that REs like "^$" will work and allow envariable setting - * based on missing or empty field. - */ - if (val =3D=3D NULL) { - val =3D ""; - val_len =3D 0; - } + setenvif_val_match(r, val, val_len, b); =20 - if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) || - (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, re= gm, - 0))) { - const apr_array_header_t *arr =3D apr_table_elts(b->features); - elts =3D (const apr_table_entry_t *) arr->elts; + } =20 - for (j =3D 0; j < arr->nelts; ++j) { - if (*(elts[j].val) =3D=3D '!') { - apr_table_unset(r->subprocess_env, elts[j].key); - } - else { - if (!b->pattern) { - char *replaced =3D ap_pregsub(r->pool, elts[j].val= , val, - AP_MAX_REG_MATCH, regm= ); - if (replaced) { - apr_table_setn(r->subprocess_env, elts[j].key, - replaced); - } - } - else { - apr_table_setn(r->subprocess_env, elts[j].key, - elts[j].val); - } - } + if (sconf->output_filter_on =3D=3D 1) + ap_add_output_filter(SEI_OUTPUT_FILTER_NAME, sconf, r, r->connecti= on); + =20 + return DECLINED; +} + +/* + * This (output) filter process headers_out to find condition to set env. + */ +static apr_status_t setenvif_out_filter(ap_filter_t * f, apr_bucket_brigad= e * bb) +{ + sei_cfg_rec *sconf; + sei_entry *entries; + const char *val; + apr_size_t val_len =3D 0; + int i; + char *last_name; + + sconf =3D (sei_cfg_rec *) f->ctx; + entries =3D (sei_entry *) sconf->conditionals->elts; + last_name =3D NULL; + for (i =3D 0; i < sconf->conditionals->nelts; ++i) { + sei_entry *b =3D &entries[i]; + + val =3D NULL; + /* + * 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 !=3D last_name) { + last_name =3D b->name; + if (b->special_type =3D=3D SPECIAL_OUTPUT_HEADER) { + /* + * As output filter, only headers_out are interresting for= us + * so we just process this type of conditions. + */ + get_header_val(b, f->r->headers_out, f->r->subprocess_env,= &val, f->r); + val_len =3D val ? strlen(val) : 0; + setenvif_val_match(f->r, val, val_len, b); } } } =20 - return DECLINED; + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); } =20 static void register_hooks(apr_pool_t *p) { ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE); + /* + * ftype is set to AP_FTYPE_CONTENT_SET + 1 to make this filter hooked + * before deflate (it's specific for the moment). + */ + ap_register_output_filter(SEI_OUTPUT_FILTER_NAME, setenvif_out_filter,= NULL, AP_FTYPE_CONTENT_SET); } =20 module AP_MODULE_DECLARE_DATA setenvif_module =3D --=-xPs1CBrZR6Eeg6kN7/sU Content-Disposition: attachment; filename=setenvif_output_header_commented_patch.txt Content-Type: text/plain; name=setenvif_output_header_commented_patch.txt; charset=UTF-8 Content-Transfer-Encoding: quoted-printable --- mod_setenvif.c.orig 2004-09-24 15:20:57.000000000 +0200 +++ mod_setenvif.c 2004-09-24 15:40:59.000000000 +0200 # I've added a new type of condition to clearly avoid mistake between input # headers (SPECIAL_NOT) and output headers (SPECIAL_OUTPUT_HEADER). @@ -101,7 +101,8 @@ SPECIAL_REQUEST_URI, SPECIAL_REQUEST_METHOD, SPECIAL_REQUEST_PROTOCOL, - SPECIAL_SERVER_ADDR + SPECIAL_SERVER_ADDR, + SPECIAL_OUTPUT_HEADER }; typedef struct { char *name; /* header name */ # Here, is added a boolean/int that indicate if this module may act as an # output filter or not (will be useful to add the filter when processing # request). @@ -116,6 +117,7 @@ =20 typedef struct { apr_array_header_t *conditionals; + int output_filter_on; /* act as output_filter for headers_out ?= */ } sei_cfg_rec; =20 module AP_MODULE_DECLARE_DATA setenvif_module; # Set by default the server configuration to not make it act as a filter. @@ -134,6 +136,7 @@ sei_cfg_rec *new =3D (sei_cfg_rec *) apr_palloc(p, sizeof(sei_cfg_rec)= ); =20 new->conditionals =3D apr_array_make(p, 20, sizeof(sei_entry)); + new->output_filter_on =3D 0; return (void *) new; } =20 # Include the process of the boolean output_filter_on in the merging functi= on. @@ -154,6 +157,8 @@ =20 a->conditionals =3D apr_array_append(p, base->conditionals, overrides->conditionals); + if(base->output_filter_on || overrides->output_filter_on) + a->output_filter_on =3D 1; return a; } =20 # Add a macro to define the name of the new output filter. @@ -163,6 +168,7 @@ */ #define ICASE_MAGIC ((void *)(&setenvif_module)) #define SEI_MAGIC_HEIRLOOM "setenvif-phase-flag" +#define SEI_OUTPUT_FILTER_NAME "setenvif-output-filter" =20 static int is_header_regex(apr_pool_t *p, const char* name)=20 { # Move this code below the parsing of the key word as explained in the comm= ent # The regex is the 3rd word when we use the keyword "output_header". # because we keep the same syntax for output headers as for input header # string ie:=20 # SetEnvIf output_header Content-Type .*gif.* no-gzip # must be parsed the same way as : # SetEnvIf Accept-Type .*no-gzip.* no-gzip # Then we redefine new->name and new->regex when we encounter output_header # key word. @@ -306,26 +312,6 @@ new->name =3D fname; new->regex =3D regex; new->icase =3D icase; - if ((simple_pattern =3D non_regex_pattern(cmd->pool, regex))) { - new->pattern =3D apr_strmatch_precompile(cmd->pool, - simple_pattern, !icase)= ; - if (new->pattern =3D=3D NULL) { - return apr_pstrcat(cmd->pool, cmd->cmd->name, - " pattern could not be compiled.", NULL= ); - } - new->preg =3D NULL; - } - else { - new->preg =3D ap_pregcomp(cmd->pool, regex, - (REG_EXTENDED | (icase ? REG_ICASE : 0= ))); - if (new->preg =3D=3D NULL) { - return apr_pstrcat(cmd->pool, cmd->cmd->name, - " regex could not be compiled.", NULL); - } - new->pattern =3D NULL; - } - new->features =3D apr_table_make(cmd->pool, 2); - if (!strcasecmp(fname, "remote_addr")) { new->special_type =3D SPECIAL_REMOTE_ADDR; } # It is a basic rip of the last else {} block to set correctly the regexps = to # match header name and content. @@ -344,7 +330,36 @@ else if (!strcasecmp(fname, "server_addr")) { new->special_type =3D SPECIAL_SERVER_ADDR; } - else { + else if (!strcasecmp(fname, "output_header")) { + new->special_type =3D SPECIAL_OUTPUT_HEADER; + sconf->output_filter_on =3D 1; + /* + * The regex is the 3rd word here, because we keep the same sy= ntax + * as for header string, but we added the output_header key wo= rd to + * keep backward compatibility. (to avoid the matching of bot= h + * request and response header on a regex). + * In this case the 2nd param of the directive is the header + * regex/name. + */ + new->name =3D regex; + if (is_header_regex(cmd->pool, new->name)) { + new->pnamereg =3D ap_pregcomp(cmd->pool, new->name, (REG_E= XTENDED | + REG_NOSUB | (icase ? REG_ICASE : 0))); + if (new->pnamereg =3D=3D NULL) + return apr_pstrcat(cmd->pool, cmd->cmd->name, "Header = name + regex could not be compiled.", NUL= L); + } + else { + new->pnamereg =3D NULL; + } + regex =3D ap_getword_conf(cmd->pool, &args); + if (!*regex) { + return apr_pstrcat(cmd->pool, "Missing regular expression = for ", + cmd->cmd->name, NULL); + } + new->regex =3D regex; + } + else { new->special_type =3D SPECIAL_NOT; /* Handle fname as a regular expression. * If fname a simple header string, identify as such # This is the block of code we move from the top of this block. @@ -364,6 +379,30 @@ new->pnamereg =3D NULL; } } + /* + * This block must be after the identification of the parameters, = as + * the regex may be in an other position than the second one depen= ds + * of the special type. + */ + if ((simple_pattern =3D non_regex_pattern(cmd->pool, regex))) { + new->pattern =3D apr_strmatch_precompile(cmd->pool, + simple_pattern, !icase)= ; + if (new->pattern =3D=3D NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + " pattern could not be compiled.", NULL= ); + } + new->preg =3D NULL; + } + else { + new->preg =3D ap_pregcomp(cmd->pool, regex, + (REG_EXTENDED | (icase ? REG_ICASE : 0= ))); + if (new->preg =3D=3D NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + " regex could not be compiled.", NULL); + } + new->pattern =3D NULL; + } + new->features =3D apr_table_make(cmd->pool, 2); } else { new =3D &entries[i]; # This block of code are two functions out-sourced from match_headers # function. I basically out-sourced them to avoid code duplication from the # request hook in the filter. @@ -435,6 +474,95 @@ }; =20 /* + * This routine get from a header (or subprocess_env if no match in header= ), + * the value that correspond to a header name or a header matching regexp. + * (according to a sei_entry). + */ +static void get_header_val(sei_entry *b, apr_table_t *headers, apr_table_t= *subprocess_env, char const **val, request_rec *r) { + const apr_table_entry_t *elts; + /* Matching headers_in against a regex. Iterate through + * the headers until we find a match or run out of + * headers. + */ + const apr_array_header_t *arr =3D apr_table_elts(headers); + int j; + + + if (b->pnamereg) { + elts =3D (const apr_table_entry_t *) arr->elts; + *val =3D NULL; + for (j =3D 0; j < arr->nelts; ++j) { + if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {=20 + *val =3D elts[j].val; + /* + * Jok: either we may break or add a condition NULL + * !=3D val in the for loop cond ? + * or maybe concatene ?!? + */ + } + } + } + else { + /* Not matching against a regex */ + *val =3D apr_table_get(headers, b->name); + if (*val =3D=3D NULL) { + *val =3D apr_table_get(subprocess_env, b->name); + } + } +} + + +/* + * This routine set environment variable according to val. + * Question : why sei_entry's not const ? because ap_regexec is not define= d as + * well... pcreposix does not define the use of the regex as not modifying= it? + */ +static void setenvif_val_match(request_rec *r, const char *val, apr_size_t + val_len, sei_entry *b) { + regmatch_t regm[AP_MAX_REG_MATCH]; + const apr_table_entry_t *elts; + int j; + + /* + * 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 =3D=3D NULL) { + val =3D ""; + val_len =3D 0; + } + + if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) || + (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, re= gm, + 0))) { + const apr_array_header_t *arr =3D apr_table_elts(b->features); + elts =3D (const apr_table_entry_t *) arr->elts; + + for (j =3D 0; j < arr->nelts; ++j) { + if (*(elts[j].val) =3D=3D '!') { + apr_table_unset(r->subprocess_env, elts[j].key); + } + else { + if (!b->pattern) { + char *replaced =3D ap_pregsub(r->pool, elts[j].val, va= l, + AP_MAX_REG_MATCH, regm); + if (replaced) { + apr_table_setn(r->subprocess_env, elts[j].key, + replaced); + } + } + else { + apr_table_setn(r->subprocess_env, elts[j].key, + elts[j].val); + } + } + } + } +} + +/* * This routine gets called at two different points in request processing: * once before the URI has been translated (during the post-read-request * phase) and once after (during the header-parse phase). We use differen= t # Removal of variable that were exclusivly in the functions out-sourced. @@ -447,12 +575,10 @@ { sei_cfg_rec *sconf; sei_entry *entries; - const apr_table_entry_t *elts; const char *val; apr_size_t val_len =3D 0; - int i, j; + int i; char *last_name; - regmatch_t regm[AP_MAX_REG_MATCH]; =20 if (!ap_get_module_config(r->request_config, &setenvif_module)) { ap_set_module_config(r->request_config, &setenvif_module, # The reset of val variable was in a function out-sourced so now it is set = in # the for loop. @@ -466,10 +592,11 @@ } entries =3D (sei_entry *) sconf->conditionals->elts; last_name =3D NULL; - val =3D NULL; + for (i =3D 0; i < sconf->conditionals->nelts; ++i) { sei_entry *b =3D &entries[i]; =20 + val =3D NULL; /* 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 # Out-sourced above. # at the end of this diff block are :=20 # 1/ the ap_add_output_filter if needed. # 2/ the filter function. # 3/ the ap_register_output_filter. @@ -498,79 +625,73 @@ val =3D r->protocol; break; case SPECIAL_NOT: - if (b->pnamereg) { - /* Matching headers_in against a regex. Iterate throug= h - * the headers_in until we find a match or run out of - * headers. - */ - const apr_array_header_t - *arr =3D apr_table_elts(r->headers_in); - - elts =3D (const apr_table_entry_t *) arr->elts; - val =3D NULL; - for (j =3D 0; j < arr->nelts; ++j) { - if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL,= 0)) {=20 - val =3D elts[j].val; - } - } - } - else { - /* Not matching against a regex */ - val =3D apr_table_get(r->headers_in, b->name); - if (val =3D=3D NULL) { - val =3D apr_table_get(r->subprocess_env, b->name); - } - } + get_header_val(b, r->headers_in, r->subprocess_env, &val, = r); } val_len =3D val ? strlen(val) : 0; } =20 - /* - * A NULL value indicates that the header field or special entity - * wasn't present or is undefined. Represent that as an empty str= ing - * so that REs like "^$" will work and allow envariable setting - * based on missing or empty field. - */ - if (val =3D=3D NULL) { - val =3D ""; - val_len =3D 0; - } + setenvif_val_match(r, val, val_len, b); =20 - if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) || - (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, re= gm, - 0))) { - const apr_array_header_t *arr =3D apr_table_elts(b->features); - elts =3D (const apr_table_entry_t *) arr->elts; + } =20 - for (j =3D 0; j < arr->nelts; ++j) { - if (*(elts[j].val) =3D=3D '!') { - apr_table_unset(r->subprocess_env, elts[j].key); - } - else { - if (!b->pattern) { - char *replaced =3D ap_pregsub(r->pool, elts[j].val= , val, - AP_MAX_REG_MATCH, regm= ); - if (replaced) { - apr_table_setn(r->subprocess_env, elts[j].key, - replaced); - } - } - else { - apr_table_setn(r->subprocess_env, elts[j].key, - elts[j].val); - } - } + if (sconf->output_filter_on =3D=3D 1) + ap_add_output_filter(SEI_OUTPUT_FILTER_NAME, sconf, r, r->connecti= on); + =20 + return DECLINED; +} + +/* + * This (output) filter process headers_out to find condition to set env. + */ +static apr_status_t setenvif_out_filter(ap_filter_t * f, apr_bucket_brigad= e * bb) +{ + sei_cfg_rec *sconf; + sei_entry *entries; + const char *val; + apr_size_t val_len =3D 0; + int i; + char *last_name; + + sconf =3D (sei_cfg_rec *) f->ctx; + entries =3D (sei_entry *) sconf->conditionals->elts; + last_name =3D NULL; + for (i =3D 0; i < sconf->conditionals->nelts; ++i) { + sei_entry *b =3D &entries[i]; + + val =3D NULL; + /* + * 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 !=3D last_name) { + last_name =3D b->name; + if (b->special_type =3D=3D SPECIAL_OUTPUT_HEADER) { + /* + * As output filter, only headers_out are interresting for= us + * so we just process this type of conditions. + */ + get_header_val(b, f->r->headers_out, f->r->subprocess_env,= &val, f->r); + val_len =3D val ? strlen(val) : 0; + setenvif_val_match(f->r, val, val_len, b); } } } =20 - return DECLINED; + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); } =20 static void register_hooks(apr_pool_t *p) { ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE); + /* + * ftype is set to AP_FTYPE_CONTENT_SET + 1 to make this filter hooked + * before deflate (it's specific for the moment). + */ + ap_register_output_filter(SEI_OUTPUT_FILTER_NAME, setenvif_out_filter,= NULL, AP_FTYPE_CONTENT_SET); } =20 module AP_MODULE_DECLARE_DATA setenvif_module =3D --=-xPs1CBrZR6Eeg6kN7/sU--