[MEDIUM] http: add support for conditional response header rewriting

Just as for the req* rules, we can now condition rsp* rules with ACLs.
ACLs match on response, so volatile request information cannot be used.
A warning is emitted if a configuration contains such an anomaly.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index c7f549d..77e61d8 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -405,6 +405,23 @@
 	return ERR_WARN;
 }
 
+/* Report it if a request ACL condition uses some request-only volatile parameters.
+ * It returns either 0 or ERR_WARN so that its result can be or'ed with err_code.
+ * Note that <cond> may be NULL and then will be ignored.
+ */
+static int warnif_cond_requires_req(const struct acl_cond *cond, const char *file, int line)
+{
+	struct acl *acl;
+
+	if (!cond || !(cond->requires & ACL_USE_REQ_VOLATILE))
+		return 0;
+
+	acl = cond_find_require(cond, ACL_USE_REQ_VOLATILE);
+	Warning("parsing [%s:%d] : acl '%s' involves some volatile request-only criteria which will be ignored.\n",
+		file, line, acl ? acl->name : "(unknown)");
+	return ERR_WARN;
+}
+
 
 /*
  * parse a line in a <global> section. Returns the error code, 0 if OK, or
@@ -947,6 +964,8 @@
 
 	if (dir == ACL_DIR_REQ)
 		err_code |= warnif_cond_requires_resp(cond, file, line);
+	else
+		err_code |= warnif_cond_requires_req(cond, file, line);
 
 	preg = calloc(1, sizeof(regex_t));
 	if (!preg) {
@@ -3770,21 +3789,21 @@
 
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_RTR, ACT_REPLACE, 0,
-						   args[0], args[1], args[2], NULL);
+						   args[0], args[1], args[2], (const char **)args+3);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "rspdel")) {  /* delete response header from a regex */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_RTR, ACT_REMOVE, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "rspdeny")) {  /* block response header from a regex */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_RTR, ACT_DENY, 0,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
@@ -3798,21 +3817,21 @@
 
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_RTR, ACT_REPLACE, REG_ICASE,
-						   args[0], args[1], args[2], NULL);
+						   args[0], args[1], args[2], (const char **)args+3);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "rspidel")) {  /* delete response header from a regex ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_RTR, ACT_REMOVE, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
 	else if (!strcmp(args[0], "rspideny")) {  /* block response header from a regex ignoring case */
 		err_code |= create_cond_regex_rule(file, linenum, curproxy,
 						   ACL_DIR_RTR, ACT_DENY, REG_ICASE,
-						   args[0], args[1], NULL, NULL);
+						   args[0], args[1], NULL, (const char **)args+2);
 		if (err_code & ERR_FATAL)
 			goto out;
 	}
@@ -3833,7 +3852,24 @@
 			goto out;
 		}
 	
+		if ((strcmp(args[2], "if") == 0 || strcmp(args[2], "unless") == 0)) {
+			if ((cond = build_acl_cond(file, linenum, curproxy, (const char **)args+2)) == NULL) {
+				Alert("parsing [%s:%d] : error detected while parsing a '%s' condition.\n",
+				      file, linenum, args[0]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			err_code |= warnif_cond_requires_req(cond, file, linenum);
+		}
+		else if (*args[2]) {
+			Alert("parsing [%s:%d] : '%s' : Expecting nothing, 'if', or 'unless', got '%s'.\n",
+			      file, linenum, args[0], args[2]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
 		wl = calloc(1, sizeof(*wl));
+		wl->cond = cond;
 		wl->s = strdup(args[1]);
 		LIST_ADDQ(&curproxy->rsp_add, &wl->list);
 	}
diff --git a/src/proto_http.c b/src/proto_http.c
index 1ee2307..b2dd232 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4582,7 +4582,7 @@
 
 			/* try headers filters */
 			if (rule_set->rsp_exp != NULL) {
-				if (apply_filters_to_response(t, rep, rule_set->rsp_exp) < 0) {
+				if (apply_filters_to_response(t, rep, rule_set) < 0) {
 				return_bad_resp:
 					if (t->srv) {
 						t->srv->counters.failed_resp++;
@@ -4618,6 +4618,14 @@
 			list_for_each_entry(wl, &rule_set->rsp_add, list) {
 				if (txn->status < 200)
 					break;
+				if (wl->cond) {
+					int ret = acl_exec_cond(wl->cond, px, t, txn, ACL_DIR_RTR);
+					ret = acl_pass(ret);
+					if (((struct acl_cond *)wl->cond)->pol == ACL_COND_UNLESS)
+						ret = !ret;
+					if (!ret)
+						continue;
+				}
 				if (unlikely(http_header_add_tail(rep, &txn->rsp, &txn->hdr_idx, wl->s) < 0))
 					goto return_bad_resp;
 			}
@@ -5806,15 +5814,16 @@
 
 
 /*
- * Apply all the resp filters <exp> to all headers in buffer <rtr> of session <t>.
+ * Apply all the resp filters of proxy <px> to all headers in buffer <rtr> of session <s>.
  * Returns 0 if everything is alright, or -1 in case a replacement lead to an
  * unparsable response.
  */
-int apply_filters_to_response(struct session *t, struct buffer *rtr, struct hdr_exp *exp)
+int apply_filters_to_response(struct session *s, struct buffer *rtr, struct proxy *px)
 {
-	struct http_txn *txn = &t->txn;
-	/* iterate through the filters in the outer loop */
-	while (exp && !(txn->flags & TX_SVDENY)) {
+	struct http_txn *txn = &s->txn;
+	struct hdr_exp *exp;
+
+	for (exp = px->rsp_exp; exp; exp = exp->next) {
 		int ret;
 
 		/*
@@ -5823,6 +5832,9 @@
 		 * the evaluation.
 		 */
 
+		if (txn->flags & TX_SVDENY)
+			break;
+
 		if ((txn->flags & TX_SVALLOW) &&
 		    (exp->action == ACT_ALLOW || exp->action == ACT_DENY ||
 		     exp->action == ACT_PASS)) {
@@ -5830,8 +5842,20 @@
 			continue;
 		}
 
+		/* if this filter had a condition, evaluate it now and skip to
+		 * next filter if the condition does not match.
+		 */
+		if (exp->cond) {
+			ret = acl_exec_cond(exp->cond, px, s, txn, ACL_DIR_RTR);
+			ret = acl_pass(ret);
+			if (((struct acl_cond *)exp->cond)->pol == ACL_COND_UNLESS)
+				ret = !ret;
+			if (!ret)
+				continue;
+		}
+
 		/* Apply the filter to the status line. */
-		ret = apply_filter_to_sts_line(t, rtr, exp);
+		ret = apply_filter_to_sts_line(s, rtr, exp);
 		if (unlikely(ret < 0))
 			return -1;
 
@@ -5839,9 +5863,8 @@
 			/* The filter did not match the response, it can be
 			 * iterated through all headers.
 			 */
-			apply_filter_to_resp_headers(t, rtr, exp);
+			apply_filter_to_resp_headers(s, rtr, exp);
 		}
-		exp = exp->next;
 	}
 	return 0;
 }