[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