From 55ea7579d7fb8992bdf5bf08bdbfd1e872179cce Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sun, 17 Jun 2007 19:56:27 +0200 Subject: [PATCH] [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 {if|unless} --- include/types/proxy.h | 10 ++++++ src/cfgparse.c | 71 +++++++++++++++++++++++++++++++++++++++++++ src/proto_http.c | 23 ++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/include/types/proxy.h b/include/types/proxy.h index bfbe63125..6d87f806d 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -85,6 +85,7 @@ struct proxy { } 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 proxy { 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 77bd8277e..f3aefa0d5 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -510,6 +510,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args) 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 @@ int cfg_parse_listen(const char *file, int linenum, char **args) } 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 @@ int readcfgfile(const char *file) } while (curproxy != NULL) { + struct switching_rule *rule; + if (curproxy->state == PR_STSTOPPED) { curproxy = curproxy->next; continue; @@ -2479,6 +2518,38 @@ int readcfgfile(const char *file) } } } + + /* 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 ced4514e5..33f1a95b8 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -1888,6 +1888,29 @@ int process_cli(struct session *t) 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