diff --git a/doc/configuration.txt b/doc/configuration.txt index 8eb3f8e84..8347e8a4d 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -11141,7 +11141,8 @@ use-server unless May be used in sections : defaults | frontend | listen | backend no | no | yes | yes Arguments : - is the name of a valid server in the same backend section. + is the name of a valid server in the same backend section + or a "log-format" string resolving to a server name. is a condition composed of ACLs, as described in section 7. @@ -11186,6 +11187,13 @@ use-server unless # all the rest is forwarded to this server server default 192.168.0.2:443 check + When is a simple name, it is checked against existing servers in the + configuration and an error is reported if the specified server does not exist. + If it is a log-format, no check is performed when parsing the configuration, + and if we can't resolve a valid server name at runtime but the use-server rule + was conditionned by an ACL returning true, no other use-server rule is applied + and we fall back to load balancing. + See also: "use_backend", section 5 about server and section 7 about ACLs. diff --git a/include/types/arg.h b/include/types/arg.h index a9778f2ec..80e0b0a7b 100644 --- a/include/types/arg.h +++ b/include/types/arg.h @@ -81,6 +81,7 @@ enum { ARGC_SRV, /* server line */ ARGC_SPOE, /* spoe message args */ ARGC_UBK, /* use_backend message */ + ARGC_USRV, /* use-server message */ }; /* flags used when compiling and executing regex */ diff --git a/include/types/proxy.h b/include/types/proxy.h index a3da428e8..84ac8d058 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -509,10 +509,14 @@ struct switching_rule { struct server_rule { struct list list; /* list linked to from the proxy */ struct acl_cond *cond; /* acl condition to meet */ + int dynamic; union { struct server *ptr; /* target server */ char *name; /* target server name during config parsing */ } srv; + struct list expr; /* logformat expression to use for dynamic rules */ + char *file; + int line; }; struct persist_rule { diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 9bffee5d2..ffc575c39 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -1592,6 +1592,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) rule = calloc(1, sizeof(*rule)); rule->cond = cond; rule->srv.name = strdup(args[1]); + rule->line = linenum; + rule->file = strdup(file); LIST_INIT(&rule->list); LIST_ADDQ(&curproxy->server_rules, &rule->list); curproxy->be_req_ana |= AN_REQ_SRV_RULES; diff --git a/src/cfgparse.c b/src/cfgparse.c index 2c8008312..9b4a0be37 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2701,7 +2701,40 @@ int check_config_validity() /* find the target server for 'use_server' rules */ list_for_each_entry(srule, &curproxy->server_rules, list) { - struct server *target = findserver(curproxy, srule->srv.name); + struct server *target; + struct logformat_node *node; + char *server_name; + + /* We try to parse the string as a log format expression. If the result of the parsing + * is only one entry containing a single string, then it's a standard string corresponding + * to a static rule, thus the parsing is cancelled and we fall back to setting srv.ptr. + */ + server_name = srule->srv.name; + LIST_INIT(&srule->expr); + curproxy->conf.args.ctx = ARGC_USRV; + err = NULL; + if (!parse_logformat_string(server_name, curproxy, &srule->expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) { + ha_alert("Parsing [%s:%d]; use-server rule failed to parse log-format '%s' : %s.\n", + srule->file, srule->line, server_name, err); + free(err); + cfgerr++; + continue; + } + node = LIST_NEXT(&srule->expr, struct logformat_node *, list); + + if (!LIST_ISEMPTY(&srule->expr)) { + if (node->type != LOG_FMT_TEXT || node->list.n != &srule->expr) { + srule->dynamic = 1; + free(server_name); + continue; + } + free(node->arg); + free(node); + } + + srule->dynamic = 0; + srule->srv.name = server_name; + target = findserver(curproxy, srule->srv.name); if (!target) { ha_alert("config : %s '%s' : unable to find server '%s' referenced in a 'use-server' rule.\n", diff --git a/src/stream.c b/src/stream.c index c059a7a22..8b9d885e7 100644 --- a/src/stream.c +++ b/src/stream.c @@ -1169,7 +1169,20 @@ static int process_server_rules(struct stream *s, struct channel *req, int an_bi ret = !ret; if (ret) { - struct server *srv = rule->srv.ptr; + struct server *srv; + + if (rule->dynamic) { + struct buffer *tmp = get_trash_chunk(); + + if (!build_logline(s, tmp->area, tmp->size, &rule->expr)) + break; + + srv = findserver(s->be, tmp->area); + if (!srv) + break; + } + else + srv = rule->srv.ptr; if ((srv->cur_state != SRV_ST_STOPPED) || (px->options & PR_O_PERSIST) ||