MINOR: stats: Add JSON export from the stats page

It is now possible to export stats using the JSON format from the HTTP stats
page. Like for the CSV export, to export stats in JSON, you must add the option
";json" on the stats URL. It is also possible to dump the JSON schema with the
option ";json-schema". Corresponding Links have been added on the HTML page.

This patch fixes the issue #263.
diff --git a/include/types/stats.h b/include/types/stats.h
index 2a71ff6..d99d519 100644
--- a/include/types/stats.h
+++ b/include/types/stats.h
@@ -28,9 +28,12 @@
 #define STAT_NO_REFRESH 0x00000010	/* do not automatically refresh the stats page */
 #define STAT_ADMIN      0x00000020	/* indicate a stats admin level */
 #define STAT_CHUNKED    0x00000040      /* use chunked encoding (HTTP/1.1) */
+#define STAT_JSON_SCHM  0x00000080      /* dump the json schema */
 #define STAT_BOUND      0x00800000	/* bound statistics to selected proxies/types/services */
 #define STAT_STARTED    0x01000000	/* some output has occurred */
 
+#define STAT_FMT_MASK   0x00000007
+
 #define STATS_TYPE_FE  0
 #define STATS_TYPE_BE  1
 #define STATS_TYPE_SV  2
diff --git a/src/http_ana.c b/src/http_ana.c
index eef0c09..6fff39b 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -4399,19 +4399,35 @@
 
 	for (h = lookup; h <= end - 4; h++) {
 		if (memcmp(h, ";csv", 4) == 0) {
-			appctx->ctx.stats.flags &= ~STAT_FMT_HTML;
+			appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
 			break;
 		}
 	}
 
 	for (h = lookup; h <= end - 6; h++) {
 		if (memcmp(h, ";typed", 6) == 0) {
-			appctx->ctx.stats.flags &= ~STAT_FMT_HTML;
+			appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
 			appctx->ctx.stats.flags |= STAT_FMT_TYPED;
 			break;
 		}
 	}
 
+	for (h = lookup; h <= end - 5; h++) {
+		if (memcmp(h, ";json", 5) == 0) {
+			appctx->ctx.stats.flags &= ~(STAT_FMT_MASK|STAT_JSON_SCHM);
+			appctx->ctx.stats.flags |= STAT_FMT_JSON;
+			break;
+		}
+	}
+
+	for (h = lookup; h <= end - 12; h++) {
+		if (memcmp(h, ";json-schema", 12) == 0) {
+			appctx->ctx.stats.flags &= ~STAT_FMT_MASK;
+			appctx->ctx.stats.flags |= STAT_JSON_SCHM;
+			break;
+		}
+	}
+
 	for (h = lookup; h <= end - 8; h++) {
 		if (memcmp(h, ";st=", 4) == 0) {
 			int i;
diff --git a/src/stats.c b/src/stats.c
index 34efbfb..e59ad10 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -251,6 +251,7 @@
 /* one line of stats */
 static THREAD_LOCAL struct field stats[ST_F_TOTAL_FIELDS];
 
+static void stats_dump_json_schema(struct buffer *out);
 
 static int stats_putchk(struct channel *chn, struct htx *htx, struct buffer *chk)
 {
@@ -2527,6 +2528,12 @@
 		      scope_txt);
 
 	chunk_appendf(&trash,
+	              "<li><a href=\"%s;json%s%s\">JSON export</a> (<a href=\"%s;json-schema\">schema</a>)<br>\n",
+	              uri->uri_prefix,
+	              (uri->refresh > 0) ? ";norefresh" : "",
+		      scope_txt, uri->uri_prefix);
+
+	chunk_appendf(&trash,
 	              "</ul></td>"
 	              "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
 	              "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
@@ -2682,6 +2689,8 @@
 	case STAT_ST_HEAD:
 		if (appctx->ctx.stats.flags & STAT_FMT_HTML)
 			stats_dump_html_head(uri);
+		else if (appctx->ctx.stats.flags & STAT_JSON_SCHM)
+			stats_dump_json_schema(&trash);
 		else if (appctx->ctx.stats.flags & STAT_FMT_JSON)
 			stats_dump_json_header();
 		else if (!(appctx->ctx.stats.flags & STAT_FMT_TYPED))
@@ -2690,6 +2699,10 @@
 		if (!stats_putchk(rep, htx, &trash))
 			goto full;
 
+		if (appctx->ctx.stats.flags & STAT_JSON_SCHM) {
+			appctx->st2 = STAT_ST_FIN;
+			return 1;
+		}
 		appctx->st2 = STAT_ST_INFO;
 		/* fall through */
 
@@ -3124,6 +3137,10 @@
 		if (!htx_add_header(htx, ist("Content-Type"), ist("text/html")))
 			goto full;
 	}
+	else if (appctx->ctx.stats.flags & (STAT_FMT_JSON|STAT_JSON_SCHM)) {
+		if (!htx_add_header(htx, ist("Content-Type"), ist("application/json")))
+			goto full;
+	}
 	else {
 		if (!htx_add_header(htx, ist("Content-Type"), ist("text/plain")))
 			goto full;