[MAJOR] added the 'use_backend' keyword for full content-switching

The new "use_backend" keyword permits full content switching by the
use of ACLs. Its usage is simple :

   use_backend <backend_name> {if|unless} <acl_cond>
diff --git a/include/types/proxy.h b/include/types/proxy.h
index bfbe631..6d87f80 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -85,6 +85,7 @@
 	} defbe;
 	struct list acl;                        /* ACL declared on this proxy */
 	struct list block_cond;                 /* early blocking conditions (chained) */
+	struct list switching_rules;            /* content switching rules (chained) */
 	struct server *srv;			/* known servers */
 	int srv_act, srv_bck;			/* # of running servers */
 	int tot_wact, tot_wbck;			/* total weights of active and backup servers */
@@ -150,6 +151,15 @@
 	struct chunk errmsg[HTTP_ERR_SIZE];	/* default or customized error messages for known errors */
 };
 
+struct switching_rule {
+	struct list list;			/* list linked to from the proxy */
+	struct acl_cond *cond;			/* acl condition to meet */
+	union {
+		struct proxy *backend;		/* target backend */
+		char *name;			/* target backend name during config parsing */
+	} be;
+};
+
 extern struct proxy *proxy;
 
 #endif /* _TYPES_PROXY_H */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 77bd827..f3aefa0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -510,6 +510,7 @@
 		LIST_INIT(&curproxy->pendconns);
 		LIST_INIT(&curproxy->acl);
 		LIST_INIT(&curproxy->block_cond);
+		LIST_INIT(&curproxy->switching_rules);
 
 		/* Timeouts are defined as -1, so we cannot use the zeroed area
 		 * as a default value.
@@ -977,6 +978,42 @@
 		}
 		LIST_ADDQ(&curproxy->block_cond, &cond->list);
 	}
+	else if (!strcmp(args[0], "use_backend")) {  /* early blocking based on ACLs */
+		int pol = ACL_COND_NONE;
+		struct acl_cond *cond;
+		struct switching_rule *rule;
+
+		if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL))
+			return 0;
+
+		if (*(args[1]) == 0) {
+			Alert("parsing [%s:%d] : '%s' expects a backend name.\n", file, linenum, args[0]);
+			return -1;
+		}
+
+		if (!strcmp(args[2], "if"))
+			pol = ACL_COND_IF;
+		else if (!strcmp(args[2], "unless"))
+			pol = ACL_COND_UNLESS;
+
+		if (pol == ACL_COND_NONE) {
+			Alert("parsing [%s:%d] : '%s' requires either 'if' or 'unless' followed by a condition.\n",
+			      file, linenum, args[0]);
+			return -1;
+		}
+
+		if ((cond = parse_acl_cond((const char **)args + 3, &curproxy->acl, pol)) == NULL) {
+			Alert("parsing [%s:%d] : error detected while parsing blocking condition.\n",
+			      file, linenum);
+			return -1;
+		}
+
+		rule = (struct switching_rule *)calloc(1, sizeof(*rule));
+		rule->cond = cond;
+		rule->be.name = strdup(args[1]);
+		LIST_INIT(&rule->list);
+		LIST_ADDQ(&curproxy->switching_rules, &rule->list);
+	}
 	else if (!strcmp(args[0], "stats")) {
 		if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			return 0;
@@ -2344,6 +2381,8 @@
 	}
 
 	while (curproxy != NULL) {
+		struct switching_rule *rule;
+
 		if (curproxy->state == PR_STSTOPPED) {
 			curproxy = curproxy->next;
 			continue;
@@ -2479,6 +2518,38 @@
 				}
 			}
 		}
+
+		/* find the target proxy for 'use_backend' rules */
+		list_for_each_entry(rule, &curproxy->switching_rules, list) {
+			/* map jump target for ACT_SETBE in req_rep chain */ 
+			struct proxy *target;
+
+			for (target = proxy; target != NULL; target = target->next) {
+				if (strcmp(target->id, rule->be.name) == 0)
+					break;
+			}
+
+			if (target == NULL) {
+				Alert("parsing %s : backend '%s' in HTTP %s '%s' was not found !\n", 
+				      file, rule->be.name, proxy_type_str(curproxy), curproxy->id);
+				cfgerr++;
+			} else if (target == curproxy) {
+				Alert("parsing %s : loop detected for backend %s !\n", file, rule->be.name);
+				cfgerr++;
+			} else if (!(target->cap & PR_CAP_BE)) {
+				Alert("parsing %s : target '%s' in HTTP %s '%s' has no backend capability !\n",
+				      file, rule->be.name, proxy_type_str(curproxy), curproxy->id);
+				cfgerr++;
+			} else if (target->mode != curproxy->mode) {
+				Alert("parsing %s : backend '%s' referenced in %s '%s' is of different mode !\n",
+				      file, rule->be.name, proxy_type_str(curproxy), curproxy->id);
+				cfgerr++;
+			} else {
+				free((void *)rule->be.name);
+				rule->be.backend = target;
+			}
+		}
+
 		if ((curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) &&
 		    (((curproxy->cap & PR_CAP_FE) && !tv_isset(&curproxy->clitimeout)) ||
 		     ((curproxy->cap & PR_CAP_BE) && (curproxy->srv) &&
diff --git a/src/proto_http.c b/src/proto_http.c
index ced4514..33f1a95 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1888,6 +1888,29 @@
 					return 1;
 			}
 
+			/* now check whether we have some switching rules for this request */
+			if (!(t->flags & SN_BE_ASSIGNED)) {
+				struct switching_rule *rule;
+
+				list_for_each_entry(rule, &cur_proxy->switching_rules, list) {
+					int ret;
+
+					ret = acl_exec_cond(rule->cond, cur_proxy, t, txn, ACL_DIR_REQ);
+					if (cond->pol == ACL_COND_UNLESS)
+						ret = !ret;
+
+					if (ret) {
+						t->be = rule->be.backend;
+						t->be->beconn++;
+						if (t->be->beconn > t->be->beconn_max)
+							t->be->beconn_max = t->be->beconn;
+						t->be->cum_beconn++;
+						t->flags |= SN_BE_ASSIGNED;
+						break;
+					}
+				}
+			}
+
 			if (!(t->flags & SN_BE_ASSIGNED) && cur_proxy->defbe.be) {
 				/* No backend was set, but there was a default
 				 * backend set in the frontend, so we use it and