]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] added the 'use_backend' keyword for full content-switching
authorWilly Tarreau <w@1wt.eu>
Sun, 17 Jun 2007 17:56:27 +0000 (19:56 +0200)
committerWilly Tarreau <w@1wt.eu>
Sun, 17 Jun 2007 17:56:27 +0000 (19:56 +0200)
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>

include/types/proxy.h
src/cfgparse.c
src/proto_http.c

index bfbe631251634d422484e8b27b499a3d18bf311e..6d87f806da9cec24d0f86c61a84a9c3a76841a35 100644 (file)
@@ -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 */
index 77bd8277e904fe8c75967a2c33cf11fa02c33481..f3aefa0d551b425649fccf91795b046fb3cc5f29 100644 (file)
@@ -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) &&
index ced4514e5723aef00f3c94d99f845b2e6e16ea28..33f1a95b81f808fc426630905dc4ce0dbfdfb403 100644 (file)
@@ -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