diff --git a/doc/configuration.txt b/doc/configuration.txt index babcefcafe..bc582c29e0 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2578,7 +2578,9 @@ fullconn Since it's hard to get this value right, haproxy automatically sets it to 10% of the sum of the maxconns of all frontends that may branch to this - backend. That way it's safe to leave it unset. + backend (based on "use_backend" and "default_backend" rules). That way it's + safe to leave it unset. However, "use_backend" involving dynamic names are + not counted since there is no way to know if they could match or not. Example : # The servers will accept between 100 and 1000 concurrent connections each @@ -7711,7 +7713,8 @@ use_backend unless May be used in sections : defaults | frontend | listen | backend no | yes | yes | no Arguments : - is the name of a valid backend or "listen" section. + is the name of a valid backend or "listen" section, or a + "log-format" string resolving to a backend name. is a condition composed of ACLs, as described in section 7. @@ -7740,7 +7743,22 @@ use_backend unless a complete HTTP request to get in. This feature is useful when a frontend must decode several protocols on a unique port, one of them being HTTP. - See also: "default_backend", "tcp-request", and section 7 about ACLs. + When is a simple name, it is resolved at configuration time, and an + error is reported if the specified backend does not exist. If is + a log-format string instead, no check may be done at configuration time, so + the backend name is resolved dynamically at run time. If the resulting + backend name does not correspond to any valid backend, no other rule is + evaluated, and the default_backend directive is applied instead. Note that + when using dynamic backend names, it is highly recommended to use a prefix + that no other backend uses in order to ensure that an unauthorized backend + cannot be forced from the request. + + It is worth mentionning that "use_backend" rules with an explicit name are + used to detect the association between frontends and backends to compute the + backend's "fullconn" setting. This cannot be done for dynamic names. + + See also: "default_backend", "tcp-request", "fullconn", "log-format", and + section 7 about ACLs. use-server if diff --git a/include/types/proxy.h b/include/types/proxy.h index 1a778bd911..dc75f63dbd 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -379,9 +379,11 @@ struct proxy { struct switching_rule { struct list list; /* list linked to from the proxy */ struct acl_cond *cond; /* acl condition to meet */ + int dynamic; /* this is a dynamic rule using the logformat expression */ union { struct proxy *backend; /* target backend */ char *name; /* target backend name during config parsing */ + struct list expr; /* logformat expression to use for dynamic rules */ } be; }; diff --git a/src/cfgparse.c b/src/cfgparse.c index 9f91f2813a..e433c2c275 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -6813,6 +6813,33 @@ int check_config_validity() /* find the target proxy for 'use_backend' rules */ list_for_each_entry(rule, &curproxy->switching_rules, list) { struct proxy *target; + struct logformat_node *node; + char *pxname; + + /* Try to parse the string as a log format expression. If the result + * of the parsing is only one entry containing a simple string, then + * it's a standard string corresponding to a static rule, thus the + * parsing is cancelled and be.name is restored to be resolved. + */ + pxname = rule->be.name; + LIST_INIT(&rule->be.expr); + parse_logformat_string(pxname, curproxy, &rule->be.expr, 0, SMP_VAL_FE_HRQ_HDR, + curproxy->conf.args.file, curproxy->conf.args.line); + node = LIST_NEXT(&rule->be.expr, struct logformat_node *, list); + + if (!LIST_ISEMPTY(&rule->be.expr)) { + if (node->type != LOG_FMT_TEXT || node->list.n != &rule->be.expr) { + rule->dynamic = 1; + free(pxname); + continue; + } + /* simple string: free the expression and fall back to static rule */ + free(node->arg); + free(node); + } + + rule->dynamic = 0; + rule->be.name = pxname; target = findproxy_mode(rule->be.name, curproxy->mode, PR_CAP_BE); @@ -7722,7 +7749,7 @@ int check_config_validity() /* check if a "use_backend" rule matches */ if (!found) { list_for_each_entry(rule, &fe->switching_rules, list) { - if (rule->be.backend == curproxy) { + if (!rule->dynamic && rule->be.backend == curproxy) { found = 1; break; } diff --git a/src/session.c b/src/session.c index a8f68a29c3..efc0736ec0 100644 --- a/src/session.c +++ b/src/session.c @@ -1244,7 +1244,24 @@ static int process_switching_rules(struct session *s, struct channel *req, int a ret = !ret; if (ret) { - if (!session_set_backend(s, rule->be.backend)) + /* If the backend name is dynamic, try to resolve the name. + * If we can't resolve the name, or if any error occurs, break + * the loop and fallback to the default backend. + */ + struct proxy *backend; + + if (rule->dynamic) { + struct chunk *tmp = get_trash_chunk(); + if (!build_logline(s, tmp->str, tmp->size, &rule->be.expr)) + break; + backend = findproxy(tmp->str, PR_CAP_BE); + if (!backend) + break; + } + else + backend = rule->be.backend; + + if (!session_set_backend(s, backend)) goto sw_failed; break; }