You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Brian Akins <ba...@web.turner.com> on 2006/09/11 13:09:10 UTC
Re: [PATCH] setenvif filter
Any thoughts on this? This has been brought up a few times.
Good usage example, I need to be able to set a header based upon a
reponse's content type. I would like to be able to do:
SetEnvIf response Content-Type text/html is_html=1
Header append Cache-Control private env=is_html
But there seems to be no intuitive way with current code...
Brian Akins wrote:
> Here's a newer version with some special handling for content-type. In
> response headers, we need to match r->content_type rather than the header.
>
>
>
> ------------------------------------------------------------------------
>
> --- mod_setenvif.c.bak 2006-05-23 10:08:56.000000000 -0400
> +++ mod_setenvif.c 2006-05-23 16:55:50.000000000 -0400
> @@ -94,6 +94,8 @@
> #include "http_log.h"
> #include "http_protocol.h"
>
> +#define SETENVIF_REQUEST 1
> +#define SETENVIF_RESPONSE 2
>
> enum special {
> SPECIAL_NOT,
> @@ -102,7 +104,8 @@
> SPECIAL_REQUEST_URI,
> SPECIAL_REQUEST_METHOD,
> SPECIAL_REQUEST_PROTOCOL,
> - SPECIAL_SERVER_ADDR
> + SPECIAL_SERVER_ADDR,
> + SPECIAL_CONTENT_TYPE
> };
> typedef struct {
> char *name; /* header name */
> @@ -113,12 +116,15 @@
> apr_table_t *features; /* env vars to set (or unset) */
> enum special special_type; /* is it a "special" header ? */
> int icase; /* ignoring case? */
> + int mode; /*request or response*/
> } sei_entry;
>
> typedef struct {
> apr_array_header_t *conditionals;
> } sei_cfg_rec;
>
> +static ap_filter_rec_t *setenvif_output_filter_handle = NULL;
> +
> module AP_MODULE_DECLARE_DATA setenvif_module;
>
> /*
> @@ -249,7 +255,7 @@
> }
>
> static const char *add_setenvif_core(cmd_parms *cmd, void *mconfig,
> - char *fname, const char *args)
> + char *fname, int mode, const char *args)
> {
> char *regex;
> const char *simple_pattern;
> @@ -304,6 +310,7 @@
>
> /* no match, create a new entry */
> new = apr_array_push(sconf->conditionals);
> + new->mode = mode;
> new->name = fname;
> new->regex = regex;
> new->icase = icase;
> @@ -345,6 +352,9 @@
> else if (!strcasecmp(fname, "server_addr")) {
> new->special_type = SPECIAL_SERVER_ADDR;
> }
> + else if ((SETENVIF_RESPONSE == mode) && !strcasecmp(fname, "content-type")) {
> + new->special_type = SPECIAL_CONTENT_TYPE;
> + }
> else {
> new->special_type = SPECIAL_NOT;
> /* Handle fname as a regular expression.
> @@ -400,15 +410,35 @@
> static const char *add_setenvif(cmd_parms *cmd, void *mconfig,
> const char *args)
> {
> - char *fname;
> -
> + char *fname = NULL;
> + int mode = SETENVIF_REQUEST;
> +
> /* get header name */
> fname = ap_getword_conf(cmd->pool, &args);
> - if (!*fname) {
> + /*is this a mode?*/
> +
> + if (!fname) {
> + return apr_pstrcat(cmd->pool, "Missing header-field name for ",
> + cmd->cmd->name, NULL);
> + }
> +
> + if(!strcasecmp(fname, "request")) {
> + mode = SETENVIF_REQUEST;
> + fname = NULL;
> + } else if (!strcasecmp(fname, "response")) {
> + mode = SETENVIF_RESPONSE;
> + fname = NULL;
> + }
> +
> + if(!fname) {
> + fname = ap_getword_conf(cmd->pool, &args);
> + }
> +
> + if (!fname) {
> return apr_pstrcat(cmd->pool, "Missing header-field name for ",
> cmd->cmd->name, NULL);
> }
> - return add_setenvif_core(cmd, mconfig, fname, args);
> + return add_setenvif_core(cmd, mconfig, fname, mode, args);
> }
>
> /*
> @@ -418,7 +448,7 @@
> */
> static const char *add_browser(cmd_parms *cmd, void *mconfig, const char *args)
> {
> - return add_setenvif_core(cmd, mconfig, "User-Agent", args);
> + return add_setenvif_core(cmd, mconfig, "User-Agent", SETENVIF_REQUEST, args);
> }
>
> static const command_rec setenvif_module_cmds[] =
> @@ -444,7 +474,7 @@
> * signal which call it is by having the earlier one pass a flag to the
> * later one.
> */
> -static int match_headers(request_rec *r)
> +static int match_headers(request_rec *r, int mode)
> {
> sei_cfg_rec *sconf;
> sei_entry *entries;
> @@ -454,7 +484,14 @@
> int i, j;
> char *last_name;
> ap_regmatch_t regm[AP_MAX_REG_MATCH];
> -
> + apr_table_t *headers;
> +
> + if(SETENVIF_RESPONSE == mode) {
> + headers = r->headers_out;
> + } else {
> + headers = r->headers_in;
> + }
> +
> if (!ap_get_module_config(r->request_config, &setenvif_module)) {
> ap_set_module_config(r->request_config, &setenvif_module,
> SEI_MAGIC_HEIRLOOM);
> @@ -468,9 +505,17 @@
> entries = (sei_entry *) sconf->conditionals->elts;
> last_name = NULL;
> val = NULL;
> +
> for (i = 0; i < sconf->conditionals->nelts; ++i) {
> sei_entry *b = &entries[i];
> -
> +
> + if(b->mode != mode) {
> + continue;
> + }
> +
> + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
> + "setenvif: trying %s", b->name);
> +
> /* 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,6 +543,10 @@
> case SPECIAL_REQUEST_PROTOCOL:
> val = r->protocol;
> break;
> + /*we only set this in config in !RESPONSE*/
> + case SPECIAL_CONTENT_TYPE:
> + val = r->content_type;
> + break;
> case SPECIAL_NOT:
> if (b->pnamereg) {
> /* Matching headers_in against a regex. Iterate through
> @@ -505,7 +554,7 @@
> * headers.
> */
> const apr_array_header_t
> - *arr = apr_table_elts(r->headers_in);
> + *arr = apr_table_elts(headers);
>
> elts = (const apr_table_entry_t *) arr->elts;
> val = NULL;
> @@ -517,7 +566,7 @@
> }
> else {
> /* Not matching against a regex */
> - val = apr_table_get(r->headers_in, b->name);
> + val = apr_table_get(headers, b->name);
> if (val == NULL) {
> val = apr_table_get(r->subprocess_env, b->name);
> }
> @@ -537,12 +586,19 @@
> val_len = 0;
> }
>
> + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
> + "setenvif: val = %s", val);
> +
> +
> if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
> (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
> 0))) {
> const apr_array_header_t *arr = apr_table_elts(b->features);
> elts = (const apr_table_entry_t *) arr->elts;
> -
> +
> + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
> + "setenvif: matched: %s, %s", b->name, val);
> +
> for (j = 0; j < arr->nelts; ++j) {
> if (*(elts[j].val) == '!') {
> apr_table_unset(r->subprocess_env, elts[j].key);
> @@ -568,10 +624,57 @@
> return DECLINED;
> }
>
> +static int setenvif_request(request_rec *r)
> +{
> + return match_headers(r, SETENVIF_REQUEST);
> +
> +}
> +static apr_status_t setenvif_output_filter(ap_filter_t *f,
> + apr_bucket_brigade *in)
> +{
> + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
> + "setenvif: setenvif_output_filter()");
> +
> + ap_set_module_config(f->r->request_config, &setenvif_module,
> + NULL);
> + match_headers(f->r, SETENVIF_RESPONSE);
> + /*have to run twice, once to get server, then dir*/
> + match_headers(f->r, SETENVIF_RESPONSE);
> + /* remove ourselves from the filter chain */
> + ap_remove_output_filter(f);
> +
> + /* send the data up the stack */
> + return ap_pass_brigade(f->next,in);
> +}
> +
> +static void setenvif_insert_output_filter(request_rec *r)
> +{
> + sei_cfg_rec *sconf;
> + int count = 0;
> +
> + /*only add it if we have some*/
> + sconf = (sei_cfg_rec *) ap_get_module_config(r->server->module_config,
> + &setenvif_module);
> + count += sconf->conditionals->nelts;
> +
> + sconf = (sei_cfg_rec *) ap_get_module_config(r->per_dir_config,
> + &setenvif_module);
> + count += sconf->conditionals->nelts;
> +
> + if(count) {
> + ap_add_output_filter_handle(setenvif_output_filter_handle, NULL, r,
> + r->connection);
> + }
> +}
> +
> 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);
> + ap_hook_header_parser(setenvif_request, NULL, NULL, APR_HOOK_MIDDLE);
> + ap_hook_post_read_request(setenvif_request, NULL, NULL, APR_HOOK_MIDDLE);
> + setenvif_output_filter_handle = ap_register_output_filter("SETENVIF_OUT",
> + setenvif_output_filter,
> + NULL, AP_FTYPE_CONTENT_SET);
> + ap_hook_insert_filter(setenvif_insert_output_filter, NULL, NULL, APR_HOOK_LAST);
> }
>
> module AP_MODULE_DECLARE_DATA setenvif_module =
--
Brian Akins
Chief Operations Engineer
Turner Digital Media Technologies