[MINOR]: stats: add show-legends to report additional informations

Supported informations, available via "tr/td title":
  - cap: capabilities (proxy)
  - mode: one of tcp, http or health (proxy)
  - id: SNMP ID (proxy, socket, server)
  - IP (socket, server)
  - cookie (backend, server)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 46787e5..8652505 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4084,6 +4084,19 @@
 
   See also: "show-node", "stats enable", "stats uri" and "description" in global section.
 
+stats show-legends
+  Enable reporting additional informations on the statistics page :
+    - cap: capabilities (proxy)
+    - mode: one of tcp, http or health (proxy)
+    - id: SNMP ID (proxy, socket, server)
+    - IP (socket, server)
+    - cookie (backend, server)
+
+  Though this statement alone is enough to enable statistics reporting, it is
+  recommended to set all other settings in order to avoid relying on default
+  unobvious parameters.
+
+  See also: "stats enable", "stats uri".
 
 stats realm <realm>
   Enable statistics and set authentication realm
diff --git a/include/common/uri_auth.h b/include/common/uri_auth.h
index 3a7fd9c..64f818b 100644
--- a/include/common/uri_auth.h
+++ b/include/common/uri_auth.h
@@ -34,6 +34,7 @@
 #define	ST_HIDEVER	0x00000001	/* do not report the version and reldate */
 #define	ST_SHNODE	0x00000002	/* show node name */
 #define	ST_SHDESC	0x00000004	/* show description */
+#define ST_SHLGNDS	0x0000008	/* show legends */
 
 /* later we may link them to support multiple URI matching */
 struct uri_auth {
diff --git a/include/proto/backend.h b/include/proto/backend.h
index 7a248da..a6f1c9f 100644
--- a/include/proto/backend.h
+++ b/include/proto/backend.h
@@ -34,6 +34,7 @@
 int assign_server_and_queue(struct session *s);
 int connect_server(struct session *s);
 int srv_redispatch_connect(struct session *t);
+const char *backend_lb_algo_str(int algo);
 int backend_parse_balance(const char **args, char *err,
 			  int errlen, struct proxy *curproxy);
 
diff --git a/src/backend.c b/src/backend.c
index fedac39..34783f5 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -950,6 +950,33 @@
 	return now.tv_sec - px->last_change + px->down_time;
 }
 
