From f02ba4100645f0742ae17077d7dc3f8510f92af1 Mon Sep 17 00:00:00 2001
From: Joe K <goodjoe2049@gmail.com>
Date: Wed, 20 Feb 2019 19:25:00 +0000
Subject: [PATCH 1/1] make use server accept log format

---
 include/types/proxy.h |  2 ++
 src/cfgparse.c        | 38 ++++++++++++++++++++++++++++++++++++++
 src/stream.c          | 40 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/include/types/proxy.h b/include/types/proxy.h
index 14b6046c..3f8ede58 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -490,9 +490,11 @@ 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;				/* this is a dynamic rule using the logformat expression */
 	union {
 		struct server *ptr;		/* target server */
 		char *name;			/* target server name during config parsing */
+		struct list expr;		/* logformat expression to use for dynamic rules */
 	} srv;
 };
 
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 7c316df0..e03277d0 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2437,6 +2437,44 @@ int check_config_validity()
 
 		/* find the target server for 'use_server' rules */
 		list_for_each_entry(srule, &curproxy->server_rules, list) {
+			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 = srule->srv.name;
+			LIST_INIT(&srule->srv.expr);
+			curproxy->conf.args.ctx = ARGC_UBK;
+			curproxy->conf.args.file = rule->file;
+			curproxy->conf.args.line = rule->line;
+			err=NULL;
+			if (!parse_logformat_string(pxname, curproxy, &srule->srv.expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) {
+				ha_alert("Parsing [%s:%d]: failed to parse use-server rule '%s' : %s.\n",
+					 rule->file, rule->line, pxname, err);
+				free(err);
+				cfgerr++;
+				continue;
+			}
+			node = LIST_NEXT(&srule->srv.expr, struct logformat_node *, list);
+
+			if (!LIST_ISEMPTY(&srule->srv.expr)) {
+				if (node->type != LOG_FMT_TEXT || node->list.n != &srule->srv.expr) {
+					srule->dynamic = 1;
+					free(pxname);
+					continue;
+				}
+				/* simple string: free the expression and fall back to static rule */
+				free(node->arg);
+				free(node);
+			}
+
+			srule->dynamic = 0;
+			srule->srv.name = pxname;
+
 			struct server *target = findserver(curproxy, srule->srv.name);
 
 			if (!target) {
diff --git a/src/stream.c b/src/stream.c
index ac323b1e..6bcf865e 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -1404,7 +1404,29 @@ static int process_server_rules(struct stream *s, struct channel *req, int an_bi
 				ret = !ret;
 
 			if (ret) {
-				struct server *srv = rule->srv.ptr;
+				/* If the server 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 server.
+				 */
+				struct server *srv = NULL;
+
+				if (rule->dynamic) {
+					struct buffer *tmp;
+
+					tmp = alloc_trash_chunk();
+					if (!tmp)
+						goto sr_failed;
+
+					if (build_logline(s, tmp->area, tmp->size, &rule->srv.expr))
+						srv = server_find_by_name(px, tmp->area);
+
+					free_trash_chunk(tmp);
+					tmp = NULL;
+
+					if (!srv)
+						break;
+				} else
+					srv = rule->srv.ptr;
 
 				if ((srv->cur_state != SRV_ST_STOPPED) ||
 				    (px->options & PR_O_PERSIST) ||
@@ -1423,6 +1445,22 @@ static int process_server_rules(struct stream *s, struct channel *req, int an_bi
 	req->analysers &= ~an_bit;
 	req->analyse_exp = TICK_ETERNITY;
 	return 1;
+
+ sr_failed:
+	/* immediately abort this request in case of allocation failure */
+	channel_abort(&s->req);
+	channel_abort(&s->res);
+
+	if (!(s->flags & SF_ERR_MASK))
+		s->flags |= SF_ERR_RESOURCE;
+	if (!(s->flags & SF_FINST_MASK))
+		s->flags |= SF_FINST_R;
+
+	if (s->txn)
+		s->txn->status = 500;
+	s->req.analysers &= AN_REQ_FLT_END;
+	s->req.analyse_exp = TICK_ETERNITY;
+	return 0;
 }
 
 /* This stream analyser works on a request. It applies all sticking rules on
-- 
2.20.1

