Add 'get server <backend>/<request>' runtime api

Change-Id: Idc57f080703b5da7e6dac96874d20e2abf99e609
diff --git a/include/haproxy/backend.h b/include/haproxy/backend.h
index 4bfdf07..0a81fb3 100644
--- a/include/haproxy/backend.h
+++ b/include/haproxy/backend.h
@@ -48,6 +48,7 @@
 void recount_servers(struct proxy *px);
 void update_backend_weight(struct proxy *px);
 int be_lastsession(const struct proxy *be);
+struct server *get_server_uh(struct proxy *px, char *uri, int uri_len, const struct server *avoid);
 
 /* Returns number of usable servers in backend */
 static inline int be_usable_srv(struct proxy *be)
diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h
index f5c096c..8b7ec74 100644
--- a/include/haproxy/proxy.h
+++ b/include/haproxy/proxy.h
@@ -76,6 +76,8 @@
                               const struct proxy *defproxy);
 int get_backend_server(const char *bk_name, const char *sv_name,
 		       struct proxy **bk, struct server **sv);
+int test_backend_server(const char *bk_name, char *uri,
+		struct proxy **bk, struct server **sv);
 void proxy_capture_error(struct proxy *proxy, int is_back,
 			 struct proxy *other_end, enum obj_type *target,
 			 const struct session *sess,
diff --git a/src/backend.c b/src/backend.c
index d80e552..92d812d 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -205,7 +205,7 @@
  * algorithm out of a tens because it gave him the best results.
  *
  */
-static struct server *get_server_uh(struct proxy *px, char *uri, int uri_len, const struct server *avoid)
+struct server *get_server_uh(struct proxy *px, char *uri, int uri_len, const struct server *avoid)
 {
 	unsigned int hash = 0;
 	int c;
diff --git a/src/proxy.c b/src/proxy.c
index 709dc4a..c53ea02 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -466,6 +466,26 @@
 	return best_ptr;
 }
 
+int test_backend_server(const char *bk_name, char *uri,
+		struct proxy **bk, struct server **sv)
+{
+	struct proxy *p;
+	struct server *s;
+	int uri_len = strlen(uri);
+	*sv = NULL;
+	p = proxy_be_by_name(bk_name);
+	if (bk)
+		*bk = p;
+	if (!p || (p->lbprm.algo & BE_LB_PARM) != BE_LB_HASH_URI)
+		return 0;
+	fprintf(stderr, "%s\n", uri);
+	s = get_server_uh(p, uri, uri_len, NULL);
+	*sv = s;
+	if (!s)
+		return 0;
+	return 1;
+}
+
 /*
  * This function scans the list of backends and servers to retrieve the first
  * backend and the first server with the given names, and sets them in both
diff --git a/src/server.c b/src/server.c
index 9c7dc83..a5dbf42 100644
--- a/src/server.c
+++ b/src/server.c
@@ -4506,6 +4506,48 @@
 	return 1;
 }
 
+static int cli_parse_get_server(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct proxy *px;
+	struct server *sv;
+	char *line;
+	char pn[INET6_ADDRSTRLEN];
+	uint16_t port;
+	struct sockaddr_storage* addr;
+
+	/* split "backend/server" and make <line> point to server */
+	for (line = args[2]; *line; line++)
+		if (*line == '/') {
+			*line++ = '\0';
+			break;
+		}
+
+	if (!*line) {
+		return cli_err(appctx, "Require 'backend/server'.\n");
+	}
+
+	if (!test_backend_server(args[2], line, &px, &sv)) {
+		return cli_err(appctx, px ? "No such server.\n" : "No such backend.\n");
+	}
+	addr = &(sv->addr);
+	switch (addr_to_str(addr, pn, sizeof(pn))) {
+	case AF_INET:
+		port = ((struct sockaddr_in *)addr)->sin_port;
+		break;
+	case AF_INET6:
+		port = ((struct sockaddr_in6 *)addr)->sin6_port;
+		break;
+	default:
+		port = 0;
+		break;
+	}
+	snprintf(trash.area, trash.size, "%s,%s,%u\n", sv->id, pn, ntohs(port));
+    if (applet_putstr(appctx, trash.area) == -1) {
+        return 0;
+    }
+    return 1;
+}
+
 static int cli_parse_get_weight(char **args, char *payload, struct appctx *appctx, void *private)
 {
 	struct proxy *px;
@@ -5297,6 +5339,7 @@
 	{ { "enable", "server",  NULL },         "enable server  (DEPRECATED)             : enable a disabled server (use 'set server' instead)",         cli_parse_enable_server, NULL },
 	{ { "set", "maxconn", "server",  NULL }, "set maxconn server <bk>/<srv>           : change a server's maxconn setting",                           cli_parse_set_maxconn_server, NULL },
 	{ { "set", "server", NULL },             "set server <bk>/<srv> [opts]            : change a server's state, weight, address or ssl",             cli_parse_set_server },
+        { { "get", "server", NULL },             "get server <bk>/<req>                   : get target backend server by request",                        cli_parse_get_server },
 	{ { "get", "weight", NULL },             "get weight <bk>/<srv>                   : report a server's current weight",                            cli_parse_get_weight },
 	{ { "set", "weight", NULL },             "set weight <bk>/<srv>  (DEPRECATED)     : change a server's weight (use 'set server' instead)",         cli_parse_set_weight },
 	{ { "add", "server", NULL },             "add server <bk>/<srv>                   : create a new server",                                         cli_parse_add_server, NULL },