+/*
+ * This function returns a string containing the balancing
+ * mode of the proxy in a format suitable for stats.
+ */
+
+const char *backend_lb_algo_str(int algo) {
+
+	if (algo == BE_LB_ALGO_RR)
+		return "roundrobin";
+	else if (algo == BE_LB_ALGO_SRR)
+		return "static-rr";
+	else if (algo == BE_LB_ALGO_LC)
+		return "leastconn";
+	else if (algo == BE_LB_ALGO_SH)
+		return "source";
+	else if (algo == BE_LB_ALGO_UH)
+		return "uri";
+	else if (algo == BE_LB_ALGO_PH)
+		return "url_param";
+	else if (algo == BE_LB_ALGO_HH)
+		return "hdr";
+	else if (algo == BE_LB_ALGO_RCH)
+		return "rdp-cookie";
+	else
+		return NULL;
+}
+
 /* This function parses a "balance" statement in a backend section describing
  * <curproxy>. It returns -1 if there is any error, otherwise zero. If it
  * returns -1, it may write an error message into ther <err> buffer, for at
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 10013fe..20fb657 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1963,7 +1963,7 @@
 			curproxy->uri_auth = NULL; /* we must detach from the default config */
 
 		if (*(args[1]) == 0) {
-			Alert("parsing [%s:%d] : '%s' expects 'uri', 'realm', 'auth', 'scope' or 'enable', 'hide-version', 'show-node', 'show-desc'.\n", file, linenum, args[0]);
+			Alert("parsing [%s:%d] : '%s' expects 'uri', 'realm', 'auth', 'scope' or 'enable', 'hide-version', 'show-node', 'show-desc', 'show-legends'.\n", file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		} else if (!strcmp(args[1], "uri")) {
@@ -2032,6 +2032,12 @@
 				err_code |= ERR_ALERT | ERR_ABORT;
 				goto out;
 			}
+		} else if (!strcmp(args[1], "show-legends")) {
+			if (!stats_set_flag(&curproxy->uri_auth, ST_SHLGNDS)) {
+				Alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+				err_code |= ERR_ALERT | ERR_ABORT;
+				goto out;
+			}
 		} else if (!strcmp(args[1], "show-node")) {
 
 			if (*args[2]) {
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 576566d..e51a314 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -1231,8 +1231,30 @@
 			chunk_printf(&msg,
 				     "<table class=\"tbl\" width=\"100%%\">\n"
 				     "<tr class=\"titre\">"
-				     "<th class=\"pxname\" width=\"10%%\">"
-				     "<a name=\"%s\"></a>"
+				     "<th class=\"pxname\" width=\"10%%\"");
+
+			if (uri->flags&ST_SHLGNDS) {
+				/* cap, mode, id */
+				chunk_printf(&msg, " title=\"cap: %s, mode: %s, id: %d",
+					proxy_cap_str(px->cap), proxy_mode_str(px->mode),
+					px->uuid);
+
+				/* cookie */
+				if (px->cookie_name) {
+					struct chunk src;
+
+					chunk_printf(&msg, ", cookie: '");
+					chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
+					chunk_htmlencode(&msg, &src);
+
+					chunk_printf(&msg, "'");
+				}
+
+				chunk_printf(&msg, "\"");
+			}
+
+			chunk_printf(&msg,
+				     "><a name=\"%s\"></a>"
 				     "<a class=px href=\"#%s\">%s</a></th>"
 				     "<th class=\"%s\" width=\"90%%\">%s</th>"
 				     "</tr>\n"
@@ -1404,9 +1426,44 @@
 			}
 
 			if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
+				chunk_printf(&msg, "<tr class=socket><td class=ac");
+
+					if (uri->flags&ST_SHLGNDS) {
+						char str[INET6_ADDRSTRLEN], *fmt = NULL;
+						int port;
+
+						chunk_printf(&msg, " title=\"IP: ");
+
+						port = (l->addr.ss_family == AF_INET6)
+							? ntohs(((struct sockaddr_in6 *)(&l->addr))->sin6_port)
+							: ntohs(((struct sockaddr_in *)(&l->addr))->sin_port);
+
+						if (l->addr.ss_family == AF_INET) {
+							if (inet_ntop(AF_INET,
+							    (const void *)&((struct sockaddr_in *)&l->addr)->sin_addr,
+							    str, sizeof(str)))
+								fmt = "%s:%d";
+						} else {
+							if (inet_ntop(AF_INET6,
+							    (const void *)&((struct sockaddr_in6 *)(&l->addr))->sin6_addr,
+							    str, sizeof(str)))
+								fmt = "[%s]:%d";
+						}
+
+						if (fmt)
+							chunk_printf(&msg, fmt, str, port);
+						else
+							chunk_printf(&msg, "(%s)", strerror(errno));
+
+						/* id */
+						chunk_printf(&msg, ", id: %d", l->luid);
+
+						chunk_printf(&msg, "\"");
+					}
+
 				chunk_printf(&msg,
 				     /* name, queue */
-				     "<tr class=socket><td class=ac><a name=\"%s/+%s\"></a>"
+				     "><a name=\"%s/+%s\"></a>"
 				     "<a class=lfsb href=\"#%s/+%s\">%s</a></td><td colspan=3></td>"
 				     /* sessions rate: current, max, limit */
 				     "<td colspan=3>&nbsp;</td>"
@@ -1536,8 +1593,40 @@
 							       "<i>no check</i>" };
 				chunk_printf(&msg,
 				     /* name */
-				     "<tr class=\"%s%d\"><td class=ac>"
-				     "<a name=\"%s/%s\"></a>"
+				     "<tr class=\"%s%d\"><td class=ac",
+				     (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state);
+
+				if (uri->flags&ST_SHLGNDS) {
+					char str[INET6_ADDRSTRLEN];
+
+					chunk_printf(&msg, " title=\"IP: ");
+
+					/* IP */
+					if (inet_ntop(sv->addr.sin_family, &sv->addr.sin_addr, str, sizeof(str)))
+						chunk_printf(&msg, "%s:%d", str, htons(sv->addr.sin_port));
+					else
+						chunk_printf(&msg, "(%s)", strerror(errno));
+
+					/* id */
+					chunk_printf(&msg, ", id: %d", sv->puid);
+
+					/* cookie */
+					if (sv->cookie) {
+						struct chunk src;
+
+						chunk_printf(&msg, ", cookie: '");
+
+						chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
+						chunk_htmlencode(&msg, &src);
+
+						chunk_printf(&msg, "'");
+					}
+
+					chunk_printf(&msg, "\"");
+				}
+
+				chunk_printf(&msg,
+				     "><a name=\"%s/%s\"></a>"
 				     "<a class=lfsb href=\"#%s/%s\">%s</a></td>"
 				     /* queue : current, max, limit */
 				     "<td>%s</td><td>%s</td><td>%s</td>"
@@ -1547,8 +1636,7 @@
 				     "<td>%s</td><td>%s</td><td>%s</td>"
 				     "<td"
 				     "",
-				     (sv->state & SRV_BACKUP) ? "backup" : "active",
-				     sv_state, px->id, sv->id, px->id, sv->id, sv->id,
+				     px->id, sv->id, px->id, sv->id, sv->id,
 				     U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"),
 				     U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max),
 				     U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-"));
@@ -1803,8 +1891,17 @@
 			if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
 				chunk_printf(&msg,
 				     /* name */
-				     "<tr class=\"backend\"><td class=ac>"
-				     "<a name=\"%s/Backend\"></a>"
+				     "<tr class=\"backend\"><td class=ac");
+
+				if (uri->flags&ST_SHLGNDS) {
+					/* balancing */
+
+					 chunk_printf(&msg, " title=\"balancing: %s\"",
+						 backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
+				}
+
+				chunk_printf(&msg,
+				     "><a name=\"%s/Backend\"></a>"
 				     "<a class=lfsb href=\"#%s/Backend\">Backend</a></td>"
 				     /* queue : current, max */
 				     "<td>%s</td><td>%s</td><td></td>"