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