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

Change-Id: Idc57f080703b5da7e6dac96874d20e2abf99e609
diff --git a/include/haproxy/backend.h b/include/haproxy/backend.h
index 8a763bf..0e2d75f 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 8a2a5c8..5f742e3 100644
--- a/include/haproxy/proxy.h
+++ b/include/haproxy/proxy.h
@@ -70,6 +70,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 b5d351c..bf369da 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -202,7 +202,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 99c4430..52cb069 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -419,6 +419,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 c2cf90f..e7e0151 100644
--- a/src/server.c
+++ b/src/server.c
@@ -4124,6 +4124,50 @@
 	return 1;
 }
 
+static int cli_parse_get_server(char **args, char *payload, struct appctx *appctx, void *private)
+{
+	struct stream_interface *si = appctx->owner;
+	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 (ci_putstr(si_ic(si), trash.area) == -1) {
+                si_rx_room_blk(si);
+                return 0;
+        }
+        return 1;
+}
+
 static int cli_parse_get_weight(char **args, char *payload, struct appctx *appctx, void *private)
 {
 	struct stream_interface *si = appctx->owner;
@@ -4708,6 +4752,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 (EXPERIMENTAL)",                          cli_parse_add_server, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },