This patch add a filter to mod_setenvif that lets it match against
response headers as well as request headers. It is probably a horrible
implementation, but submitted to encourage others to think of the idea.
This changes the configuration to allow another optional field to
designate the "mode" of the match (default is request)
SetEnvIf response Content-Type text/* is_text=1
will match against the response header content-type.
The main purpose of this is to allow configurations such as:
AddOutputFilterByType DEFLATE text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
SetEnvIf response Content-Type text/html user-agent-vary=1
Header append Vary User-Agent env=user-agent-vary
With this patch, the correct vary headers are added in a reverse proxy
situation.
most of the code was adapted from mod_headers.
Thoughts?
--
Brian Akins
Lead Systems Engineer
CNN Internet Technologies
--- mod_setenvif.c.bak 2006-05-23 10:08:56.000000000 -0400
+++ mod_setenvif.c 2006-05-23 11:03:06.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,
@@ -113,12 +115,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 +254,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 +309,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;
@@ -400,15 +406,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 +444,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 +470,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 +480,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 +501,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
@@ -505,7 +546,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 +558,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);
}
@@ -542,7 +583,10 @@
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", b->name);
+
for (j = 0; j < arr->nelts; ++j) {
if (*(elts[j].val) == '!') {
apr_table_unset(r->subprocess_env, elts[j].key);
@@ -568,10 +612,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 =