]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: stats: extract HTML related functions
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 16 Apr 2024 09:21:06 +0000 (11:21 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 18 Apr 2024 15:04:08 +0000 (17:04 +0200)
Extract functions related to HTML stats webpage from stats.c into a new
module named stats-html. This allows to reduce stats.c to roughly half
of its original size.

Makefile
include/haproxy/stats-html-t.h [new file with mode: 0644]
include/haproxy/stats-html.h [new file with mode: 0644]
include/haproxy/stats-t.h
include/haproxy/stats.h
src/http_ana.c
src/stats-html.c [new file with mode: 0644]
src/stats.c

index 6417c69c140724a7b6e19c5ac704e88275b2f191..b97d69b7bd2ac7b13a7d529a844a216ee334689d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -975,7 +975,7 @@ OBJS += src/mux_h2.o src/mux_fcgi.o src/mux_h1.o src/tcpcheck.o               \
         src/dynbuf.o src/wdt.o src/pipe.o src/init.o src/http_acl.o           \
         src/hpack-huff.o src/hpack-enc.o src/dict.o src/freq_ctr.o            \
         src/ebtree.o src/hash.o src/dgram.o src/version.o src/proto_rhttp.o   \
-        src/guid.o
+        src/guid.o src/stats-html.o
 
 ifneq ($(TRACE),)
   OBJS += src/calltrace.o
diff --git a/include/haproxy/stats-html-t.h b/include/haproxy/stats-html-t.h
new file mode 100644 (file)
index 0000000..1b77aea
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _HAPROXY_STATS_HTML_T_H
+#define _HAPROXY_STATS_HTML_T_H
+
+/* HTTP stats : applet.st0 */
+enum {
+       STAT_HTTP_INIT = 0,  /* Initial state */
+       STAT_HTTP_HEAD,      /* send headers before dump */
+       STAT_HTTP_DUMP,      /* dumping stats */
+       STAT_HTTP_POST,      /* waiting post data */
+       STAT_HTTP_LAST,      /* sending last chunk of response */
+       STAT_HTTP_DONE,      /* dump is finished */
+       STAT_HTTP_END,       /* finished */
+};
+
+/* HTML form to limit output scope */
+#define STAT_SCOPE_TXT_MAXLEN 20      /* max len for scope substring */
+#define STAT_SCOPE_INPUT_NAME "scope" /* pattern form scope name <input> in html form */
+#define STAT_SCOPE_PATTERN    "?" STAT_SCOPE_INPUT_NAME "="
+
+
+#endif /* _HAPROXY_STATS_HTML_T_H */
diff --git a/include/haproxy/stats-html.h b/include/haproxy/stats-html.h
new file mode 100644 (file)
index 0000000..3102290
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _HAPROXY_STATS_HTML_H
+#define _HAPROXY_STATS_HTML_H
+
+#include <haproxy/stats-html-t.h>
+
+#include <haproxy/applet-t.h>
+#include <haproxy/buf-t.h>
+#include <haproxy/proxy-t.h>
+#include <haproxy/stats-t.h>
+#include <haproxy/stconn-t.h>
+
+void stats_dump_html_head(struct appctx *appctx);
+void stats_dump_html_info(struct stconn *sc);
+int stats_dump_fields_html(struct buffer *out, const struct field *stats,
+                           struct show_stat_ctx *ctx);
+void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px);
+void stats_dump_html_px_end(struct stconn *sc, struct proxy *px);
+void stats_dump_html_end();
+
+extern struct applet http_stats_applet;
+
+#endif /* _HAPROXY_STATS_HTML_H */
index 1c5e86e14f7a632dd191e3c89ec1f90bb6640ca4..6f07a581220564f87a1fdce302d5c912f921fc67 100644 (file)
 #define STATS_DOMAIN  (0)               /* used for bitshifting, type of statistics: proxy or dns */
 #define STATS_PX_CAP  (8)               /* used for bitshifting, differentiate obj1 type for proxy statistics */
 
-/* HTTP stats : applet.st0 */
-enum {
-       STAT_HTTP_INIT = 0,  /* Initial state */
-       STAT_HTTP_HEAD,      /* send headers before dump */
-       STAT_HTTP_DUMP,      /* dumping stats */
-       STAT_HTTP_POST,      /* waiting post data */
-       STAT_HTTP_LAST,      /* sending last chunk of response */
-       STAT_HTTP_DONE,      /* dump is finished */
-       STAT_HTTP_END,       /* finished */
-};
-
 /* status codes available for the stats admin page */
 enum {
        STAT_STATUS_INIT = 0,
@@ -83,11 +72,6 @@ enum {
        STAT_STATUS_SIZE
 };
 
-/* HTML form to limit output scope */
-#define STAT_SCOPE_TXT_MAXLEN 20      /* max len for scope substring */
-#define STAT_SCOPE_INPUT_NAME "scope" /* pattern form scope name <input> in html form */
-#define STAT_SCOPE_PATTERN    "?" STAT_SCOPE_INPUT_NAME "="
-
 /* Actions available for the stats admin forms */
 enum {
        ST_ADM_ACTION_NONE = 0,
index 031e9d9a9dfacb10607580d5327a5d280ed671de..6affd5e95f4e151f0334ecec6cac4b40bc0eb201 100644 (file)
@@ -33,6 +33,7 @@ struct buffer;
 struct proxy;
 struct appctx;
 struct htx;
+struct stconn;
 
 /* These two structs contains all field names and descriptions according to
  * the the number of entries in "enum stat_field" and "enum info_field"
@@ -48,6 +49,8 @@ extern THREAD_LOCAL struct field *stat_l[];
 struct htx;
 int stats_putchk(struct appctx *appctx, struct buffer *buf, struct htx *htx);
 
+const char *stats_scope_ptr(struct appctx *appctx);
+
 int stats_dump_one_line(const struct field *stats, size_t stats_count, struct appctx *appctx);
 
 int stats_fill_info(struct field *info, int len, uint flags);
@@ -60,6 +63,8 @@ int stats_fill_sv_stats(struct proxy *px, struct server *sv, int flags,
 int stats_fill_be_stats(struct proxy *px, int flags, struct field *stats, int len,
                        enum stat_field *selected_field);
 
+int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx);
+
 int stats_emit_raw_data_field(struct buffer *out, const struct field *f);
 int stats_emit_typed_data_field(struct buffer *out, const struct field *f);
 int stats_emit_field_tags(struct buffer *out, const struct field *f,
index 4c1ff98eb4a1f916d8855bfa1d9e03a6099884ff..a19163fd2869cc2a3a7ef4b9801f04c96caf90df 100644 (file)
@@ -35,6 +35,7 @@
 #include <haproxy/sc_strm.h>
 #include <haproxy/server-t.h>
 #include <haproxy/stats.h>
+#include <haproxy/stats-html.h>
 #include <haproxy/stconn.h>
 #include <haproxy/stream.h>
 #include <haproxy/trace.h>
diff --git a/src/stats-html.c b/src/stats-html.c
new file mode 100644 (file)
index 0000000..de4103f
--- /dev/null
@@ -0,0 +1,2082 @@
+#include <haproxy/stats-html.h>
+
+#include <string.h>
+
+#include <import/ist.h>
+#include <haproxy/api.h>
+#include <haproxy/applet.h>
+#include <haproxy/buf.h>
+#include <haproxy/chunk.h>
+#include <haproxy/clock.h>
+#include <haproxy/freq_ctr.h>
+#include <haproxy/global.h>
+#include <haproxy/http.h>
+#include <haproxy/http_htx.h>
+#include <haproxy/htx.h>
+#include <haproxy/list.h>
+#include <haproxy/listener.h>
+#include <haproxy/obj_type-t.h>
+#include <haproxy/pipe.h>
+#include <haproxy/proxy.h>
+#include <haproxy/stats.h>
+#include <haproxy/stconn.h>
+#include <haproxy/server.h>
+#include <haproxy/task.h>
+#include <haproxy/thread.h>
+#include <haproxy/time.h>
+#include <haproxy/tinfo.h>
+#include <haproxy/tools.h>
+#include <haproxy/uri_auth-t.h>
+#include <haproxy/version.h>
+
+static const char *field_to_html_str(const struct field *f)
+{
+       switch (field_format(f, 0)) {
+       case FF_S32: return U2H(f->u.s32);
+       case FF_S64: return U2H(f->u.s64);
+       case FF_U64: return U2H(f->u.u64);
+       case FF_U32: return U2H(f->u.u32);
+       case FF_FLT: return F2H(f->u.flt);
+       case FF_STR: return field_str(f, 0);
+       case FF_EMPTY:
+       default:
+               return "";
+       }
+}
+
+/* Dumps the HTTP stats head block to chunk ctx buffer and uses the per-uri
+ * parameters from the parent proxy. The caller is responsible for clearing
+ * chunk ctx buffer if needed.
+ */
+void stats_dump_html_head(struct appctx *appctx)
+{
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct buffer *chk = &ctx->chunk;
+       struct uri_auth *uri;
+
+       BUG_ON(!ctx->http_px);
+       uri = ctx->http_px->uri_auth;
+
+       /* WARNING! This must fit in the first buffer !!! */
+       chunk_appendf(chk,
+                     "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
+                     "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
+                     "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
+                     "<link rel=\"icon\" href=\"data:,\">\n"
+                     "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
+                     "<style type=\"text/css\"><!--\n"
+                     "body {"
+                     " font-family: arial, helvetica, sans-serif;"
+                     " font-size: 12px;"
+                     " font-weight: normal;"
+                     " color: black;"
+                     " background: white;"
+                     "}\n"
+                     "th,td {"
+                     " font-size: 10px;"
+                     "}\n"
+                     "h1 {"
+                     " font-size: x-large;"
+                     " margin-bottom: 0.5em;"
+                     "}\n"
+                     "h2 {"
+                     " font-family: helvetica, arial;"
+                     " font-size: x-large;"
+                     " font-weight: bold;"
+                     " font-style: italic;"
+                     " color: #6020a0;"
+                     " margin-top: 0em;"
+                     " margin-bottom: 0em;"
+                     "}\n"
+                     "h3 {"
+                     " font-family: helvetica, arial;"
+                     " font-size: 16px;"
+                     " font-weight: bold;"
+                     " color: #b00040;"
+                     " background: #e8e8d0;"
+                     " margin-top: 0em;"
+                     " margin-bottom: 0em;"
+                     "}\n"
+                     "li {"
+                     " margin-top: 0.25em;"
+                     " margin-right: 2em;"
+                     "}\n"
+                     ".hr {margin-top: 0.25em;"
+                     " border-color: black;"
+                     " border-bottom-style: solid;"
+                     "}\n"
+                     ".titre   {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
+                     ".total   {background: #20D0D0;color: #ffff80;}\n"
+                     ".frontend        {background: #e8e8d0;}\n"
+                     ".socket  {background: #d0d0d0;}\n"
+                     ".backend {background: #e8e8d0;}\n"
+                     ".active_down             {background: #ff9090;}\n"
+                     ".active_going_up         {background: #ffd020;}\n"
+                     ".active_going_down       {background: #ffffa0;}\n"
+                     ".active_up               {background: #c0ffc0;}\n"
+                     ".active_nolb             {background: #20a0ff;}\n"
+                     ".active_draining         {background: #20a0FF;}\n"
+                     ".active_no_check         {background: #e0e0e0;}\n"
+                     ".backup_down             {background: #ff9090;}\n"
+                     ".backup_going_up         {background: #ff80ff;}\n"
+                     ".backup_going_down       {background: #c060ff;}\n"
+                     ".backup_up               {background: #b0d0ff;}\n"
+                     ".backup_nolb             {background: #90b0e0;}\n"
+                     ".backup_draining         {background: #cc9900;}\n"
+                     ".backup_no_check         {background: #e0e0e0;}\n"
+                     ".maintain        {background: #c07820;}\n"
+                     ".rls      {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
+                     "\n"
+                     "a.px:link {color: #ffff40; text-decoration: none;}"
+                     "a.px:visited {color: #ffff40; text-decoration: none;}"
+                     "a.px:hover {color: #ffffff; text-decoration: none;}"
+                     "a.lfsb:link {color: #000000; text-decoration: none;}"
+                     "a.lfsb:visited {color: #000000; text-decoration: none;}"
+                     "a.lfsb:hover {color: #505050; text-decoration: none;}"
+                     "\n"
+                     "table.tbl { border-collapse: collapse; border-style: none;}\n"
+                     "table.tbl td { text-align: right; border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray; white-space: nowrap;}\n"
+                     "table.tbl td.ac { text-align: center;}\n"
+                     "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
+                     "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
+                     "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
+                     "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
+                     "\n"
+                     "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
+                     "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
+                     "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
+                     "table.det { border-collapse: collapse; border-style: none; }\n"
+                     "table.det th { text-align: left; border-width: 0px; padding: 0px 1px 0px 0px; font-style:normal;font-size:11px;font-weight:bold;font-family: sans-serif;}\n"
+                     "table.det td { text-align: right; border-width: 0px; padding: 0px 0px 0px 4px; white-space: nowrap; font-style:normal;font-size:11px;font-weight:normal;}\n"
+                     "u {text-decoration:none; border-bottom: 1px dotted black;}\n"
+                     "div.tips {\n"
+                     " display:block;\n"
+                     " visibility:hidden;\n"
+                     " z-index:2147483647;\n"
+                     " position:absolute;\n"
+                     " padding:2px 4px 3px;\n"
+                     " background:#f0f060; color:#000000;\n"
+                     " border:1px solid #7040c0;\n"
+                     " white-space:nowrap;\n"
+                     " font-style:normal;font-size:11px;font-weight:normal;\n"
+                     " -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;\n"
+                     " -moz-box-shadow:gray 2px 2px 3px;-webkit-box-shadow:gray 2px 2px 3px;box-shadow:gray 2px 2px 3px;\n"
+                     "}\n"
+                     "u:hover div.tips {visibility:visible;}\n"
+                     "@media (prefers-color-scheme: dark) {\n"
+                     " body { font-family: arial, helvetica, sans-serif; font-size: 12px; font-weight: normal; color: #e8e6e3; background: #131516;}\n"
+                     " h1 { color: #a265e0!important; }\n"
+                     " h2 { color: #a265e0; }\n"
+                     " h3 { color: #ff5190; background-color: #3e3e1f; }\n"
+                     " a { color: #3391ff; }\n"
+                     " input { background-color: #2f3437; }\n"
+                     " .hr { border-color: #8c8273; }\n"
+                     " .titre { background-color: #1aa6a6; color: #e8e6e3; }\n"
+                     " .frontend {background: #2f3437;}\n"
+                     " .socket {background: #2a2d2f;}\n"
+                     " .backend {background: #2f3437;}\n"
+                     " .active_down {background: #760000;}\n"
+                     " .active_going_up {background: #b99200;}\n"
+                     " .active_going_down {background: #6c6c00;}\n"
+                     " .active_up {background: #165900;}\n"
+                     " .active_nolb {background: #006ab9;}\n"
+                     " .active_draining {background: #006ab9;}\n"
+                     " .active_no_check {background: #2a2d2f;}\n"
+                     " .backup_down {background: #760000;}\n"
+                     " .backup_going_up {background: #7f007f;}\n"
+                     " .backup_going_down {background: #580092;}\n"
+                     " .backup_up {background: #2e3234;}\n"
+                     " .backup_nolb {background: #1e3c6a;}\n"
+                     " .backup_draining {background: #a37a00;}\n"
+                     " .backup_no_check {background: #2a2d2f;}\n"
+                     " .maintain {background: #9a601a;}\n"
+                     " a.px:link {color: #d8d83b; text-decoration: none;}\n"
+                     " a.px:visited {color: #d8d83b; text-decoration: none;}\n"
+                     " a.px:hover {color: #ffffff; text-decoration: none;}\n"
+                     " a.lfsb:link {color: #e8e6e3; text-decoration: none;}\n"
+                     " a.lfsb:visited {color: #e8e6e3; text-decoration: none;}\n"
+                     " a.lfsb:hover {color: #b5afa6; text-decoration: none;}\n"
+                     " table.tbl th.empty { background-color: #181a1b; }\n"
+                     " table.tbl th.desc { background: #181a1b; }\n"
+                     " table.tbl th.pxname { background-color: #8d0033; color: #ffff46; }\n"
+                     " table.tbl th { border-color: #808080; }\n"
+                     " table.tbl td { border-color: #808080; }\n"
+                     " u {text-decoration:none; border-bottom: 1px dotted #e8e6e3;}\n"
+                     " div.tips {\n"
+                     "  background:#8e8e0d;\n"
+                     "  color:#e8e6e3;\n"
+                     "  border-color: #4e2c86;\n"
+                     "  -moz-box-shadow: #60686c 2px 2px 3px;\n"
+                     "  -webkit-box-shadow: #60686c 2px 2px 3px;\n"
+                     "  box-shadow: #60686c 2px 2px 3px;\n"
+                     " }\n"
+                     "}\n"
+                     "-->\n"
+                     "</style></head>\n",
+                     (ctx->flags & STAT_SHNODE) ? " on " : "",
+                     (ctx->flags & STAT_SHNODE) ? (uri && uri->node ? uri->node : global.node) : ""
+                     );
+}
+
+/* Dumps the HTML stats information block to chunk ctx buffer and uses the
+ * state from stream connector <sc> and per-uri parameter from the parent
+ * proxy. The caller is responsible for clearing chunk ctx buffer if needed.
+ */
+void stats_dump_html_info(struct stconn *sc)
+{
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct buffer *chk = &ctx->chunk;
+       unsigned int up = ns_to_sec(now_ns - start_time_ns);
+       char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
+       const char *scope_ptr = stats_scope_ptr(appctx);
+       struct uri_auth *uri;
+       unsigned long long bps;
+       int thr;
+
+       BUG_ON(!ctx->http_px);
+       uri = ctx->http_px->uri_auth;
+       for (bps = thr = 0; thr < global.nbthread; thr++)
+               bps += 32ULL * read_freq_ctr(&ha_thread_ctx[thr].out_32bps);
+
+       /* Turn the bytes per second to bits per second and take care of the
+        * usual ethernet overhead in order to help figure how far we are from
+        * interface saturation since it's the only case which usually matters.
+        * For this we count the total size of an Ethernet frame on the wire
+        * including preamble and IFG (1538) for the largest TCP segment it
+        * transports (1448 with TCP timestamps). This is not valid for smaller
+        * packets (under-estimated), but it gives a reasonably accurate
+        * estimation of how far we are from uplink saturation.
+        */
+       bps = bps * 8 * 1538 / 1448;
+
+       /* WARNING! this has to fit the first packet too.
+        * We are around 3.5 kB, add adding entries will
+        * become tricky if we want to support 4kB buffers !
+        */
+       chunk_appendf(chk,
+                     "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
+                     PRODUCT_NAME "%s</a></h1>\n"
+                     "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
+                     "<hr width=\"100%%\" class=\"hr\">\n"
+                     "<h3>&gt; General process information</h3>\n"
+                     "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
+                     "<p><b>pid = </b> %d (process #%d, nbproc = %d, nbthread = %d)<br>\n"
+                     "<b>uptime = </b> %dd %dh%02dm%02ds; warnings = %u<br>\n"
+                     "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
+                     "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>reached = </b> %llu; <b>maxpipes = </b> %d<br>\n"
+                     "current conns = %d; current pipes = %d/%d; conn rate = %d/sec; bit rate = %.3f %cbps<br>\n"
+                     "Running tasks: %d/%d (%d niced); idle = %d %%<br>\n"
+                     "</td><td align=\"center\" nowrap>\n"
+                     "<table class=\"lgd\"><tr>\n"
+                     "<td class=\"active_up\">&nbsp;</td><td class=\"noborder\">active UP </td>"
+                     "<td class=\"backup_up\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
+                     "</tr><tr>\n"
+                     "<td class=\"active_going_down\"></td><td class=\"noborder\">active UP, going down </td>"
+                     "<td class=\"backup_going_down\"></td><td class=\"noborder\">backup UP, going down </td>"
+                     "</tr><tr>\n"
+                     "<td class=\"active_going_up\"></td><td class=\"noborder\">active DOWN, going up </td>"
+                     "<td class=\"backup_going_up\"></td><td class=\"noborder\">backup DOWN, going up </td>"
+                     "</tr><tr>\n"
+                     "<td class=\"active_down\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
+                     "<td class=\"active_no_check\"></td><td class=\"noborder\">not checked </td>"
+                     "</tr><tr>\n"
+                     "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) &nbsp;</td>"
+                     "</tr><tr>\n"
+                     "<td class=\"active_draining\"></td><td class=\"noborder\" colspan=\"3\">active or backup SOFT STOPPED for maintenance &nbsp;</td>"
+                     "</tr></table>\n"
+                     "Note: \"NOLB\"/\"DRAIN\" = UP with load-balancing disabled."
+                     "</td>"
+                     "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
+                     "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
+                     "",
+                     (ctx->flags & STAT_HIDEVER) ? "" : (stats_version_string),
+                     pid, (ctx->flags & STAT_SHNODE) ? " on " : "",
+                     (ctx->flags & STAT_SHNODE) ? (uri->node ? uri->node : global.node) : "",
+                     (ctx->flags & STAT_SHDESC) ? ": " : "",
+                     (ctx->flags & STAT_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
+                     pid, 1, 1, global.nbthread,
+                     up / 86400, (up % 86400) / 3600,
+                     (up % 3600) / 60, (up % 60),
+                     HA_ATOMIC_LOAD(&tot_warnings),
+                     global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
+                     global.rlimit_memmax ? " MB" : "",
+                     global.rlimit_nofile,
+                     global.maxsock, global.maxconn, HA_ATOMIC_LOAD(&maxconn_reached), global.maxpipes,
+                     actconn, pipes_used, pipes_used+pipes_free, read_freq_ctr(&global.conn_per_sec),
+                     bps >= 1000000000UL ? (bps / 1000000000.0) : bps >= 1000000UL ? (bps / 1000000.0) : (bps / 1000.0),
+                     bps >= 1000000000UL ? 'G' : bps >= 1000000UL ? 'M' : 'k',
+                     total_run_queues(), total_allocated_tasks(), total_niced_running_tasks(), clock_report_idle());
+
+       /* scope_txt = search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+       memcpy(scope_txt, scope_ptr, ctx->scope_len);
+       scope_txt[ctx->scope_len] = '\0';
+
+       chunk_appendf(chk,
+                     "<li><form method=\"GET\">Scope : <input value=\"%s\" name=\"" STAT_SCOPE_INPUT_NAME "\" size=\"8\" maxlength=\"%d\" tabindex=\"1\"/></form>\n",
+                     (ctx->scope_len > 0) ? scope_txt : "",
+                     STAT_SCOPE_TXT_MAXLEN);
+
+       /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+       scope_txt[0] = 0;
+       if (ctx->scope_len) {
+               strlcpy2(scope_txt, STAT_SCOPE_PATTERN, sizeof(scope_txt));
+               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
+               scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
+       }
+
+       if (ctx->flags & STAT_HIDE_DOWN)
+               chunk_appendf(chk,
+                             "<li><a href=\"%s%s%s%s\">Show all servers</a><br>\n",
+                             uri->uri_prefix,
+                             "",
+                             (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                             scope_txt);
+       else
+               chunk_appendf(chk,
+                             "<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
+                             uri->uri_prefix,
+                             ";up",
+                             (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                             scope_txt);
+
+       if (uri->refresh > 0) {
+               if (ctx->flags & STAT_NO_REFRESH)
+                       chunk_appendf(chk,
+                                     "<li><a href=\"%s%s%s%s\">Enable refresh</a><br>\n",
+                                     uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     "",
+                                     scope_txt);
+               else
+                       chunk_appendf(chk,
+                                     "<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
+                                     uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     ";norefresh",
+                                     scope_txt);
+       }
+
+       chunk_appendf(chk,
+                     "<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
+                     uri->uri_prefix,
+                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                     scope_txt);
+
+       chunk_appendf(chk,
+                     "<li><a href=\"%s;csv%s%s\">CSV export</a><br>\n",
+                     uri->uri_prefix,
+                     (uri->refresh > 0) ? ";norefresh" : "",
+                     scope_txt);
+
+       chunk_appendf(chk,
+                     "<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(chk,
+                     "</ul></td>"
+                     "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
+                     "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
+                     "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
+                     "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
+                     "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
+                     "</ul>"
+                     "</td>"
+                     "</tr></table>\n"
+                     ""
+                     );
+
+       if (ctx->st_code) {
+               switch (ctx->st_code) {
+               case STAT_STATUS_DONE:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_up>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "Action processed successfully."
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               case STAT_STATUS_NONE:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_going_down>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "Nothing has changed."
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               case STAT_STATUS_PART:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_going_down>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "Action partially processed.<br>"
+                                     "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               case STAT_STATUS_ERRP:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_down>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "Action not processed because of invalid parameters."
+                                     "<ul>"
+                                     "<li>The action is maybe unknown.</li>"
+                                     "<li>Invalid key parameter (empty or too long).</li>"
+                                     "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
+                                     "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
+                                     "</ul>"
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               case STAT_STATUS_EXCD:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_down>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "<b>Action not processed : the buffer couldn't store all the data.<br>"
+                                     "You should retry with less servers at a time.</b>"
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               case STAT_STATUS_DENY:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_down>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "<b>Action denied.</b>"
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               case STAT_STATUS_IVAL:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_down>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "<b>Invalid requests (unsupported method or chunked encoded request).</b>"
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+                       break;
+               default:
+                       chunk_appendf(chk,
+                                     "<p><div class=active_no_check>"
+                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
+                                     "Unexpected result."
+                                     "</div>\n", uri->uri_prefix,
+                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                                     scope_txt);
+               }
+               chunk_appendf(chk, "<p>\n");
+       }
+}
+
+/* Dump all fields from <stats> into <out> using the HTML format. A column is
+ * reserved for the checkbox is STAT_ADMIN is set in <flags>. Some extra info
+ * are provided if STAT_SHLGNDS is present in <flags>. The statistics from
+ * extra modules are displayed at the end of the lines if STAT_SHMODULES is
+ * present in <flags>.
+ */
+int stats_dump_fields_html(struct buffer *out,
+                           const struct field *stats,
+                           struct show_stat_ctx *ctx)
+{
+       struct buffer src;
+       struct stats_module *mod;
+       int flags = ctx->flags;
+       int i = 0, j = 0;
+
+       if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE) {
+               chunk_appendf(out,
+                             /* name, queue */
+                             "<tr class=\"frontend\">");
+
+               if (flags & STAT_ADMIN) {
+                       /* Column sub-heading for Enable or Disable server */
+                       chunk_appendf(out, "<td></td>");
+               }
+
+               chunk_appendf(out,
+                             "<td class=ac>"
+                             "<a name=\"%s/Frontend\"></a>"
+                             "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
+                             "<td colspan=3></td>"
+                             "",
+                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_PXNAME));
+
+               chunk_appendf(out,
+                             /* sessions rate : current */
+                             "<td><u>%s<div class=tips><table class=det>"
+                             "<tr><th>Current connection rate:</th><td>%s/s</td></tr>"
+                             "<tr><th>Current session rate:</th><td>%s/s</td></tr>"
+                             "",
+                             U2H(stats[ST_F_RATE].u.u32),
+                             U2H(stats[ST_F_CONN_RATE].u.u32),
+                             U2H(stats[ST_F_RATE].u.u32));
+
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
+                       chunk_appendf(out,
+                                     "<tr><th>Current request rate:</th><td>%s/s</td></tr>",
+                                     U2H(stats[ST_F_REQ_RATE].u.u32));
+
+               chunk_appendf(out,
+                             "</table></div></u></td>"
+                             /* sessions rate : max */
+                             "<td><u>%s<div class=tips><table class=det>"
+                             "<tr><th>Max connection rate:</th><td>%s/s</td></tr>"
+                             "<tr><th>Max session rate:</th><td>%s/s</td></tr>"
+                             "",
+                             U2H(stats[ST_F_RATE_MAX].u.u32),
+                             U2H(stats[ST_F_CONN_RATE_MAX].u.u32),
+                             U2H(stats[ST_F_RATE_MAX].u.u32));
+
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
+                       chunk_appendf(out,
+                                     "<tr><th>Max request rate:</th><td>%s/s</td></tr>",
+                                     U2H(stats[ST_F_REQ_RATE_MAX].u.u32));
+
+               chunk_appendf(out,
+                             "</table></div></u></td>"
+                             /* sessions rate : limit */
+                             "<td>%s</td>",
+                             LIM2A(stats[ST_F_RATE_LIM].u.u32, "-"));
+
+               chunk_appendf(out,
+                             /* sessions: current, max, limit, total */
+                             "<td>%s</td><td>%s</td><td>%s</td>"
+                             "<td><u>%s<div class=tips><table class=det>"
+                             "<tr><th>Cum. connections:</th><td>%s</td></tr>"
+                             "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
+                             "",
+                             U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
+                             U2H(stats[ST_F_STOT].u.u64),
+                             U2H(stats[ST_F_CONN_TOT].u.u64),
+                             U2H(stats[ST_F_STOT].u.u64));
+
+               /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
+                       chunk_appendf(out,
+                                     "<tr><th>- HTTP/1 sessions:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP/2 sessions:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP/3 sessions:</th><td>%s</td></tr>"
+                                     "<tr><th>- other sessions:</th><td>%s</td></tr>"
+                                     "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP/1 requests:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP/2 requests:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP/3 requests:</th><td>%s</td></tr>"
+                                     "<tr><th>- other requests:</th><td>%s</td></tr>"
+                                     "",
+                                     U2H(stats[ST_F_H1SESS].u.u64),
+                                     U2H(stats[ST_F_H2SESS].u.u64),
+                                     U2H(stats[ST_F_H3SESS].u.u64),
+                                     U2H(stats[ST_F_SESS_OTHER].u.u64),
+                                     U2H(stats[ST_F_REQ_TOT].u.u64),
+                                     U2H(stats[ST_F_H1REQ].u.u64),
+                                     U2H(stats[ST_F_H2REQ].u.u64),
+                                     U2H(stats[ST_F_H3REQ].u.u64),
+                                     U2H(stats[ST_F_REQ_OTHER].u.u64));
+
+                       chunk_appendf(out,
+                                     "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- other responses:</th><td>%s</td></tr>"
+                                     "",
+                                     U2H(stats[ST_F_HRSP_1XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_2XX].u.u64),
+                                     U2H(stats[ST_F_COMP_RSP].u.u64),
+                                     stats[ST_F_HRSP_2XX].u.u64 ?
+                                     (int)(100 * stats[ST_F_COMP_RSP].u.u64 / stats[ST_F_HRSP_2XX].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_3XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_4XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_5XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_OTHER].u.u64));
+
+                       chunk_appendf(out,
+                                     "<tr><th>Intercepted requests:</th><td>%s</td></tr>"
+                                     "<tr><th>Cache lookups:</th><td>%s</td></tr>"
+                                     "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
+                                     "<tr><th>Internal errors:</th><td>%s</td></tr>"
+                                     "",
+                                     U2H(stats[ST_F_INTERCEPTED].u.u64),
+                                     U2H(stats[ST_F_CACHE_LOOKUPS].u.u64),
+                                     U2H(stats[ST_F_CACHE_HITS].u.u64),
+                                     stats[ST_F_CACHE_LOOKUPS].u.u64 ?
+                                     (int)(100 * stats[ST_F_CACHE_HITS].u.u64 / stats[ST_F_CACHE_LOOKUPS].u.u64) : 0,
+                                     U2H(stats[ST_F_WREW].u.u64),
+                                     U2H(stats[ST_F_EINT].u.u64));
+               }
+
+               chunk_appendf(out,
+                             "</table></div></u></td>"
+                             /* sessions: lbtot, lastsess */
+                             "<td></td><td></td>"
+                             /* bytes : in */
+                             "<td>%s</td>"
+                             "",
+                             U2H(stats[ST_F_BIN].u.u64));
+
+               chunk_appendf(out,
+                             /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
+                             "<td>%s%s<div class=tips><table class=det>"
+                             "<tr><th>Response bytes in:</th><td>%s</td></tr>"
+                             "<tr><th>Compression in:</th><td>%s</td></tr>"
+                             "<tr><th>Compression out:</th><td>%s</td><td>(%d%%)</td></tr>"
+                             "<tr><th>Compression bypass:</th><td>%s</td></tr>"
+                             "<tr><th>Total bytes saved:</th><td>%s</td><td>(%d%%)</td></tr>"
+                             "</table></div>%s</td>",
+                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "<u>":"",
+                             U2H(stats[ST_F_BOUT].u.u64),
+                             U2H(stats[ST_F_BOUT].u.u64),
+                             U2H(stats[ST_F_COMP_IN].u.u64),
+                             U2H(stats[ST_F_COMP_OUT].u.u64),
+                             stats[ST_F_COMP_IN].u.u64 ? (int)(stats[ST_F_COMP_OUT].u.u64 * 100 / stats[ST_F_COMP_IN].u.u64) : 0,
+                             U2H(stats[ST_F_COMP_BYP].u.u64),
+                             U2H(stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64),
+                             stats[ST_F_BOUT].u.u64 ? (int)((stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64) * 100 / stats[ST_F_BOUT].u.u64) : 0,
+                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "</u>":"");
+
+               chunk_appendf(out,
+                             /* denied: req, resp */
+                             "<td>%s</td><td>%s</td>"
+                             /* errors : request, connect, response */
+                             "<td>%s</td><td></td><td></td>"
+                             /* warnings: retries, redispatches */
+                             "<td></td><td></td>"
+                             /* server status : reflect frontend status */
+                             "<td class=ac>%s</td>"
+                             /* rest of server: nothing */
+                             "<td class=ac colspan=8></td>"
+                             "",
+                             U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
+                             U2H(stats[ST_F_EREQ].u.u64),
+                             field_str(stats, ST_F_STATUS));
+
+               if (flags & STAT_SHMODULES) {
+                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+                               chunk_appendf(out, "<td>");
+
+                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE) {
+                                       chunk_appendf(out,
+                                                     "<u>%s<div class=tips><table class=det>",
+                                                     mod->name);
+                                       for (j = 0; j < mod->stats_count; ++j) {
+                                               chunk_appendf(out,
+                                                             "<tr><th>%s</th><td>%s</td></tr>",
+                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+                                               ++i;
+                                       }
+                                       chunk_appendf(out, "</table></div></u>");
+                               } else {
+                                       i += mod->stats_count;
+                               }
+
+                               chunk_appendf(out, "</td>");
+                       }
+               }
+
+               chunk_appendf(out, "</tr>");
+       }
+       else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO) {
+               chunk_appendf(out, "<tr class=socket>");
+               if (flags & STAT_ADMIN) {
+                       /* Column sub-heading for Enable or Disable server */
+                       chunk_appendf(out, "<td></td>");
+               }
+
+               chunk_appendf(out,
+                             /* frontend name, listener name */
+                             "<td class=ac><a name=\"%s/+%s\"></a>%s"
+                             "<a class=lfsb href=\"#%s/+%s\">%s</a>"
+                             "",
+                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME),
+                             (flags & STAT_SHLGNDS)?"<u>":"",
+                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME), field_str(stats, ST_F_SVNAME));
+
+               if (flags & STAT_SHLGNDS) {
+                       chunk_appendf(out, "<div class=tips>");
+
+                       if (isdigit((unsigned char)*field_str(stats, ST_F_ADDR)))
+                               chunk_appendf(out, "IPv4: %s, ", field_str(stats, ST_F_ADDR));
+                       else if (*field_str(stats, ST_F_ADDR) == '[')
+                               chunk_appendf(out, "IPv6: %s, ", field_str(stats, ST_F_ADDR));
+                       else if (*field_str(stats, ST_F_ADDR))
+                               chunk_appendf(out, "%s, ", field_str(stats, ST_F_ADDR));
+
+                       chunk_appendf(out, "proto=%s, ", field_str(stats, ST_F_PROTO));
+
+                       /* id */
+                       chunk_appendf(out, "id: %d</div>", stats[ST_F_SID].u.u32);
+               }
+
+               chunk_appendf(out,
+                             /* queue */
+                             "%s</td><td colspan=3></td>"
+                             /* sessions rate: current, max, limit */
+                             "<td colspan=3>&nbsp;</td>"
+                             /* sessions: current, max, limit, total, lbtot, lastsess */
+                             "<td>%s</td><td>%s</td><td>%s</td>"
+                             "<td>%s</td><td>&nbsp;</td><td>&nbsp;</td>"
+                             /* bytes: in, out */
+                             "<td>%s</td><td>%s</td>"
+                             "",
+                             (flags & STAT_SHLGNDS)?"</u>":"",
+                             U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
+                             U2H(stats[ST_F_STOT].u.u64), U2H(stats[ST_F_BIN].u.u64), U2H(stats[ST_F_BOUT].u.u64));
+
+               chunk_appendf(out,
+                             /* denied: req, resp */
+                             "<td>%s</td><td>%s</td>"
+                             /* errors: request, connect, response */
+                             "<td>%s</td><td></td><td></td>"
+                             /* warnings: retries, redispatches */
+                             "<td></td><td></td>"
+                             /* server status: reflect listener status */
+                             "<td class=ac>%s</td>"
+                             /* rest of server: nothing */
+                             "<td class=ac colspan=8></td>"
+                             "",
+                             U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
+                             U2H(stats[ST_F_EREQ].u.u64),
+                             field_str(stats, ST_F_STATUS));
+
+               if (flags & STAT_SHMODULES) {
+                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+                               chunk_appendf(out, "<td>");
+
+                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI) {
+                                       chunk_appendf(out,
+                                                     "<u>%s<div class=tips><table class=det>",
+                                                     mod->name);
+                                       for (j = 0; j < mod->stats_count; ++j) {
+                                               chunk_appendf(out,
+                                                             "<tr><th>%s</th><td>%s</td></tr>",
+                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+                                               ++i;
+                                       }
+                                       chunk_appendf(out, "</table></div></u>");
+                               } else {
+                                       i += mod->stats_count;
+                               }
+
+                               chunk_appendf(out, "</td>");
+                       }
+               }
+
+               chunk_appendf(out, "</tr>");
+       }
+       else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV) {
+               const char *style;
+
+               /* determine the style to use depending on the server's state,
+                * its health and weight. There isn't a 1-to-1 mapping between
+                * state and styles for the cases where the server is (still)
+                * up. The reason is that we don't want to report nolb and
+                * drain with the same color.
+                */
+
+               if (strcmp(field_str(stats, ST_F_STATUS), "DOWN") == 0 ||
+                   strcmp(field_str(stats, ST_F_STATUS), "DOWN (agent)") == 0) {
+                       style = "down";
+               }
+               else if (strncmp(field_str(stats, ST_F_STATUS), "DOWN ", strlen("DOWN ")) == 0) {
+                       style = "going_up";
+               }
+               else if (strcmp(field_str(stats, ST_F_STATUS), "DRAIN") == 0) {
+                       style = "draining";
+               }
+               else if (strncmp(field_str(stats, ST_F_STATUS), "NOLB ", strlen("NOLB ")) == 0) {
+                       style = "going_down";
+               }
+               else if (strcmp(field_str(stats, ST_F_STATUS), "NOLB") == 0) {
+                       style = "nolb";
+               }
+               else if (strcmp(field_str(stats, ST_F_STATUS), "no check") == 0) {
+                       style = "no_check";
+               }
+               else if (!stats[ST_F_CHKFAIL].type ||
+                        stats[ST_F_CHECK_HEALTH].u.u32 == stats[ST_F_CHECK_RISE].u.u32 + stats[ST_F_CHECK_FALL].u.u32 - 1) {
+                       /* no check or max health = UP */
+                       if (stats[ST_F_WEIGHT].u.u32)
+                               style = "up";
+                       else
+                               style = "draining";
+               }
+               else {
+                       style = "going_down";
+               }
+
+               if (strncmp(field_str(stats, ST_F_STATUS), "MAINT", 5) == 0)
+                       chunk_appendf(out, "<tr class=\"maintain\">");
+               else
+                       chunk_appendf(out,
+                                     "<tr class=\"%s_%s\">",
+                                     (stats[ST_F_BCK].u.u32) ? "backup" : "active", style);
+
+
+               if (flags & STAT_ADMIN)
+                       chunk_appendf(out,
+                                     "<td><input class='%s-checkbox' type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
+                                     field_str(stats, ST_F_PXNAME),
+                                     field_str(stats, ST_F_SVNAME));
+
+               chunk_appendf(out,
+                             "<td class=ac><a name=\"%s/%s\"></a>%s"
+                             "<a class=lfsb href=\"#%s/%s\">%s</a>"
+                             "",
+                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME),
+                             (flags & STAT_SHLGNDS) ? "<u>" : "",
+                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME), field_str(stats, ST_F_SVNAME));
+
+               if (flags & STAT_SHLGNDS) {
+                       chunk_appendf(out, "<div class=tips>");
+
+                       if (isdigit((unsigned char)*field_str(stats, ST_F_ADDR)))
+                               chunk_appendf(out, "IPv4: %s, ", field_str(stats, ST_F_ADDR));
+                       else if (*field_str(stats, ST_F_ADDR) == '[')
+                               chunk_appendf(out, "IPv6: %s, ", field_str(stats, ST_F_ADDR));
+                       else if (*field_str(stats, ST_F_ADDR))
+                               chunk_appendf(out, "%s, ", field_str(stats, ST_F_ADDR));
+
+                       /* id */
+                       chunk_appendf(out, "id: %d, rid: %d", stats[ST_F_SID].u.u32, stats[ST_F_SRID].u.u32);
+
+                       /* cookie */
+                       if (stats[ST_F_COOKIE].type) {
+                               chunk_appendf(out, ", cookie: '");
+                               chunk_initstr(&src, field_str(stats, ST_F_COOKIE));
+                               chunk_htmlencode(out, &src);
+                               chunk_appendf(out, "'");
+                       }
+
+                       chunk_appendf(out, "</div>");
+               }
+
+               chunk_appendf(out,
+                             /* queue : current, max, limit */
+                             "%s</td><td>%s</td><td>%s</td><td>%s</td>"
+                             /* sessions rate : current, max, limit */
+                             "<td>%s</td><td>%s</td><td></td>"
+                             "",
+                             (flags & STAT_SHLGNDS) ? "</u>" : "",
+                             U2H(stats[ST_F_QCUR].u.u32), U2H(stats[ST_F_QMAX].u.u32), LIM2A(stats[ST_F_QLIMIT].u.u32, "-"),
+                             U2H(stats[ST_F_RATE].u.u32), U2H(stats[ST_F_RATE_MAX].u.u32));
+
+               chunk_appendf(out,
+                             /* sessions: current, max, limit, total */
+                             "<td><u>%s<div class=tips>"
+                               "<table class=det>"
+                               "<tr><th>Current active connections:</th><td>%s</td></tr>"
+                               "<tr><th>Current used connections:</th><td>%s</td></tr>"
+                               "<tr><th>Current idle connections:</th><td>%s</td></tr>"
+                               "<tr><th>- unsafe:</th><td>%s</td></tr>"
+                               "<tr><th>- safe:</th><td>%s</td></tr>"
+                               "<tr><th>Estimated need of connections:</th><td>%s</td></tr>"
+                               "<tr><th>Active connections limit:</th><td>%s</td></tr>"
+                               "<tr><th>Idle connections limit:</th><td>%s</td></tr>"
+                               "</table></div></u>"
+                             "</td><td>%s</td><td>%s</td>"
+                             "<td><u>%s<div class=tips><table class=det>"
+                             "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
+                             "",
+                             U2H(stats[ST_F_SCUR].u.u32),
+                             U2H(stats[ST_F_SCUR].u.u32),
+                             U2H(stats[ST_F_USED_CONN_CUR].u.u32),
+                             U2H(stats[ST_F_SRV_ICUR].u.u32),
+                             U2H(stats[ST_F_IDLE_CONN_CUR].u.u32),
+                             U2H(stats[ST_F_SAFE_CONN_CUR].u.u32),
+                             U2H(stats[ST_F_NEED_CONN_EST].u.u32),
+
+                               LIM2A(stats[ST_F_SLIM].u.u32, "-"),
+                               stats[ST_F_SRV_ILIM].type ? U2H(stats[ST_F_SRV_ILIM].u.u32) : "-",
+                             U2H(stats[ST_F_SMAX].u.u32), LIM2A(stats[ST_F_SLIM].u.u32, "-"),
+                             U2H(stats[ST_F_STOT].u.u64),
+                             U2H(stats[ST_F_STOT].u.u64));
+
+               /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
+                       chunk_appendf(out,
+                                     "<tr><th>New connections:</th><td>%s</td></tr>"
+                                     "<tr><th>Reused connections:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 1xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- HTTP 2xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- HTTP 3xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- HTTP 4xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- HTTP 5xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- other responses:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
+                                     "<tr><th>Internal error:</th><td>%s</td></tr>"
+                                     "",
+                                     U2H(stats[ST_F_CONNECT].u.u64),
+                                     U2H(stats[ST_F_REUSE].u.u64),
+                                     (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64) ?
+                                     (int)(100 * stats[ST_F_REUSE].u.u64 / (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64)) : 0,
+                                     U2H(stats[ST_F_REQ_TOT].u.u64),
+                                     U2H(stats[ST_F_HRSP_1XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
+                                     (int)(100 * stats[ST_F_HRSP_1XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_2XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
+                                     (int)(100 * stats[ST_F_HRSP_2XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_3XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
+                                     (int)(100 * stats[ST_F_HRSP_3XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_4XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
+                                     (int)(100 * stats[ST_F_HRSP_4XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_5XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
+                                     (int)(100 * stats[ST_F_HRSP_5XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_OTHER].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
+                                     (int)(100 * stats[ST_F_HRSP_OTHER].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
+                                     U2H(stats[ST_F_WREW].u.u64),
+                                     U2H(stats[ST_F_EINT].u.u64));
+               }
+
+               chunk_appendf(out, "<tr><th colspan=3>Max / Avg over last 1024 success. conn.</th></tr>");
+               chunk_appendf(out, "<tr><th>- Queue time:</th><td>%s / %s</td><td>ms</td></tr>",
+                             U2H(stats[ST_F_QT_MAX].u.u32), U2H(stats[ST_F_QTIME].u.u32));
+               chunk_appendf(out, "<tr><th>- Connect time:</th><td>%s / %s</td><td>ms</td></tr>",
+                             U2H(stats[ST_F_CT_MAX].u.u32), U2H(stats[ST_F_CTIME].u.u32));
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
+                       chunk_appendf(out, "<tr><th>- Responses time:</th><td>%s / %s</td><td>ms</td></tr>",
+                                     U2H(stats[ST_F_RT_MAX].u.u32), U2H(stats[ST_F_RTIME].u.u32));
+               chunk_appendf(out, "<tr><th>- Total time:</th><td>%s / %s</td><td>ms</td></tr>",
+                             U2H(stats[ST_F_TT_MAX].u.u32), U2H(stats[ST_F_TTIME].u.u32));
+
+               chunk_appendf(out,
+                             "</table></div></u></td>"
+                             /* sessions: lbtot, last */
+                             "<td>%s</td><td>%s</td>",
+                             U2H(stats[ST_F_LBTOT].u.u64),
+                             human_time(stats[ST_F_LASTSESS].u.s32, 1));
+
+               chunk_appendf(out,
+                             /* bytes : in, out */
+                             "<td>%s</td><td>%s</td>"
+                             /* denied: req, resp */
+                             "<td></td><td>%s</td>"
+                             /* errors : request, connect */
+                             "<td></td><td>%s</td>"
+                             /* errors : response */
+                             "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
+                             /* warnings: retries, redispatches */
+                             "<td>%lld</td><td>%lld</td>"
+                             "",
+                             U2H(stats[ST_F_BIN].u.u64), U2H(stats[ST_F_BOUT].u.u64),
+                             U2H(stats[ST_F_DRESP].u.u64),
+                             U2H(stats[ST_F_ECON].u.u64),
+                             U2H(stats[ST_F_ERESP].u.u64),
+                             (long long)stats[ST_F_CLI_ABRT].u.u64,
+                             (long long)stats[ST_F_SRV_ABRT].u.u64,
+                             (long long)stats[ST_F_WRETR].u.u64,
+                             (long long)stats[ST_F_WREDIS].u.u64);
+
+               /* status, last change */
+               chunk_appendf(out, "<td class=ac>");
+
+               /* FIXME!!!!
+                *   LASTCHG should contain the last change for *this* server and must be computed
+                * properly above, as was done below, ie: this server if maint, otherwise ref server
+                * if tracking. Note that ref is either local or remote depending on tracking.
+                */
+
+
+               if (strncmp(field_str(stats, ST_F_STATUS), "MAINT", 5) == 0) {
+                       chunk_appendf(out, "%s MAINT", human_time(stats[ST_F_LASTCHG].u.u32, 1));
+               }
+               else if (strcmp(field_str(stats, ST_F_STATUS), "no check") == 0) {
+                       chunk_strcat(out, "<i>no check</i>");
+               }
+               else {
+                       chunk_appendf(out, "%s %s", human_time(stats[ST_F_LASTCHG].u.u32, 1), field_str(stats, ST_F_STATUS));
+                       if (strncmp(field_str(stats, ST_F_STATUS), "DOWN", 4) == 0) {
+                               if (stats[ST_F_CHECK_HEALTH].u.u32)
+                                       chunk_strcat(out, " &uarr;");
+                       }
+                       else if (stats[ST_F_CHECK_HEALTH].u.u32 < stats[ST_F_CHECK_RISE].u.u32 + stats[ST_F_CHECK_FALL].u.u32 - 1)
+                               chunk_strcat(out, " &darr;");
+               }
+
+               if (strncmp(field_str(stats, ST_F_STATUS), "DOWN", 4) == 0 &&
+                   stats[ST_F_AGENT_STATUS].type && !stats[ST_F_AGENT_HEALTH].u.u32) {
+                       chunk_appendf(out,
+                                     "</td><td class=ac><u> %s",
+                                     field_str(stats, ST_F_AGENT_STATUS));
+
+                       if (stats[ST_F_AGENT_CODE].type)
+                               chunk_appendf(out, "/%d", stats[ST_F_AGENT_CODE].u.u32);
+
+                       if (stats[ST_F_AGENT_DURATION].type)
+                               chunk_appendf(out, " in %lums", (long)stats[ST_F_AGENT_DURATION].u.u64);
+
+                       chunk_appendf(out, "<div class=tips>%s", field_str(stats, ST_F_AGENT_DESC));
+
+                       if (*field_str(stats, ST_F_LAST_AGT)) {
+                               chunk_appendf(out, ": ");
+                               chunk_initstr(&src, field_str(stats, ST_F_LAST_AGT));
+                               chunk_htmlencode(out, &src);
+                       }
+                       chunk_appendf(out, "</div></u>");
+               }
+               else if (stats[ST_F_CHECK_STATUS].type) {
+                       chunk_appendf(out,
+                                     "</td><td class=ac><u> %s",
+                                     field_str(stats, ST_F_CHECK_STATUS));
+
+                       if (stats[ST_F_CHECK_CODE].type)
+                               chunk_appendf(out, "/%d", stats[ST_F_CHECK_CODE].u.u32);
+
+                       if (stats[ST_F_CHECK_DURATION].type)
+                               chunk_appendf(out, " in %lums", (long)stats[ST_F_CHECK_DURATION].u.u64);
+
+                       chunk_appendf(out, "<div class=tips>%s", field_str(stats, ST_F_CHECK_DESC));
+
+                       if (*field_str(stats, ST_F_LAST_CHK)) {
+                               chunk_appendf(out, ": ");
+                               chunk_initstr(&src, field_str(stats, ST_F_LAST_CHK));
+                               chunk_htmlencode(out, &src);
+                       }
+                       chunk_appendf(out, "</div></u>");
+               }
+               else
+                       chunk_appendf(out, "</td><td>");
+
+               chunk_appendf(out,
+                             /* weight / uweight */
+                             "</td><td class=ac>%d/%d</td>"
+                             /* act, bck */
+                             "<td class=ac>%s</td><td class=ac>%s</td>"
+                             "",
+                             stats[ST_F_WEIGHT].u.u32, stats[ST_F_UWEIGHT].u.u32,
+                             stats[ST_F_BCK].u.u32 ? "-" : "Y",
+                             stats[ST_F_BCK].u.u32 ? "Y" : "-");
+
+               /* check failures: unique, fatal, down time */
+               if (strcmp(field_str(stats, ST_F_STATUS), "MAINT (resolution)") == 0) {
+                       chunk_appendf(out, "<td class=ac colspan=3>resolution</td>");
+               }
+               else if (stats[ST_F_CHKFAIL].type) {
+                       chunk_appendf(out, "<td><u>%lld", (long long)stats[ST_F_CHKFAIL].u.u64);
+
+                       if (stats[ST_F_HANAFAIL].type)
+                               chunk_appendf(out, "/%lld", (long long)stats[ST_F_HANAFAIL].u.u64);
+
+                       chunk_appendf(out,
+                                     "<div class=tips>Failed Health Checks%s</div></u></td>"
+                                     "<td>%lld</td><td>%s</td>"
+                                     "",
+                                     stats[ST_F_HANAFAIL].type ? "/Health Analyses" : "",
+                                     (long long)stats[ST_F_CHKDOWN].u.u64, human_time(stats[ST_F_DOWNTIME].u.u32, 1));
+               }
+               else if (strcmp(field_str(stats, ST_F_STATUS), "MAINT") != 0 && field_format(stats, ST_F_TRACKED) == FF_STR) {
+                       /* tracking a server (hence inherited maint would appear as "MAINT (via...)" */
+                       chunk_appendf(out,
+                                     "<td class=ac colspan=3><a class=lfsb href=\"#%s\">via %s</a></td>",
+                                     field_str(stats, ST_F_TRACKED), field_str(stats, ST_F_TRACKED));
+               }
+               else
+                       chunk_appendf(out, "<td colspan=3></td>");
+
+               /* throttle */
+               if (stats[ST_F_THROTTLE].type)
+                       chunk_appendf(out, "<td class=ac>%d %%</td>\n", stats[ST_F_THROTTLE].u.u32);
+               else
+                       chunk_appendf(out, "<td class=ac>-</td>");
+
+               if (flags & STAT_SHMODULES) {
+                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+                               chunk_appendf(out, "<td>");
+
+                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV) {
+                                       chunk_appendf(out,
+                                                     "<u>%s<div class=tips><table class=det>",
+                                                     mod->name);
+                                       for (j = 0; j < mod->stats_count; ++j) {
+                                               chunk_appendf(out,
+                                                             "<tr><th>%s</th><td>%s</td></tr>",
+                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+                                               ++i;
+                                       }
+                                       chunk_appendf(out, "</table></div></u>");
+                               } else {
+                                       i += mod->stats_count;
+                               }
+
+                               chunk_appendf(out, "</td>");
+                       }
+               }
+
+               chunk_appendf(out, "</tr>\n");
+       }
+       else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE) {
+               chunk_appendf(out, "<tr class=\"backend\">");
+               if (flags & STAT_ADMIN) {
+                       /* Column sub-heading for Enable or Disable server */
+                       chunk_appendf(out, "<td></td>");
+               }
+               chunk_appendf(out,
+                             "<td class=ac>"
+                             /* name */
+                             "%s<a name=\"%s/Backend\"></a>"
+                             "<a class=lfsb href=\"#%s/Backend\">Backend</a>"
+                             "",
+                             (flags & STAT_SHLGNDS)?"<u>":"",
+                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_PXNAME));
+
+               if (flags & STAT_SHLGNDS) {
+                       /* balancing */
+                       chunk_appendf(out, "<div class=tips>balancing: %s",
+                                     field_str(stats, ST_F_ALGO));
+
+                       /* cookie */
+                       if (stats[ST_F_COOKIE].type) {
+                               chunk_appendf(out, ", cookie: '");
+                               chunk_initstr(&src, field_str(stats, ST_F_COOKIE));
+                               chunk_htmlencode(out, &src);
+                               chunk_appendf(out, "'");
+                       }
+                       chunk_appendf(out, "</div>");
+               }
+
+               chunk_appendf(out,
+                             "%s</td>"
+                             /* queue : current, max */
+                             "<td>%s</td><td>%s</td><td></td>"
+                             /* sessions rate : current, max, limit */
+                             "<td>%s</td><td>%s</td><td></td>"
+                             "",
+                             (flags & STAT_SHLGNDS)?"</u>":"",
+                             U2H(stats[ST_F_QCUR].u.u32), U2H(stats[ST_F_QMAX].u.u32),
+                             U2H(stats[ST_F_RATE].u.u32), U2H(stats[ST_F_RATE_MAX].u.u32));
+
+               chunk_appendf(out,
+                             /* sessions: current, max, limit, total */
+                             "<td>%s</td><td>%s</td><td>%s</td>"
+                             "<td><u>%s<div class=tips><table class=det>"
+                             "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
+                             "",
+                             U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
+                             U2H(stats[ST_F_STOT].u.u64),
+                             U2H(stats[ST_F_STOT].u.u64));
+
+               /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
+                       chunk_appendf(out,
+                                     "<tr><th>New connections:</th><td>%s</td></tr>"
+                                     "<tr><th>Reused connections:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
+                                     "<tr><th>- other responses:</th><td>%s</td></tr>"
+                                     "<tr><th>Cache lookups:</th><td>%s</td></tr>"
+                                     "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
+                                     "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
+                                     "<tr><th>Internal errors:</th><td>%s</td></tr>"
+                                     "",
+                                     U2H(stats[ST_F_CONNECT].u.u64),
+                                     U2H(stats[ST_F_REUSE].u.u64),
+                                     (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64) ?
+                                     (int)(100 * stats[ST_F_REUSE].u.u64 / (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64)) : 0,
+                                     U2H(stats[ST_F_REQ_TOT].u.u64),
+                                     U2H(stats[ST_F_HRSP_1XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_2XX].u.u64),
+                                     U2H(stats[ST_F_COMP_RSP].u.u64),
+                                     stats[ST_F_HRSP_2XX].u.u64 ?
+                                     (int)(100 * stats[ST_F_COMP_RSP].u.u64 / stats[ST_F_HRSP_2XX].u.u64) : 0,
+                                     U2H(stats[ST_F_HRSP_3XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_4XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_5XX].u.u64),
+                                     U2H(stats[ST_F_HRSP_OTHER].u.u64),
+                                     U2H(stats[ST_F_CACHE_LOOKUPS].u.u64),
+                                     U2H(stats[ST_F_CACHE_HITS].u.u64),
+                                     stats[ST_F_CACHE_LOOKUPS].u.u64 ?
+                                     (int)(100 * stats[ST_F_CACHE_HITS].u.u64 / stats[ST_F_CACHE_LOOKUPS].u.u64) : 0,
+                                     U2H(stats[ST_F_WREW].u.u64),
+                                     U2H(stats[ST_F_EINT].u.u64));
+               }
+
+               chunk_appendf(out, "<tr><th colspan=3>Max / Avg over last 1024 success. conn.</th></tr>");
+               chunk_appendf(out, "<tr><th>- Queue time:</th><td>%s / %s</td><td>ms</td></tr>",
+                             U2H(stats[ST_F_QT_MAX].u.u32), U2H(stats[ST_F_QTIME].u.u32));
+               chunk_appendf(out, "<tr><th>- Connect time:</th><td>%s / %s</td><td>ms</td></tr>",
+                             U2H(stats[ST_F_CT_MAX].u.u32), U2H(stats[ST_F_CTIME].u.u32));
+               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
+                       chunk_appendf(out, "<tr><th>- Responses time:</th><td>%s / %s</td><td>ms</td></tr>",
+                                     U2H(stats[ST_F_RT_MAX].u.u32), U2H(stats[ST_F_RTIME].u.u32));
+               chunk_appendf(out, "<tr><th>- Total time:</th><td>%s / %s</td><td>ms</td></tr>",
+                             U2H(stats[ST_F_TT_MAX].u.u32), U2H(stats[ST_F_TTIME].u.u32));
+
+               chunk_appendf(out,
+                             "</table></div></u></td>"
+                             /* sessions: lbtot, last */
+                             "<td>%s</td><td>%s</td>"
+                             /* bytes: in */
+                             "<td>%s</td>"
+                             "",
+                             U2H(stats[ST_F_LBTOT].u.u64),
+                             human_time(stats[ST_F_LASTSESS].u.s32, 1),
+                             U2H(stats[ST_F_BIN].u.u64));
+
+               chunk_appendf(out,
+                             /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
+                             "<td>%s%s<div class=tips><table class=det>"
+                             "<tr><th>Response bytes in:</th><td>%s</td></tr>"
+                             "<tr><th>Compression in:</th><td>%s</td></tr>"
+                             "<tr><th>Compression out:</th><td>%s</td><td>(%d%%)</td></tr>"
+                             "<tr><th>Compression bypass:</th><td>%s</td></tr>"
+                             "<tr><th>Total bytes saved:</th><td>%s</td><td>(%d%%)</td></tr>"
+                             "</table></div>%s</td>",
+                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "<u>":"",
+                             U2H(stats[ST_F_BOUT].u.u64),
+                             U2H(stats[ST_F_BOUT].u.u64),
+                             U2H(stats[ST_F_COMP_IN].u.u64),
+                             U2H(stats[ST_F_COMP_OUT].u.u64),
+                             stats[ST_F_COMP_IN].u.u64 ? (int)(stats[ST_F_COMP_OUT].u.u64 * 100 / stats[ST_F_COMP_IN].u.u64) : 0,
+                             U2H(stats[ST_F_COMP_BYP].u.u64),
+                             U2H(stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64),
+                             stats[ST_F_BOUT].u.u64 ? (int)((stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64) * 100 / stats[ST_F_BOUT].u.u64) : 0,
+                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "</u>":"");
+
+               chunk_appendf(out,
+                             /* denied: req, resp */
+                             "<td>%s</td><td>%s</td>"
+                             /* errors : request, connect */
+                             "<td></td><td>%s</td>"
+                             /* errors : response */
+                             "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
+                             /* warnings: retries, redispatches */
+                             "<td>%lld</td><td>%lld</td>"
+                             /* backend status: reflect backend status (up/down): we display UP
+                              * if the backend has known working servers or if it has no server at
+                              * all (eg: for stats). Then we display the total weight, number of
+                              * active and backups. */
+                             "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d/%d</td>"
+                             "<td class=ac>%d</td><td class=ac>%d</td>"
+                             "",
+                             U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
+                             U2H(stats[ST_F_ECON].u.u64),
+                             U2H(stats[ST_F_ERESP].u.u64),
+                             (long long)stats[ST_F_CLI_ABRT].u.u64,
+                             (long long)stats[ST_F_SRV_ABRT].u.u64,
+                             (long long)stats[ST_F_WRETR].u.u64, (long long)stats[ST_F_WREDIS].u.u64,
+                             human_time(stats[ST_F_LASTCHG].u.u32, 1),
+                             strcmp(field_str(stats, ST_F_STATUS), "DOWN") ? field_str(stats, ST_F_STATUS) : "<font color=\"red\"><b>DOWN</b></font>",
+                             stats[ST_F_WEIGHT].u.u32, stats[ST_F_UWEIGHT].u.u32,
+                             stats[ST_F_ACT].u.u32, stats[ST_F_BCK].u.u32);
+
+               chunk_appendf(out,
+                             /* rest of backend: nothing, down transitions, total downtime, throttle */
+                             "<td class=ac>&nbsp;</td><td>%d</td>"
+                             "<td>%s</td>"
+                             "<td></td>",
+                             stats[ST_F_CHKDOWN].u.u32,
+                             stats[ST_F_DOWNTIME].type ? human_time(stats[ST_F_DOWNTIME].u.u32, 1) : "&nbsp;");
+
+               if (flags & STAT_SHMODULES) {
+                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+                               chunk_appendf(out, "<td>");
+
+                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE) {
+                                       chunk_appendf(out,
+                                                     "<u>%s<div class=tips><table class=det>",
+                                                     mod->name);
+                                       for (j = 0; j < mod->stats_count; ++j) {
+                                               chunk_appendf(out,
+                                                             "<tr><th>%s</th><td>%s</td></tr>",
+                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+                                               ++i;
+                                       }
+                                       chunk_appendf(out, "</table></div></u>");
+                               } else {
+                                       i += mod->stats_count;
+                               }
+
+                               chunk_appendf(out, "</td>");
+                       }
+               }
+
+               chunk_appendf(out, "</tr>");
+       }
+
+       return 1;
+}
+
+/* Dumps the HTML table header for proxy <px> to chunk ctx buffer and uses the
+ * state from stream connector <sc>. The caller is responsible for clearing
+ * chunk ctx buffer if needed.
+ */
+void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px)
+{
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct buffer *chk = &ctx->chunk;
+       char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
+       struct stats_module *mod;
+       int stats_module_len = 0;
+
+       if (px->cap & PR_CAP_BE && px->srv && (ctx->flags & STAT_ADMIN)) {
+               /* A form to enable/disable this proxy servers */
+
+               /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+               scope_txt[0] = 0;
+               if (ctx->scope_len) {
+                       const char *scope_ptr = stats_scope_ptr(appctx);
+
+                       strlcpy2(scope_txt, STAT_SCOPE_PATTERN, sizeof(scope_txt));
+                       memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
+                       scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
+               }
+
+               chunk_appendf(chk,
+                             "<form method=\"post\">");
+       }
+
+       /* print a new table */
+       chunk_appendf(chk,
+                     "<table class=\"tbl\" width=\"100%%\">\n"
+                     "<tr class=\"titre\">"
+                     "<th class=\"pxname\" width=\"10%%\">");
+
+       chunk_appendf(chk,
+                     "<a name=\"%s\"></a>%s"
+                     "<a class=px href=\"#%s\">%s</a>",
+                     px->id,
+                     (ctx->flags & STAT_SHLGNDS) ? "<u>":"",
+                     px->id, px->id);
+
+       if (ctx->flags & STAT_SHLGNDS) {
+               /* cap, mode, id */
+               chunk_appendf(chk, "<div class=tips>cap: %s, mode: %s, id: %d",
+                             proxy_cap_str(px->cap), proxy_mode_str(px->mode),
+                             px->uuid);
+               chunk_appendf(chk, "</div>");
+       }
+
+       chunk_appendf(chk,
+                     "%s</th>"
+                     "<th class=\"%s\" width=\"90%%\">%s</th>"
+                     "</tr>\n"
+                     "</table>\n"
+                     "<table class=\"tbl\" width=\"100%%\">\n"
+                     "<tr class=\"titre\">",
+                     (ctx->flags & STAT_SHLGNDS) ? "</u>":"",
+                     px->desc ? "desc" : "empty", px->desc ? px->desc : "");
+
+       if (ctx->flags & STAT_ADMIN) {
+               /* Column heading for Enable or Disable server */
+               if ((px->cap & PR_CAP_BE) && px->srv)
+                       chunk_appendf(chk,
+                                     "<th rowspan=2 width=1><input type=\"checkbox\" "
+                                     "onclick=\"for(c in document.getElementsByClassName('%s-checkbox')) "
+                                     "document.getElementsByClassName('%s-checkbox').item(c).checked = this.checked\"></th>",
+                                     px->id,
+                                     px->id);
+               else
+                       chunk_appendf(chk, "<th rowspan=2></th>");
+       }
+
+       chunk_appendf(chk,
+                     "<th rowspan=2></th>"
+                     "<th colspan=3>Queue</th>"
+                     "<th colspan=3>Session rate</th><th colspan=6>Sessions</th>"
+                     "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
+                     "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
+                     "<th colspan=9>Server</th>");
+
+       if (ctx->flags & STAT_SHMODULES) {
+               // calculate the count of module for colspan attribute
+               list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+                       ++stats_module_len;
+               }
+               chunk_appendf(chk, "<th colspan=%d>Extra modules</th>",
+                             stats_module_len);
+       }
+
+       chunk_appendf(chk,
+                     "</tr>\n"
+                     "<tr class=\"titre\">"
+                     "<th>Cur</th><th>Max</th><th>Limit</th>"
+                     "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
+                     "<th>Limit</th><th>Total</th><th>LbTot</th><th>Last</th><th>In</th><th>Out</th>"
+                     "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
+                     "<th>Resp</th><th>Retr</th><th>Redis</th>"
+                     "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
+                     "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
+                     "<th>Thrtle</th>\n");
+
+       if (ctx->flags & STAT_SHMODULES) {
+               list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+                       chunk_appendf(chk, "<th>%s</th>", mod->name);
+               }
+       }
+
+       chunk_appendf(chk, "</tr>");
+}
+
+/* Dumps the HTML table trailer for proxy <px> to chunk ctx buffer and uses the
+ * state from stream connector <sc>. The caller is responsible for clearing
+ * chunk ctx buffer if needed.
+ */
+void stats_dump_html_px_end(struct stconn *sc, struct proxy *px)
+{
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct buffer *chk = &ctx->chunk;
+
+       chunk_appendf(chk, "</table>");
+
+       if ((px->cap & PR_CAP_BE) && px->srv && (ctx->flags & STAT_ADMIN)) {
+               /* close the form used to enable/disable this proxy servers */
+               chunk_appendf(chk,
+                             "Choose the action to perform on the checked servers : "
+                             "<select name=action>"
+                             "<option value=\"\"></option>"
+                             "<option value=\"ready\">Set state to READY</option>"
+                             "<option value=\"drain\">Set state to DRAIN</option>"
+                             "<option value=\"maint\">Set state to MAINT</option>"
+                             "<option value=\"dhlth\">Health: disable checks</option>"
+                             "<option value=\"ehlth\">Health: enable checks</option>"
+                             "<option value=\"hrunn\">Health: force UP</option>"
+                             "<option value=\"hnolb\">Health: force NOLB</option>"
+                             "<option value=\"hdown\">Health: force DOWN</option>"
+                             "<option value=\"dagent\">Agent: disable checks</option>"
+                             "<option value=\"eagent\">Agent: enable checks</option>"
+                             "<option value=\"arunn\">Agent: force UP</option>"
+                             "<option value=\"adown\">Agent: force DOWN</option>"
+                             "<option value=\"shutdown\">Kill Sessions</option>"
+                             "</select>"
+                             "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
+                             "&nbsp;<input type=\"submit\" value=\"Apply\">"
+                             "</form>",
+                             px->uuid);
+       }
+
+       chunk_appendf(chk, "<p>\n");
+}
+
+/* Dumps the HTML stats trailer block to <out> buffer. The caller is
+ * responsible for clearing it if needed.
+ */
+void stats_dump_html_end(struct buffer *out)
+{
+       chunk_appendf(out, "</body></html>\n");
+}
+
+
+static int stats_send_http_headers(struct stconn *sc, struct htx *htx)
+{
+       struct uri_auth *uri;
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct htx_sl *sl;
+       unsigned int flags;
+
+       BUG_ON(!ctx->http_px);
+       uri = ctx->http_px->uri_auth;
+
+       flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_ENC|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
+       sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("200"), ist("OK"));
+       if (!sl)
+               goto full;
+       sl->info.res.status = 200;
+
+       if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")))
+               goto full;
+       if (ctx->flags & STAT_FMT_HTML) {
+               if (!htx_add_header(htx, ist("Content-Type"), ist("text/html")))
+                       goto full;
+       }
+       else if (ctx->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;
+       }
+
+       if (uri->refresh > 0 && !(ctx->flags & STAT_NO_REFRESH)) {
+               const char *refresh = U2A(uri->refresh);
+               if (!htx_add_header(htx, ist("Refresh"), ist(refresh)))
+                       goto full;
+       }
+
+       if (ctx->flags & STAT_CHUNKED) {
+               if (!htx_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
+                       goto full;
+       }
+
+       if (!htx_add_endof(htx, HTX_BLK_EOH))
+               goto full;
+       return 1;
+
+  full:
+       htx_reset(htx);
+       applet_set_eos(appctx);
+       applet_set_error(appctx);
+       return 0;
+}
+
+static int stats_send_http_redirect(struct stconn *sc, struct htx *htx)
+{
+       char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
+       struct uri_auth *uri;
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct htx_sl *sl;
+       unsigned int flags;
+
+       BUG_ON(!ctx->http_px);
+       uri = ctx->http_px->uri_auth;
+
+       /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
+       scope_txt[0] = 0;
+       if (ctx->scope_len) {
+               const char *scope_ptr = stats_scope_ptr(appctx);
+
+               strlcpy2(scope_txt, STAT_SCOPE_PATTERN, sizeof(scope_txt));
+               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
+               scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
+       }
+
+       /* We don't want to land on the posted stats page because a refresh will
+        * repost the data. We don't want this to happen on accident so we redirect
+        * the browse to the stats page with a GET.
+        */
+       chunk_printf(&trash, "%s;st=%s%s%s%s",
+                    uri->uri_prefix,
+                    ((ctx->st_code > STAT_STATUS_INIT) &&
+                     (ctx->st_code < STAT_STATUS_SIZE) &&
+                     stat_status_codes[ctx->st_code]) ?
+                    stat_status_codes[ctx->st_code] :
+                    stat_status_codes[STAT_STATUS_UNKN],
+                    (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
+                    (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
+                    scope_txt);
+
+       flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN|HTX_SL_F_CHNK);
+       sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("303"), ist("See Other"));
+       if (!sl)
+               goto full;
+       sl->info.res.status = 303;
+
+       if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
+           !htx_add_header(htx, ist("Content-Type"), ist("text/plain")) ||
+           !htx_add_header(htx, ist("Content-Length"), ist("0")) ||
+           !htx_add_header(htx, ist("Location"), ist2(trash.area, trash.data)))
+               goto full;
+
+       if (!htx_add_endof(htx, HTX_BLK_EOH))
+               goto full;
+
+       return 1;
+
+  full:
+       htx_reset(htx);
+       applet_set_eos(appctx);
+       applet_set_error(appctx);
+       return 0;
+}
+
+/* We reached the stats page through a POST request. The appctx is
+ * expected to have already been allocated by the caller.
+ * Parse the posted data and enable/disable servers if necessary.
+ * Returns 1 if request was parsed or zero if it needs more data.
+ */
+static int stats_process_http_post(struct stconn *sc)
+{
+       struct appctx *appctx = __sc_appctx(sc);
+       struct show_stat_ctx *ctx = appctx->svcctx;
+
+       struct proxy *px = NULL;
+       struct server *sv = NULL;
+
+       char key[LINESIZE];
+       int action = ST_ADM_ACTION_NONE;
+       int reprocess = 0;
+
+       int total_servers = 0;
+       int altered_servers = 0;
+
+       char *first_param, *cur_param, *next_param, *end_params;
+       char *st_cur_param = NULL;
+       char *st_next_param = NULL;
+
+       struct buffer *temp = get_trash_chunk();
+
+       struct htx *htx = htxbuf(&appctx->inbuf);
+       struct htx_blk *blk;
+
+       /*  we need more data */
+       if (!(htx->flags & HTX_FL_EOM)) {
+               /* check if we can receive more */
+               if (applet_fl_test(appctx, APPCTX_FL_INBLK_FULL)) {
+                       ctx->st_code = STAT_STATUS_EXCD;
+                       goto out;
+               }
+               goto  wait;
+       }
+
+       /* The request was fully received. Copy data */
+       blk = htx_get_head_blk(htx);
+       while (blk) {
+               enum htx_blk_type type = htx_get_blk_type(blk);
+
+               if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
+                       break;
+               if (type == HTX_BLK_DATA) {
+                       struct ist v = htx_get_blk_value(htx, blk);
+
+                       if (!chunk_memcat(temp, v.ptr, v.len)) {
+                               ctx->st_code = STAT_STATUS_EXCD;
+                               goto out;
+                       }
+               }
+               blk = htx_get_next_blk(htx, blk);
+       }
+
+       first_param = temp->area;
+       end_params  = temp->area + temp->data;
+       cur_param = next_param = end_params;
+       *end_params = '\0';
+
+       ctx->st_code = STAT_STATUS_NONE;
+
+       /*
+        * Parse the parameters in reverse order to only store the last value.
+        * From the html form, the backend and the action are at the end.
+        */
+       while (cur_param > first_param) {
+               char *value;
+               int poffset, plen;
+
+               cur_param--;
+
+               if ((*cur_param == '&') || (cur_param == first_param)) {
+               reprocess_servers:
+                       /* Parse the key */
+                       poffset = (cur_param != first_param ? 1 : 0);
+                       plen = next_param - cur_param + (cur_param == first_param ? 1 : 0);
+                       if ((plen > 0) && (plen <= sizeof(key))) {
+                               strncpy(key, cur_param + poffset, plen);
+                               key[plen - 1] = '\0';
+                       } else {
+                               ctx->st_code = STAT_STATUS_ERRP;
+                               goto out;
+                       }
+
+                       /* Parse the value */
+                       value = key;
+                       while (*value != '\0' && *value != '=') {
+                               value++;
+                       }
+                       if (*value == '=') {
+                               /* Ok, a value is found, we can mark the end of the key */
+                               *value++ = '\0';
+                       }
+                       if (url_decode(key, 1) < 0 || url_decode(value, 1) < 0)
+                               break;
+
+                       /* Now we can check the key to see what to do */
+                       if (!px && (strcmp(key, "b") == 0)) {
+                               if ((px = proxy_be_by_name(value)) == NULL) {
+                                       /* the backend name is unknown or ambiguous (duplicate names) */
+                                       ctx->st_code = STAT_STATUS_ERRP;
+                                       goto out;
+                               }
+                       }
+                       else if (!action && (strcmp(key, "action") == 0)) {
+                               if (strcmp(value, "ready") == 0) {
+                                       action = ST_ADM_ACTION_READY;
+                               }
+                               else if (strcmp(value, "drain") == 0) {
+                                       action = ST_ADM_ACTION_DRAIN;
+                               }
+                               else if (strcmp(value, "maint") == 0) {
+                                       action = ST_ADM_ACTION_MAINT;
+                               }
+                               else if (strcmp(value, "shutdown") == 0) {
+                                       action = ST_ADM_ACTION_SHUTDOWN;
+                               }
+                               else if (strcmp(value, "dhlth") == 0) {
+                                       action = ST_ADM_ACTION_DHLTH;
+                               }
+                               else if (strcmp(value, "ehlth") == 0) {
+                                       action = ST_ADM_ACTION_EHLTH;
+                               }
+                               else if (strcmp(value, "hrunn") == 0) {
+                                       action = ST_ADM_ACTION_HRUNN;
+                               }
+                               else if (strcmp(value, "hnolb") == 0) {
+                                       action = ST_ADM_ACTION_HNOLB;
+                               }
+                               else if (strcmp(value, "hdown") == 0) {
+                                       action = ST_ADM_ACTION_HDOWN;
+                               }
+                               else if (strcmp(value, "dagent") == 0) {
+                                       action = ST_ADM_ACTION_DAGENT;
+                               }
+                               else if (strcmp(value, "eagent") == 0) {
+                                       action = ST_ADM_ACTION_EAGENT;
+                               }
+                               else if (strcmp(value, "arunn") == 0) {
+                                       action = ST_ADM_ACTION_ARUNN;
+                               }
+                               else if (strcmp(value, "adown") == 0) {
+                                       action = ST_ADM_ACTION_ADOWN;
+                               }
+                               /* else these are the old supported methods */
+                               else if (strcmp(value, "disable") == 0) {
+                                       action = ST_ADM_ACTION_DISABLE;
+                               }
+                               else if (strcmp(value, "enable") == 0) {
+                                       action = ST_ADM_ACTION_ENABLE;
+                               }
+                               else if (strcmp(value, "stop") == 0) {
+                                       action = ST_ADM_ACTION_STOP;
+                               }
+                               else if (strcmp(value, "start") == 0) {
+                                       action = ST_ADM_ACTION_START;
+                               }
+                               else {
+                                       ctx->st_code = STAT_STATUS_ERRP;
+                                       goto out;
+                               }
+                       }
+                       else if (strcmp(key, "s") == 0) {
+                               if (!(px && action)) {
+                                       /*
+                                        * Indicates that we'll need to reprocess the parameters
+                                        * as soon as backend and action are known
+                                        */
+                                       if (!reprocess) {
+                                               st_cur_param  = cur_param;
+                                               st_next_param = next_param;
+                                       }
+                                       reprocess = 1;
+                               }
+                               else if ((sv = findserver(px, value)) != NULL) {
+                                       HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
+                                       switch (action) {
+                                       case ST_ADM_ACTION_DISABLE:
+                                               if (!(sv->cur_admin & SRV_ADMF_FMAINT)) {
+                                                       altered_servers++;
+                                                       total_servers++;
+                                                       srv_set_admin_flag(sv, SRV_ADMF_FMAINT, SRV_ADM_STCHGC_STATS_DISABLE);
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_ENABLE:
+                                               if (sv->cur_admin & SRV_ADMF_FMAINT) {
+                                                       altered_servers++;
+                                                       total_servers++;
+                                                       srv_clr_admin_flag(sv, SRV_ADMF_FMAINT);
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_STOP:
+                                               if (!(sv->cur_admin & SRV_ADMF_FDRAIN)) {
+                                                       srv_set_admin_flag(sv, SRV_ADMF_FDRAIN, SRV_ADM_STCHGC_STATS_STOP);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_START:
+                                               if (sv->cur_admin & SRV_ADMF_FDRAIN) {
+                                                       srv_clr_admin_flag(sv, SRV_ADMF_FDRAIN);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_DHLTH:
+                                               if (sv->check.state & CHK_ST_CONFIGURED) {
+                                                       sv->check.state &= ~CHK_ST_ENABLED;
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_EHLTH:
+                                               if (sv->check.state & CHK_ST_CONFIGURED) {
+                                                       sv->check.state |= CHK_ST_ENABLED;
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_HRUNN:
+                                               if (!(sv->track)) {
+                                                       sv->check.health = sv->check.rise + sv->check.fall - 1;
+                                                       srv_set_running(sv, SRV_OP_STCHGC_STATS_WEB);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_HNOLB:
+                                               if (!(sv->track)) {
+                                                       sv->check.health = sv->check.rise + sv->check.fall - 1;
+                                                       srv_set_stopping(sv, SRV_OP_STCHGC_STATS_WEB);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_HDOWN:
+                                               if (!(sv->track)) {
+                                                       sv->check.health = 0;
+                                                       srv_set_stopped(sv, SRV_OP_STCHGC_STATS_WEB);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_DAGENT:
+                                               if (sv->agent.state & CHK_ST_CONFIGURED) {
+                                                       sv->agent.state &= ~CHK_ST_ENABLED;
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_EAGENT:
+                                               if (sv->agent.state & CHK_ST_CONFIGURED) {
+                                                       sv->agent.state |= CHK_ST_ENABLED;
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_ARUNN:
+                                               if (sv->agent.state & CHK_ST_ENABLED) {
+                                                       sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
+                                                       srv_set_running(sv, SRV_OP_STCHGC_STATS_WEB);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_ADOWN:
+                                               if (sv->agent.state & CHK_ST_ENABLED) {
+                                                       sv->agent.health = 0;
+                                                       srv_set_stopped(sv, SRV_OP_STCHGC_STATS_WEB);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       case ST_ADM_ACTION_READY:
+                                               srv_adm_set_ready(sv);
+                                               altered_servers++;
+                                               total_servers++;
+                                               break;
+                                       case ST_ADM_ACTION_DRAIN:
+                                               srv_adm_set_drain(sv);
+                                               altered_servers++;
+                                               total_servers++;
+                                               break;
+                                       case ST_ADM_ACTION_MAINT:
+                                               srv_adm_set_maint(sv);
+                                               altered_servers++;
+                                               total_servers++;
+                                               break;
+                                       case ST_ADM_ACTION_SHUTDOWN:
+                                               if (!(px->flags & (PR_FL_DISABLED|PR_FL_STOPPED))) {
+                                                       srv_shutdown_streams(sv, SF_ERR_KILLED);
+                                                       altered_servers++;
+                                                       total_servers++;
+                                               }
+                                               break;
+                                       }
+                                       HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
+                               } else {
+                                       /* the server name is unknown or ambiguous (duplicate names) */
+                                       total_servers++;
+                               }
+                       }
+                       if (reprocess && px && action) {
+                               /* Now, we know the backend and the action chosen by the user.
+                                * We can safely restart from the first server parameter
+                                * to reprocess them
+                                */
+                               cur_param  = st_cur_param;
+                               next_param = st_next_param;
+                               reprocess = 0;
+                               goto reprocess_servers;
+                       }
+
+                       next_param = cur_param;
+               }
+       }
+
+       if (total_servers == 0) {
+               ctx->st_code = STAT_STATUS_NONE;
+       }
+       else if (altered_servers == 0) {
+               ctx->st_code = STAT_STATUS_ERRP;
+       }
+       else if (altered_servers == total_servers) {
+               ctx->st_code = STAT_STATUS_DONE;
+       }
+       else {
+               ctx->st_code = STAT_STATUS_PART;
+       }
+ out:
+       return 1;
+ wait:
+       ctx->st_code = STAT_STATUS_NONE;
+       return 0;
+}
+
+/* This I/O handler runs as an applet embedded in a stream connector. It is
+ * used to send HTTP stats over a TCP socket. The mechanism is very simple.
+ * appctx->st0 contains the operation in progress (dump, done). The handler
+ * automatically unregisters itself once transfer is complete.
+ */
+static void http_stats_io_handler(struct appctx *appctx)
+{
+       struct show_stat_ctx *ctx = appctx->svcctx;
+       struct stconn *sc = appctx_sc(appctx);
+       struct htx *res_htx = NULL;
+
+       /* only proxy stats are available via http */
+       ctx->domain = STATS_DOMAIN_PROXY;
+
+       if (applet_fl_test(appctx, APPCTX_FL_INBLK_ALLOC|APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
+               goto out;
+
+       if (applet_fl_test(appctx, APPCTX_FL_FASTFWD) && se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD))
+               goto out;
+
+       if (!appctx_get_buf(appctx, &appctx->outbuf)) {
+               applet_fl_set(appctx, APPCTX_FL_OUTBLK_ALLOC);
+               goto out;
+       }
+
+       res_htx = htx_from_buf(&appctx->outbuf);
+
+       if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
+               appctx->st0 = STAT_HTTP_END;
+               goto out;
+       }
+
+       /* all states are processed in sequence */
+       if (appctx->st0 == STAT_HTTP_HEAD) {
+               if (stats_send_http_headers(sc, res_htx)) {
+                       struct ist meth = htx_sl_req_meth(http_get_stline(htxbuf(&appctx->inbuf)));
+
+                       if (find_http_meth(istptr(meth), istlen(meth)) == HTTP_METH_HEAD)
+                               appctx->st0 = STAT_HTTP_DONE;
+                       else {
+                               if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD_APPLET))
+                                       se_fl_set(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
+                               appctx->st0 = STAT_HTTP_DUMP;
+                       }
+               }
+       }
+
+       if (appctx->st0 == STAT_HTTP_DUMP) {
+               ctx->chunk = b_make(trash.area, appctx->outbuf.size, 0, 0);
+               /* adjust buffer size to take htx overhead into account,
+                * make sure to perform this call on an empty buffer
+                */
+               ctx->chunk.size = buf_room_for_htx_data(&ctx->chunk);
+               if (stats_dump_stat_to_buffer(sc, NULL, res_htx))
+                       appctx->st0 = STAT_HTTP_DONE;
+       }
+
+       if (appctx->st0 == STAT_HTTP_POST) {
+               if (stats_process_http_post(sc))
+                       appctx->st0 = STAT_HTTP_LAST;
+       }
+
+       if (appctx->st0 == STAT_HTTP_LAST) {
+               if (stats_send_http_redirect(sc, res_htx))
+                       appctx->st0 = STAT_HTTP_DONE;
+       }
+
+       if (appctx->st0 == STAT_HTTP_DONE) {
+               /* no more data are expected. If the response buffer is empty,
+                * be sure to add something (EOT block in this case) to have
+                * something to send. It is important to be sure the EOM flags
+                * will be handled by the endpoint.
+                */
+               if (htx_is_empty(res_htx)) {
+                       if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
+                               applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
+                               goto out;
+                       }
+               }
+               res_htx->flags |= HTX_FL_EOM;
+               applet_set_eoi(appctx);
+               se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
+               applet_fl_clr(appctx, APPCTX_FL_FASTFWD);
+               appctx->st0 = STAT_HTTP_END;
+       }
+
+       if (appctx->st0 == STAT_HTTP_END) {
+               applet_set_eos(appctx);
+               applet_will_consume(appctx);
+       }
+
+ out:
+       /* we have left the request in the buffer for the case where we
+        * process a POST, and this automatically re-enables activity on
+        * read. It's better to indicate that we want to stop reading when
+        * we're sending, so that we know there's at most one direction
+        * deciding to wake the applet up. It saves it from looping when
+        * emitting large blocks into small TCP windows.
+        */
+       if (res_htx)
+               htx_to_buf(res_htx, &appctx->outbuf);
+
+       if (appctx->st0 == STAT_HTTP_END) {
+               /* eat the whole request */
+               b_reset(&appctx->inbuf);
+               applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
+               appctx->sedesc->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
+       }
+       else if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_FULL))
+               applet_wont_consume(appctx);
+}
+
+static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf,
+                                 size_t count, unsigned int flags)
+{
+       struct stconn *sc = appctx_sc(appctx);
+       size_t ret = 0;
+
+       ret = b_data(buf);
+       if (stats_dump_stat_to_buffer(sc, buf, NULL)) {
+               se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
+               applet_fl_clr(appctx, APPCTX_FL_FASTFWD);
+               appctx->st0 = STAT_HTTP_DONE;
+       }
+
+       ret = b_data(buf) - ret;
+       return ret;
+}
+
+static void http_stats_release(struct appctx *appctx)
+{
+       struct show_stat_ctx *ctx = appctx->svcctx;
+
+       if (ctx->px_st == STAT_PX_ST_SV)
+               srv_drop(ctx->obj2);
+}
+
+struct applet http_stats_applet = {
+       .obj_type = OBJ_TYPE_APPLET,
+       .name = "<STATS>", /* used for logging */
+       .fct = http_stats_io_handler,
+       .rcv_buf = appctx_htx_rcv_buf,
+       .snd_buf = appctx_htx_snd_buf,
+       .fastfwd = http_stats_fastfwd,
+       .release = http_stats_release,
+};
index 8ed5ec092130ac0e472c5d96bc021e1ce49f5fe6..fa035f016decd9adf834748337e9b5f7c724c5c6 100644 (file)
@@ -57,6 +57,7 @@
 #include <haproxy/server.h>
 #include <haproxy/session.h>
 #include <haproxy/stats.h>
+#include <haproxy/stats-html.h>
 #include <haproxy/stconn.h>
 #include <haproxy/stream.h>
 #include <haproxy/task.h>
@@ -356,7 +357,7 @@ full:
        return 1;
 }
 
-static const char *stats_scope_ptr(struct appctx *appctx)
+const char *stats_scope_ptr(struct appctx *appctx)
 {
        struct show_stat_ctx *ctx = appctx->svcctx;
        struct htx *htx = htxbuf(&appctx->inbuf);
@@ -435,21 +436,6 @@ int stats_emit_raw_data_field(struct buffer *out, const struct field *f)
        }
 }
 
-const char *field_to_html_str(const struct field *f)
-{
-       switch (field_format(f, 0)) {
-       case FF_S32: return U2H(f->u.s32);
-       case FF_S64: return U2H(f->u.s64);
-       case FF_U64: return U2H(f->u.u64);
-       case FF_U32: return U2H(f->u.u32);
-       case FF_FLT: return F2H(f->u.flt);
-       case FF_STR: return field_str(f, 0);
-       case FF_EMPTY:
-       default:
-               return "";
-       }
-}
-
 /* Emits a stats field prefixed with its type. No CSV encoding is prepared, the
  * output is supposed to be used on its own line. Returns non-zero on success, 0
  * if the buffer is full.
@@ -874,841 +860,6 @@ err:
        return 1;
 }
 
-/* Dump all fields from <stats> into <out> using the HTML format. A column is
- * reserved for the checkbox is STAT_ADMIN is set in <flags>. Some extra info
- * are provided if STAT_SHLGNDS is present in <flags>. The statistics from
- * extra modules are displayed at the end of the lines if STAT_SHMODULES is
- * present in <flags>.
- */
-static int stats_dump_fields_html(struct buffer *out,
-                                  const struct field *stats,
-                                  struct show_stat_ctx *ctx)
-{
-       struct buffer src;
-       struct stats_module *mod;
-       int flags = ctx->flags;
-       int i = 0, j = 0;
-
-       if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE) {
-               chunk_appendf(out,
-                             /* name, queue */
-                             "<tr class=\"frontend\">");
-
-               if (flags & STAT_ADMIN) {
-                       /* Column sub-heading for Enable or Disable server */
-                       chunk_appendf(out, "<td></td>");
-               }
-
-               chunk_appendf(out,
-                             "<td class=ac>"
-                             "<a name=\"%s/Frontend\"></a>"
-                             "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
-                             "<td colspan=3></td>"
-                             "",
-                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_PXNAME));
-
-               chunk_appendf(out,
-                             /* sessions rate : current */
-                             "<td><u>%s<div class=tips><table class=det>"
-                             "<tr><th>Current connection rate:</th><td>%s/s</td></tr>"
-                             "<tr><th>Current session rate:</th><td>%s/s</td></tr>"
-                             "",
-                             U2H(stats[ST_F_RATE].u.u32),
-                             U2H(stats[ST_F_CONN_RATE].u.u32),
-                             U2H(stats[ST_F_RATE].u.u32));
-
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
-                       chunk_appendf(out,
-                                     "<tr><th>Current request rate:</th><td>%s/s</td></tr>",
-                                     U2H(stats[ST_F_REQ_RATE].u.u32));
-
-               chunk_appendf(out,
-                             "</table></div></u></td>"
-                             /* sessions rate : max */
-                             "<td><u>%s<div class=tips><table class=det>"
-                             "<tr><th>Max connection rate:</th><td>%s/s</td></tr>"
-                             "<tr><th>Max session rate:</th><td>%s/s</td></tr>"
-                             "",
-                             U2H(stats[ST_F_RATE_MAX].u.u32),
-                             U2H(stats[ST_F_CONN_RATE_MAX].u.u32),
-                             U2H(stats[ST_F_RATE_MAX].u.u32));
-
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
-                       chunk_appendf(out,
-                                     "<tr><th>Max request rate:</th><td>%s/s</td></tr>",
-                                     U2H(stats[ST_F_REQ_RATE_MAX].u.u32));
-
-               chunk_appendf(out,
-                             "</table></div></u></td>"
-                             /* sessions rate : limit */
-                             "<td>%s</td>",
-                             LIM2A(stats[ST_F_RATE_LIM].u.u32, "-"));
-
-               chunk_appendf(out,
-                             /* sessions: current, max, limit, total */
-                             "<td>%s</td><td>%s</td><td>%s</td>"
-                             "<td><u>%s<div class=tips><table class=det>"
-                             "<tr><th>Cum. connections:</th><td>%s</td></tr>"
-                             "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
-                             "",
-                             U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
-                             U2H(stats[ST_F_STOT].u.u64),
-                             U2H(stats[ST_F_CONN_TOT].u.u64),
-                             U2H(stats[ST_F_STOT].u.u64));
-
-               /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
-                       chunk_appendf(out,
-                                     "<tr><th>- HTTP/1 sessions:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP/2 sessions:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP/3 sessions:</th><td>%s</td></tr>"
-                                     "<tr><th>- other sessions:</th><td>%s</td></tr>"
-                                     "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP/1 requests:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP/2 requests:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP/3 requests:</th><td>%s</td></tr>"
-                                     "<tr><th>- other requests:</th><td>%s</td></tr>"
-                                     "",
-                                     U2H(stats[ST_F_H1SESS].u.u64),
-                                     U2H(stats[ST_F_H2SESS].u.u64),
-                                     U2H(stats[ST_F_H3SESS].u.u64),
-                                     U2H(stats[ST_F_SESS_OTHER].u.u64),
-                                     U2H(stats[ST_F_REQ_TOT].u.u64),
-                                     U2H(stats[ST_F_H1REQ].u.u64),
-                                     U2H(stats[ST_F_H2REQ].u.u64),
-                                     U2H(stats[ST_F_H3REQ].u.u64),
-                                     U2H(stats[ST_F_REQ_OTHER].u.u64));
-
-                       chunk_appendf(out,
-                                     "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- other responses:</th><td>%s</td></tr>"
-                                     "",
-                                     U2H(stats[ST_F_HRSP_1XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_2XX].u.u64),
-                                     U2H(stats[ST_F_COMP_RSP].u.u64),
-                                     stats[ST_F_HRSP_2XX].u.u64 ?
-                                     (int)(100 * stats[ST_F_COMP_RSP].u.u64 / stats[ST_F_HRSP_2XX].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_3XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_4XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_5XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_OTHER].u.u64));
-
-                       chunk_appendf(out,
-                                     "<tr><th>Intercepted requests:</th><td>%s</td></tr>"
-                                     "<tr><th>Cache lookups:</th><td>%s</td></tr>"
-                                     "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
-                                     "<tr><th>Internal errors:</th><td>%s</td></tr>"
-                                     "",
-                                     U2H(stats[ST_F_INTERCEPTED].u.u64),
-                                     U2H(stats[ST_F_CACHE_LOOKUPS].u.u64),
-                                     U2H(stats[ST_F_CACHE_HITS].u.u64),
-                                     stats[ST_F_CACHE_LOOKUPS].u.u64 ?
-                                     (int)(100 * stats[ST_F_CACHE_HITS].u.u64 / stats[ST_F_CACHE_LOOKUPS].u.u64) : 0,
-                                     U2H(stats[ST_F_WREW].u.u64),
-                                     U2H(stats[ST_F_EINT].u.u64));
-               }
-
-               chunk_appendf(out,
-                             "</table></div></u></td>"
-                             /* sessions: lbtot, lastsess */
-                             "<td></td><td></td>"
-                             /* bytes : in */
-                             "<td>%s</td>"
-                             "",
-                             U2H(stats[ST_F_BIN].u.u64));
-
-               chunk_appendf(out,
-                             /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
-                             "<td>%s%s<div class=tips><table class=det>"
-                             "<tr><th>Response bytes in:</th><td>%s</td></tr>"
-                             "<tr><th>Compression in:</th><td>%s</td></tr>"
-                             "<tr><th>Compression out:</th><td>%s</td><td>(%d%%)</td></tr>"
-                             "<tr><th>Compression bypass:</th><td>%s</td></tr>"
-                             "<tr><th>Total bytes saved:</th><td>%s</td><td>(%d%%)</td></tr>"
-                             "</table></div>%s</td>",
-                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "<u>":"",
-                             U2H(stats[ST_F_BOUT].u.u64),
-                             U2H(stats[ST_F_BOUT].u.u64),
-                             U2H(stats[ST_F_COMP_IN].u.u64),
-                             U2H(stats[ST_F_COMP_OUT].u.u64),
-                             stats[ST_F_COMP_IN].u.u64 ? (int)(stats[ST_F_COMP_OUT].u.u64 * 100 / stats[ST_F_COMP_IN].u.u64) : 0,
-                             U2H(stats[ST_F_COMP_BYP].u.u64),
-                             U2H(stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64),
-                             stats[ST_F_BOUT].u.u64 ? (int)((stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64) * 100 / stats[ST_F_BOUT].u.u64) : 0,
-                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "</u>":"");
-
-               chunk_appendf(out,
-                             /* denied: req, resp */
-                             "<td>%s</td><td>%s</td>"
-                             /* errors : request, connect, response */
-                             "<td>%s</td><td></td><td></td>"
-                             /* warnings: retries, redispatches */
-                             "<td></td><td></td>"
-                             /* server status : reflect frontend status */
-                             "<td class=ac>%s</td>"
-                             /* rest of server: nothing */
-                             "<td class=ac colspan=8></td>"
-                             "",
-                             U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
-                             U2H(stats[ST_F_EREQ].u.u64),
-                             field_str(stats, ST_F_STATUS));
-
-               if (flags & STAT_SHMODULES) {
-                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-                               chunk_appendf(out, "<td>");
-
-                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE) {
-                                       chunk_appendf(out,
-                                                     "<u>%s<div class=tips><table class=det>",
-                                                     mod->name);
-                                       for (j = 0; j < mod->stats_count; ++j) {
-                                               chunk_appendf(out,
-                                                             "<tr><th>%s</th><td>%s</td></tr>",
-                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-                                               ++i;
-                                       }
-                                       chunk_appendf(out, "</table></div></u>");
-                               } else {
-                                       i += mod->stats_count;
-                               }
-
-                               chunk_appendf(out, "</td>");
-                       }
-               }
-
-               chunk_appendf(out, "</tr>");
-       }
-       else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO) {
-               chunk_appendf(out, "<tr class=socket>");
-               if (flags & STAT_ADMIN) {
-                       /* Column sub-heading for Enable or Disable server */
-                       chunk_appendf(out, "<td></td>");
-               }
-
-               chunk_appendf(out,
-                             /* frontend name, listener name */
-                             "<td class=ac><a name=\"%s/+%s\"></a>%s"
-                             "<a class=lfsb href=\"#%s/+%s\">%s</a>"
-                             "",
-                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME),
-                             (flags & STAT_SHLGNDS)?"<u>":"",
-                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME), field_str(stats, ST_F_SVNAME));
-
-               if (flags & STAT_SHLGNDS) {
-                       chunk_appendf(out, "<div class=tips>");
-
-                       if (isdigit((unsigned char)*field_str(stats, ST_F_ADDR)))
-                               chunk_appendf(out, "IPv4: %s, ", field_str(stats, ST_F_ADDR));
-                       else if (*field_str(stats, ST_F_ADDR) == '[')
-                               chunk_appendf(out, "IPv6: %s, ", field_str(stats, ST_F_ADDR));
-                       else if (*field_str(stats, ST_F_ADDR))
-                               chunk_appendf(out, "%s, ", field_str(stats, ST_F_ADDR));
-
-                       chunk_appendf(out, "proto=%s, ", field_str(stats, ST_F_PROTO));
-
-                       /* id */
-                       chunk_appendf(out, "id: %d</div>", stats[ST_F_SID].u.u32);
-               }
-
-               chunk_appendf(out,
-                             /* queue */
-                             "%s</td><td colspan=3></td>"
-                             /* sessions rate: current, max, limit */
-                             "<td colspan=3>&nbsp;</td>"
-                             /* sessions: current, max, limit, total, lbtot, lastsess */
-                             "<td>%s</td><td>%s</td><td>%s</td>"
-                             "<td>%s</td><td>&nbsp;</td><td>&nbsp;</td>"
-                             /* bytes: in, out */
-                             "<td>%s</td><td>%s</td>"
-                             "",
-                             (flags & STAT_SHLGNDS)?"</u>":"",
-                             U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
-                             U2H(stats[ST_F_STOT].u.u64), U2H(stats[ST_F_BIN].u.u64), U2H(stats[ST_F_BOUT].u.u64));
-
-               chunk_appendf(out,
-                             /* denied: req, resp */
-                             "<td>%s</td><td>%s</td>"
-                             /* errors: request, connect, response */
-                             "<td>%s</td><td></td><td></td>"
-                             /* warnings: retries, redispatches */
-                             "<td></td><td></td>"
-                             /* server status: reflect listener status */
-                             "<td class=ac>%s</td>"
-                             /* rest of server: nothing */
-                             "<td class=ac colspan=8></td>"
-                             "",
-                             U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
-                             U2H(stats[ST_F_EREQ].u.u64),
-                             field_str(stats, ST_F_STATUS));
-
-               if (flags & STAT_SHMODULES) {
-                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-                               chunk_appendf(out, "<td>");
-
-                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI) {
-                                       chunk_appendf(out,
-                                                     "<u>%s<div class=tips><table class=det>",
-                                                     mod->name);
-                                       for (j = 0; j < mod->stats_count; ++j) {
-                                               chunk_appendf(out,
-                                                             "<tr><th>%s</th><td>%s</td></tr>",
-                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-                                               ++i;
-                                       }
-                                       chunk_appendf(out, "</table></div></u>");
-                               } else {
-                                       i += mod->stats_count;
-                               }
-
-                               chunk_appendf(out, "</td>");
-                       }
-               }
-
-               chunk_appendf(out, "</tr>");
-       }
-       else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV) {
-               const char *style;
-
-               /* determine the style to use depending on the server's state,
-                * its health and weight. There isn't a 1-to-1 mapping between
-                * state and styles for the cases where the server is (still)
-                * up. The reason is that we don't want to report nolb and
-                * drain with the same color.
-                */
-
-               if (strcmp(field_str(stats, ST_F_STATUS), "DOWN") == 0 ||
-                   strcmp(field_str(stats, ST_F_STATUS), "DOWN (agent)") == 0) {
-                       style = "down";
-               }
-               else if (strncmp(field_str(stats, ST_F_STATUS), "DOWN ", strlen("DOWN ")) == 0) {
-                       style = "going_up";
-               }
-               else if (strcmp(field_str(stats, ST_F_STATUS), "DRAIN") == 0) {
-                       style = "draining";
-               }
-               else if (strncmp(field_str(stats, ST_F_STATUS), "NOLB ", strlen("NOLB ")) == 0) {
-                       style = "going_down";
-               }
-               else if (strcmp(field_str(stats, ST_F_STATUS), "NOLB") == 0) {
-                       style = "nolb";
-               }
-               else if (strcmp(field_str(stats, ST_F_STATUS), "no check") == 0) {
-                       style = "no_check";
-               }
-               else if (!stats[ST_F_CHKFAIL].type ||
-                        stats[ST_F_CHECK_HEALTH].u.u32 == stats[ST_F_CHECK_RISE].u.u32 + stats[ST_F_CHECK_FALL].u.u32 - 1) {
-                       /* no check or max health = UP */
-                       if (stats[ST_F_WEIGHT].u.u32)
-                               style = "up";
-                       else
-                               style = "draining";
-               }
-               else {
-                       style = "going_down";
-               }
-
-               if (strncmp(field_str(stats, ST_F_STATUS), "MAINT", 5) == 0)
-                       chunk_appendf(out, "<tr class=\"maintain\">");
-               else
-                       chunk_appendf(out,
-                                     "<tr class=\"%s_%s\">",
-                                     (stats[ST_F_BCK].u.u32) ? "backup" : "active", style);
-
-
-               if (flags & STAT_ADMIN)
-                       chunk_appendf(out,
-                                     "<td><input class='%s-checkbox' type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
-                                     field_str(stats, ST_F_PXNAME),
-                                     field_str(stats, ST_F_SVNAME));
-
-               chunk_appendf(out,
-                             "<td class=ac><a name=\"%s/%s\"></a>%s"
-                             "<a class=lfsb href=\"#%s/%s\">%s</a>"
-                             "",
-                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME),
-                             (flags & STAT_SHLGNDS) ? "<u>" : "",
-                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_SVNAME), field_str(stats, ST_F_SVNAME));
-
-               if (flags & STAT_SHLGNDS) {
-                       chunk_appendf(out, "<div class=tips>");
-
-                       if (isdigit((unsigned char)*field_str(stats, ST_F_ADDR)))
-                               chunk_appendf(out, "IPv4: %s, ", field_str(stats, ST_F_ADDR));
-                       else if (*field_str(stats, ST_F_ADDR) == '[')
-                               chunk_appendf(out, "IPv6: %s, ", field_str(stats, ST_F_ADDR));
-                       else if (*field_str(stats, ST_F_ADDR))
-                               chunk_appendf(out, "%s, ", field_str(stats, ST_F_ADDR));
-
-                       /* id */
-                       chunk_appendf(out, "id: %d, rid: %d", stats[ST_F_SID].u.u32, stats[ST_F_SRID].u.u32);
-
-                       /* cookie */
-                       if (stats[ST_F_COOKIE].type) {
-                               chunk_appendf(out, ", cookie: '");
-                               chunk_initstr(&src, field_str(stats, ST_F_COOKIE));
-                               chunk_htmlencode(out, &src);
-                               chunk_appendf(out, "'");
-                       }
-
-                       chunk_appendf(out, "</div>");
-               }
-
-               chunk_appendf(out,
-                             /* queue : current, max, limit */
-                             "%s</td><td>%s</td><td>%s</td><td>%s</td>"
-                             /* sessions rate : current, max, limit */
-                             "<td>%s</td><td>%s</td><td></td>"
-                             "",
-                             (flags & STAT_SHLGNDS) ? "</u>" : "",
-                             U2H(stats[ST_F_QCUR].u.u32), U2H(stats[ST_F_QMAX].u.u32), LIM2A(stats[ST_F_QLIMIT].u.u32, "-"),
-                             U2H(stats[ST_F_RATE].u.u32), U2H(stats[ST_F_RATE_MAX].u.u32));
-
-               chunk_appendf(out,
-                             /* sessions: current, max, limit, total */
-                             "<td><u>%s<div class=tips>"
-                               "<table class=det>"
-                               "<tr><th>Current active connections:</th><td>%s</td></tr>"
-                               "<tr><th>Current used connections:</th><td>%s</td></tr>"
-                               "<tr><th>Current idle connections:</th><td>%s</td></tr>"
-                               "<tr><th>- unsafe:</th><td>%s</td></tr>"
-                               "<tr><th>- safe:</th><td>%s</td></tr>"
-                               "<tr><th>Estimated need of connections:</th><td>%s</td></tr>"
-                               "<tr><th>Active connections limit:</th><td>%s</td></tr>"
-                               "<tr><th>Idle connections limit:</th><td>%s</td></tr>"
-                               "</table></div></u>"
-                             "</td><td>%s</td><td>%s</td>"
-                             "<td><u>%s<div class=tips><table class=det>"
-                             "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
-                             "",
-                             U2H(stats[ST_F_SCUR].u.u32),
-                             U2H(stats[ST_F_SCUR].u.u32),
-                             U2H(stats[ST_F_USED_CONN_CUR].u.u32),
-                             U2H(stats[ST_F_SRV_ICUR].u.u32),
-                             U2H(stats[ST_F_IDLE_CONN_CUR].u.u32),
-                             U2H(stats[ST_F_SAFE_CONN_CUR].u.u32),
-                             U2H(stats[ST_F_NEED_CONN_EST].u.u32),
-
-                               LIM2A(stats[ST_F_SLIM].u.u32, "-"),
-                               stats[ST_F_SRV_ILIM].type ? U2H(stats[ST_F_SRV_ILIM].u.u32) : "-",
-                             U2H(stats[ST_F_SMAX].u.u32), LIM2A(stats[ST_F_SLIM].u.u32, "-"),
-                             U2H(stats[ST_F_STOT].u.u64),
-                             U2H(stats[ST_F_STOT].u.u64));
-
-               /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
-                       chunk_appendf(out,
-                                     "<tr><th>New connections:</th><td>%s</td></tr>"
-                                     "<tr><th>Reused connections:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 1xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- HTTP 2xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- HTTP 3xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- HTTP 4xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- HTTP 5xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- other responses:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
-                                     "<tr><th>Internal error:</th><td>%s</td></tr>"
-                                     "",
-                                     U2H(stats[ST_F_CONNECT].u.u64),
-                                     U2H(stats[ST_F_REUSE].u.u64),
-                                     (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64) ?
-                                     (int)(100 * stats[ST_F_REUSE].u.u64 / (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64)) : 0,
-                                     U2H(stats[ST_F_REQ_TOT].u.u64),
-                                     U2H(stats[ST_F_HRSP_1XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
-                                     (int)(100 * stats[ST_F_HRSP_1XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_2XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
-                                     (int)(100 * stats[ST_F_HRSP_2XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_3XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
-                                     (int)(100 * stats[ST_F_HRSP_3XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_4XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
-                                     (int)(100 * stats[ST_F_HRSP_4XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_5XX].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
-                                     (int)(100 * stats[ST_F_HRSP_5XX].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_OTHER].u.u64), stats[ST_F_REQ_TOT].u.u64 ?
-                                     (int)(100 * stats[ST_F_HRSP_OTHER].u.u64 / stats[ST_F_REQ_TOT].u.u64) : 0,
-                                     U2H(stats[ST_F_WREW].u.u64),
-                                     U2H(stats[ST_F_EINT].u.u64));
-               }
-
-               chunk_appendf(out, "<tr><th colspan=3>Max / Avg over last 1024 success. conn.</th></tr>");
-               chunk_appendf(out, "<tr><th>- Queue time:</th><td>%s / %s</td><td>ms</td></tr>",
-                             U2H(stats[ST_F_QT_MAX].u.u32), U2H(stats[ST_F_QTIME].u.u32));
-               chunk_appendf(out, "<tr><th>- Connect time:</th><td>%s / %s</td><td>ms</td></tr>",
-                             U2H(stats[ST_F_CT_MAX].u.u32), U2H(stats[ST_F_CTIME].u.u32));
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
-                       chunk_appendf(out, "<tr><th>- Responses time:</th><td>%s / %s</td><td>ms</td></tr>",
-                                     U2H(stats[ST_F_RT_MAX].u.u32), U2H(stats[ST_F_RTIME].u.u32));
-               chunk_appendf(out, "<tr><th>- Total time:</th><td>%s / %s</td><td>ms</td></tr>",
-                             U2H(stats[ST_F_TT_MAX].u.u32), U2H(stats[ST_F_TTIME].u.u32));
-
-               chunk_appendf(out,
-                             "</table></div></u></td>"
-                             /* sessions: lbtot, last */
-                             "<td>%s</td><td>%s</td>",
-                             U2H(stats[ST_F_LBTOT].u.u64),
-                             human_time(stats[ST_F_LASTSESS].u.s32, 1));
-
-               chunk_appendf(out,
-                             /* bytes : in, out */
-                             "<td>%s</td><td>%s</td>"
-                             /* denied: req, resp */
-                             "<td></td><td>%s</td>"
-                             /* errors : request, connect */
-                             "<td></td><td>%s</td>"
-                             /* errors : response */
-                             "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
-                             /* warnings: retries, redispatches */
-                             "<td>%lld</td><td>%lld</td>"
-                             "",
-                             U2H(stats[ST_F_BIN].u.u64), U2H(stats[ST_F_BOUT].u.u64),
-                             U2H(stats[ST_F_DRESP].u.u64),
-                             U2H(stats[ST_F_ECON].u.u64),
-                             U2H(stats[ST_F_ERESP].u.u64),
-                             (long long)stats[ST_F_CLI_ABRT].u.u64,
-                             (long long)stats[ST_F_SRV_ABRT].u.u64,
-                             (long long)stats[ST_F_WRETR].u.u64,
-                             (long long)stats[ST_F_WREDIS].u.u64);
-
-               /* status, last change */
-               chunk_appendf(out, "<td class=ac>");
-
-               /* FIXME!!!!
-                *   LASTCHG should contain the last change for *this* server and must be computed
-                * properly above, as was done below, ie: this server if maint, otherwise ref server
-                * if tracking. Note that ref is either local or remote depending on tracking.
-                */
-
-
-               if (strncmp(field_str(stats, ST_F_STATUS), "MAINT", 5) == 0) {
-                       chunk_appendf(out, "%s MAINT", human_time(stats[ST_F_LASTCHG].u.u32, 1));
-               }
-               else if (strcmp(field_str(stats, ST_F_STATUS), "no check") == 0) {
-                       chunk_strcat(out, "<i>no check</i>");
-               }
-               else {
-                       chunk_appendf(out, "%s %s", human_time(stats[ST_F_LASTCHG].u.u32, 1), field_str(stats, ST_F_STATUS));
-                       if (strncmp(field_str(stats, ST_F_STATUS), "DOWN", 4) == 0) {
-                               if (stats[ST_F_CHECK_HEALTH].u.u32)
-                                       chunk_strcat(out, " &uarr;");
-                       }
-                       else if (stats[ST_F_CHECK_HEALTH].u.u32 < stats[ST_F_CHECK_RISE].u.u32 + stats[ST_F_CHECK_FALL].u.u32 - 1)
-                               chunk_strcat(out, " &darr;");
-               }
-
-               if (strncmp(field_str(stats, ST_F_STATUS), "DOWN", 4) == 0 &&
-                   stats[ST_F_AGENT_STATUS].type && !stats[ST_F_AGENT_HEALTH].u.u32) {
-                       chunk_appendf(out,
-                                     "</td><td class=ac><u> %s",
-                                     field_str(stats, ST_F_AGENT_STATUS));
-
-                       if (stats[ST_F_AGENT_CODE].type)
-                               chunk_appendf(out, "/%d", stats[ST_F_AGENT_CODE].u.u32);
-
-                       if (stats[ST_F_AGENT_DURATION].type)
-                               chunk_appendf(out, " in %lums", (long)stats[ST_F_AGENT_DURATION].u.u64);
-
-                       chunk_appendf(out, "<div class=tips>%s", field_str(stats, ST_F_AGENT_DESC));
-
-                       if (*field_str(stats, ST_F_LAST_AGT)) {
-                               chunk_appendf(out, ": ");
-                               chunk_initstr(&src, field_str(stats, ST_F_LAST_AGT));
-                               chunk_htmlencode(out, &src);
-                       }
-                       chunk_appendf(out, "</div></u>");
-               }
-               else if (stats[ST_F_CHECK_STATUS].type) {
-                       chunk_appendf(out,
-                                     "</td><td class=ac><u> %s",
-                                     field_str(stats, ST_F_CHECK_STATUS));
-
-                       if (stats[ST_F_CHECK_CODE].type)
-                               chunk_appendf(out, "/%d", stats[ST_F_CHECK_CODE].u.u32);
-
-                       if (stats[ST_F_CHECK_DURATION].type)
-                               chunk_appendf(out, " in %lums", (long)stats[ST_F_CHECK_DURATION].u.u64);
-
-                       chunk_appendf(out, "<div class=tips>%s", field_str(stats, ST_F_CHECK_DESC));
-
-                       if (*field_str(stats, ST_F_LAST_CHK)) {
-                               chunk_appendf(out, ": ");
-                               chunk_initstr(&src, field_str(stats, ST_F_LAST_CHK));
-                               chunk_htmlencode(out, &src);
-                       }
-                       chunk_appendf(out, "</div></u>");
-               }
-               else
-                       chunk_appendf(out, "</td><td>");
-
-               chunk_appendf(out,
-                             /* weight / uweight */
-                             "</td><td class=ac>%d/%d</td>"
-                             /* act, bck */
-                             "<td class=ac>%s</td><td class=ac>%s</td>"
-                             "",
-                             stats[ST_F_WEIGHT].u.u32, stats[ST_F_UWEIGHT].u.u32,
-                             stats[ST_F_BCK].u.u32 ? "-" : "Y",
-                             stats[ST_F_BCK].u.u32 ? "Y" : "-");
-
-               /* check failures: unique, fatal, down time */
-               if (strcmp(field_str(stats, ST_F_STATUS), "MAINT (resolution)") == 0) {
-                       chunk_appendf(out, "<td class=ac colspan=3>resolution</td>");
-               }
-               else if (stats[ST_F_CHKFAIL].type) {
-                       chunk_appendf(out, "<td><u>%lld", (long long)stats[ST_F_CHKFAIL].u.u64);
-
-                       if (stats[ST_F_HANAFAIL].type)
-                               chunk_appendf(out, "/%lld", (long long)stats[ST_F_HANAFAIL].u.u64);
-
-                       chunk_appendf(out,
-                                     "<div class=tips>Failed Health Checks%s</div></u></td>"
-                                     "<td>%lld</td><td>%s</td>"
-                                     "",
-                                     stats[ST_F_HANAFAIL].type ? "/Health Analyses" : "",
-                                     (long long)stats[ST_F_CHKDOWN].u.u64, human_time(stats[ST_F_DOWNTIME].u.u32, 1));
-               }
-               else if (strcmp(field_str(stats, ST_F_STATUS), "MAINT") != 0 && field_format(stats, ST_F_TRACKED) == FF_STR) {
-                       /* tracking a server (hence inherited maint would appear as "MAINT (via...)" */
-                       chunk_appendf(out,
-                                     "<td class=ac colspan=3><a class=lfsb href=\"#%s\">via %s</a></td>",
-                                     field_str(stats, ST_F_TRACKED), field_str(stats, ST_F_TRACKED));
-               }
-               else
-                       chunk_appendf(out, "<td colspan=3></td>");
-
-               /* throttle */
-               if (stats[ST_F_THROTTLE].type)
-                       chunk_appendf(out, "<td class=ac>%d %%</td>\n", stats[ST_F_THROTTLE].u.u32);
-               else
-                       chunk_appendf(out, "<td class=ac>-</td>");
-
-               if (flags & STAT_SHMODULES) {
-                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-                               chunk_appendf(out, "<td>");
-
-                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV) {
-                                       chunk_appendf(out,
-                                                     "<u>%s<div class=tips><table class=det>",
-                                                     mod->name);
-                                       for (j = 0; j < mod->stats_count; ++j) {
-                                               chunk_appendf(out,
-                                                             "<tr><th>%s</th><td>%s</td></tr>",
-                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-                                               ++i;
-                                       }
-                                       chunk_appendf(out, "</table></div></u>");
-                               } else {
-                                       i += mod->stats_count;
-                               }
-
-                               chunk_appendf(out, "</td>");
-                       }
-               }
-
-               chunk_appendf(out, "</tr>\n");
-       }
-       else if (stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE) {
-               chunk_appendf(out, "<tr class=\"backend\">");
-               if (flags & STAT_ADMIN) {
-                       /* Column sub-heading for Enable or Disable server */
-                       chunk_appendf(out, "<td></td>");
-               }
-               chunk_appendf(out,
-                             "<td class=ac>"
-                             /* name */
-                             "%s<a name=\"%s/Backend\"></a>"
-                             "<a class=lfsb href=\"#%s/Backend\">Backend</a>"
-                             "",
-                             (flags & STAT_SHLGNDS)?"<u>":"",
-                             field_str(stats, ST_F_PXNAME), field_str(stats, ST_F_PXNAME));
-
-               if (flags & STAT_SHLGNDS) {
-                       /* balancing */
-                       chunk_appendf(out, "<div class=tips>balancing: %s",
-                                     field_str(stats, ST_F_ALGO));
-
-                       /* cookie */
-                       if (stats[ST_F_COOKIE].type) {
-                               chunk_appendf(out, ", cookie: '");
-                               chunk_initstr(&src, field_str(stats, ST_F_COOKIE));
-                               chunk_htmlencode(out, &src);
-                               chunk_appendf(out, "'");
-                       }
-                       chunk_appendf(out, "</div>");
-               }
-
-               chunk_appendf(out,
-                             "%s</td>"
-                             /* queue : current, max */
-                             "<td>%s</td><td>%s</td><td></td>"
-                             /* sessions rate : current, max, limit */
-                             "<td>%s</td><td>%s</td><td></td>"
-                             "",
-                             (flags & STAT_SHLGNDS)?"</u>":"",
-                             U2H(stats[ST_F_QCUR].u.u32), U2H(stats[ST_F_QMAX].u.u32),
-                             U2H(stats[ST_F_RATE].u.u32), U2H(stats[ST_F_RATE_MAX].u.u32));
-
-               chunk_appendf(out,
-                             /* sessions: current, max, limit, total */
-                             "<td>%s</td><td>%s</td><td>%s</td>"
-                             "<td><u>%s<div class=tips><table class=det>"
-                             "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
-                             "",
-                             U2H(stats[ST_F_SCUR].u.u32), U2H(stats[ST_F_SMAX].u.u32), U2H(stats[ST_F_SLIM].u.u32),
-                             U2H(stats[ST_F_STOT].u.u64),
-                             U2H(stats[ST_F_STOT].u.u64));
-
-               /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0) {
-                       chunk_appendf(out,
-                                     "<tr><th>New connections:</th><td>%s</td></tr>"
-                                     "<tr><th>Reused connections:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
-                                     "<tr><th>- other responses:</th><td>%s</td></tr>"
-                                     "<tr><th>Cache lookups:</th><td>%s</td></tr>"
-                                     "<tr><th>Cache hits:</th><td>%s</td><td>(%d%%)</td></tr>"
-                                     "<tr><th>Failed hdr rewrites:</th><td>%s</td></tr>"
-                                     "<tr><th>Internal errors:</th><td>%s</td></tr>"
-                                     "",
-                                     U2H(stats[ST_F_CONNECT].u.u64),
-                                     U2H(stats[ST_F_REUSE].u.u64),
-                                     (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64) ?
-                                     (int)(100 * stats[ST_F_REUSE].u.u64 / (stats[ST_F_CONNECT].u.u64 + stats[ST_F_REUSE].u.u64)) : 0,
-                                     U2H(stats[ST_F_REQ_TOT].u.u64),
-                                     U2H(stats[ST_F_HRSP_1XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_2XX].u.u64),
-                                     U2H(stats[ST_F_COMP_RSP].u.u64),
-                                     stats[ST_F_HRSP_2XX].u.u64 ?
-                                     (int)(100 * stats[ST_F_COMP_RSP].u.u64 / stats[ST_F_HRSP_2XX].u.u64) : 0,
-                                     U2H(stats[ST_F_HRSP_3XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_4XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_5XX].u.u64),
-                                     U2H(stats[ST_F_HRSP_OTHER].u.u64),
-                                     U2H(stats[ST_F_CACHE_LOOKUPS].u.u64),
-                                     U2H(stats[ST_F_CACHE_HITS].u.u64),
-                                     stats[ST_F_CACHE_LOOKUPS].u.u64 ?
-                                     (int)(100 * stats[ST_F_CACHE_HITS].u.u64 / stats[ST_F_CACHE_LOOKUPS].u.u64) : 0,
-                                     U2H(stats[ST_F_WREW].u.u64),
-                                     U2H(stats[ST_F_EINT].u.u64));
-               }
-
-               chunk_appendf(out, "<tr><th colspan=3>Max / Avg over last 1024 success. conn.</th></tr>");
-               chunk_appendf(out, "<tr><th>- Queue time:</th><td>%s / %s</td><td>ms</td></tr>",
-                             U2H(stats[ST_F_QT_MAX].u.u32), U2H(stats[ST_F_QTIME].u.u32));
-               chunk_appendf(out, "<tr><th>- Connect time:</th><td>%s / %s</td><td>ms</td></tr>",
-                             U2H(stats[ST_F_CT_MAX].u.u32), U2H(stats[ST_F_CTIME].u.u32));
-               if (strcmp(field_str(stats, ST_F_MODE), "http") == 0)
-                       chunk_appendf(out, "<tr><th>- Responses time:</th><td>%s / %s</td><td>ms</td></tr>",
-                                     U2H(stats[ST_F_RT_MAX].u.u32), U2H(stats[ST_F_RTIME].u.u32));
-               chunk_appendf(out, "<tr><th>- Total time:</th><td>%s / %s</td><td>ms</td></tr>",
-                             U2H(stats[ST_F_TT_MAX].u.u32), U2H(stats[ST_F_TTIME].u.u32));
-
-               chunk_appendf(out,
-                             "</table></div></u></td>"
-                             /* sessions: lbtot, last */
-                             "<td>%s</td><td>%s</td>"
-                             /* bytes: in */
-                             "<td>%s</td>"
-                             "",
-                             U2H(stats[ST_F_LBTOT].u.u64),
-                             human_time(stats[ST_F_LASTSESS].u.s32, 1),
-                             U2H(stats[ST_F_BIN].u.u64));
-
-               chunk_appendf(out,
-                             /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
-                             "<td>%s%s<div class=tips><table class=det>"
-                             "<tr><th>Response bytes in:</th><td>%s</td></tr>"
-                             "<tr><th>Compression in:</th><td>%s</td></tr>"
-                             "<tr><th>Compression out:</th><td>%s</td><td>(%d%%)</td></tr>"
-                             "<tr><th>Compression bypass:</th><td>%s</td></tr>"
-                             "<tr><th>Total bytes saved:</th><td>%s</td><td>(%d%%)</td></tr>"
-                             "</table></div>%s</td>",
-                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "<u>":"",
-                             U2H(stats[ST_F_BOUT].u.u64),
-                             U2H(stats[ST_F_BOUT].u.u64),
-                             U2H(stats[ST_F_COMP_IN].u.u64),
-                             U2H(stats[ST_F_COMP_OUT].u.u64),
-                             stats[ST_F_COMP_IN].u.u64 ? (int)(stats[ST_F_COMP_OUT].u.u64 * 100 / stats[ST_F_COMP_IN].u.u64) : 0,
-                             U2H(stats[ST_F_COMP_BYP].u.u64),
-                             U2H(stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64),
-                             stats[ST_F_BOUT].u.u64 ? (int)((stats[ST_F_COMP_IN].u.u64 - stats[ST_F_COMP_OUT].u.u64) * 100 / stats[ST_F_BOUT].u.u64) : 0,
-                             (stats[ST_F_COMP_IN].u.u64 || stats[ST_F_COMP_BYP].u.u64) ? "</u>":"");
-
-               chunk_appendf(out,
-                             /* denied: req, resp */
-                             "<td>%s</td><td>%s</td>"
-                             /* errors : request, connect */
-                             "<td></td><td>%s</td>"
-                             /* errors : response */
-                             "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
-                             /* warnings: retries, redispatches */
-                             "<td>%lld</td><td>%lld</td>"
-                             /* backend status: reflect backend status (up/down): we display UP
-                              * if the backend has known working servers or if it has no server at
-                              * all (eg: for stats). Then we display the total weight, number of
-                              * active and backups. */
-                             "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d/%d</td>"
-                             "<td class=ac>%d</td><td class=ac>%d</td>"
-                             "",
-                             U2H(stats[ST_F_DREQ].u.u64), U2H(stats[ST_F_DRESP].u.u64),
-                             U2H(stats[ST_F_ECON].u.u64),
-                             U2H(stats[ST_F_ERESP].u.u64),
-                             (long long)stats[ST_F_CLI_ABRT].u.u64,
-                             (long long)stats[ST_F_SRV_ABRT].u.u64,
-                             (long long)stats[ST_F_WRETR].u.u64, (long long)stats[ST_F_WREDIS].u.u64,
-                             human_time(stats[ST_F_LASTCHG].u.u32, 1),
-                             strcmp(field_str(stats, ST_F_STATUS), "DOWN") ? field_str(stats, ST_F_STATUS) : "<font color=\"red\"><b>DOWN</b></font>",
-                             stats[ST_F_WEIGHT].u.u32, stats[ST_F_UWEIGHT].u.u32,
-                             stats[ST_F_ACT].u.u32, stats[ST_F_BCK].u.u32);
-
-               chunk_appendf(out,
-                             /* rest of backend: nothing, down transitions, total downtime, throttle */
-                             "<td class=ac>&nbsp;</td><td>%d</td>"
-                             "<td>%s</td>"
-                             "<td></td>",
-                             stats[ST_F_CHKDOWN].u.u32,
-                             stats[ST_F_DOWNTIME].type ? human_time(stats[ST_F_DOWNTIME].u.u32, 1) : "&nbsp;");
-
-               if (flags & STAT_SHMODULES) {
-                       list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-                               chunk_appendf(out, "<td>");
-
-                               if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE) {
-                                       chunk_appendf(out,
-                                                     "<u>%s<div class=tips><table class=det>",
-                                                     mod->name);
-                                       for (j = 0; j < mod->stats_count; ++j) {
-                                               chunk_appendf(out,
-                                                             "<tr><th>%s</th><td>%s</td></tr>",
-                                                             mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-                                               ++i;
-                                       }
-                                       chunk_appendf(out, "</table></div></u>");
-                               } else {
-                                       i += mod->stats_count;
-                               }
-
-                               chunk_appendf(out, "</td>");
-                       }
-               }
-
-               chunk_appendf(out, "</tr>");
-       }
-
-       return 1;
-}
-
 int stats_dump_one_line(const struct field *stats, size_t stats_count,
                         struct appctx *appctx)
 {
@@ -3015,159 +2166,6 @@ static int stats_dump_be_stats(struct stconn *sc, struct proxy *px)
        return stats_dump_one_line(stats, stats_count, appctx);
 }
 
-/* Dumps the HTML table header for proxy <px> to chunk ctx buffer and uses the
- * state from stream connector <sc>. The caller is responsible for clearing
- * chunk ctx buffer if needed.
- */
-static void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px)
-{
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct buffer *chk = &ctx->chunk;
-       char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
-       struct stats_module *mod;
-       int stats_module_len = 0;
-
-       if (px->cap & PR_CAP_BE && px->srv && (ctx->flags & STAT_ADMIN)) {
-               /* A form to enable/disable this proxy servers */
-
-               /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
-               scope_txt[0] = 0;
-               if (ctx->scope_len) {
-                       const char *scope_ptr = stats_scope_ptr(appctx);
-
-                       strlcpy2(scope_txt, STAT_SCOPE_PATTERN, sizeof(scope_txt));
-                       memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
-                       scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
-               }
-
-               chunk_appendf(chk,
-                             "<form method=\"post\">");
-       }
-
-       /* print a new table */
-       chunk_appendf(chk,
-                     "<table class=\"tbl\" width=\"100%%\">\n"
-                     "<tr class=\"titre\">"
-                     "<th class=\"pxname\" width=\"10%%\">");
-
-       chunk_appendf(chk,
-                     "<a name=\"%s\"></a>%s"
-                     "<a class=px href=\"#%s\">%s</a>",
-                     px->id,
-                     (ctx->flags & STAT_SHLGNDS) ? "<u>":"",
-                     px->id, px->id);
-
-       if (ctx->flags & STAT_SHLGNDS) {
-               /* cap, mode, id */
-               chunk_appendf(chk, "<div class=tips>cap: %s, mode: %s, id: %d",
-                             proxy_cap_str(px->cap), proxy_mode_str(px->mode),
-                             px->uuid);
-               chunk_appendf(chk, "</div>");
-       }
-
-       chunk_appendf(chk,
-                     "%s</th>"
-                     "<th class=\"%s\" width=\"90%%\">%s</th>"
-                     "</tr>\n"
-                     "</table>\n"
-                     "<table class=\"tbl\" width=\"100%%\">\n"
-                     "<tr class=\"titre\">",
-                     (ctx->flags & STAT_SHLGNDS) ? "</u>":"",
-                     px->desc ? "desc" : "empty", px->desc ? px->desc : "");
-
-       if (ctx->flags & STAT_ADMIN) {
-               /* Column heading for Enable or Disable server */
-               if ((px->cap & PR_CAP_BE) && px->srv)
-                       chunk_appendf(chk,
-                                     "<th rowspan=2 width=1><input type=\"checkbox\" "
-                                     "onclick=\"for(c in document.getElementsByClassName('%s-checkbox')) "
-                                     "document.getElementsByClassName('%s-checkbox').item(c).checked = this.checked\"></th>",
-                                     px->id,
-                                     px->id);
-               else
-                       chunk_appendf(chk, "<th rowspan=2></th>");
-       }
-
-       chunk_appendf(chk,
-                     "<th rowspan=2></th>"
-                     "<th colspan=3>Queue</th>"
-                     "<th colspan=3>Session rate</th><th colspan=6>Sessions</th>"
-                     "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
-                     "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
-                     "<th colspan=9>Server</th>");
-
-       if (ctx->flags & STAT_SHMODULES) {
-               // calculate the count of module for colspan attribute
-               list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-                       ++stats_module_len;
-               }
-               chunk_appendf(chk, "<th colspan=%d>Extra modules</th>",
-                             stats_module_len);
-       }
-
-       chunk_appendf(chk,
-                     "</tr>\n"
-                     "<tr class=\"titre\">"
-                     "<th>Cur</th><th>Max</th><th>Limit</th>"
-                     "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
-                     "<th>Limit</th><th>Total</th><th>LbTot</th><th>Last</th><th>In</th><th>Out</th>"
-                     "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
-                     "<th>Resp</th><th>Retr</th><th>Redis</th>"
-                     "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
-                     "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
-                     "<th>Thrtle</th>\n");
-
-       if (ctx->flags & STAT_SHMODULES) {
-               list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-                       chunk_appendf(chk, "<th>%s</th>", mod->name);
-               }
-       }
-
-       chunk_appendf(chk, "</tr>");
-}
-
-/* Dumps the HTML table trailer for proxy <px> to chunk ctx buffer and uses the
- * state from stream connector <sc>. The caller is responsible for clearing
- * chunk ctx buffer if needed.
- */
-static void stats_dump_html_px_end(struct stconn *sc, struct proxy *px)
-{
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct buffer *chk = &ctx->chunk;
-
-       chunk_appendf(chk, "</table>");
-
-       if ((px->cap & PR_CAP_BE) && px->srv && (ctx->flags & STAT_ADMIN)) {
-               /* close the form used to enable/disable this proxy servers */
-               chunk_appendf(chk,
-                             "Choose the action to perform on the checked servers : "
-                             "<select name=action>"
-                             "<option value=\"\"></option>"
-                             "<option value=\"ready\">Set state to READY</option>"
-                             "<option value=\"drain\">Set state to DRAIN</option>"
-                             "<option value=\"maint\">Set state to MAINT</option>"
-                             "<option value=\"dhlth\">Health: disable checks</option>"
-                             "<option value=\"ehlth\">Health: enable checks</option>"
-                             "<option value=\"hrunn\">Health: force UP</option>"
-                             "<option value=\"hnolb\">Health: force NOLB</option>"
-                             "<option value=\"hdown\">Health: force DOWN</option>"
-                             "<option value=\"dagent\">Agent: disable checks</option>"
-                             "<option value=\"eagent\">Agent: enable checks</option>"
-                             "<option value=\"arunn\">Agent: force UP</option>"
-                             "<option value=\"adown\">Agent: force DOWN</option>"
-                             "<option value=\"shutdown\">Kill Sessions</option>"
-                             "</select>"
-                             "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
-                             "&nbsp;<input type=\"submit\" value=\"Apply\">"
-                             "</form>",
-                             px->uuid);
-       }
-
-       chunk_appendf(chk, "<p>\n");
-}
-
 /*
  * Dumps statistics for a proxy. The output is sent to the stream connector's
  * input buffer. Returns 0 if it had to stop dumping data because of lack of
@@ -3410,453 +2408,6 @@ more:
        return 0;
 }
 
-/* Dumps the HTTP stats head block to chunk ctx buffer and uses the per-uri
- * parameters from the parent proxy. The caller is responsible for clearing
- * chunk ctx buffer if needed.
- */
-static void stats_dump_html_head(struct appctx *appctx)
-{
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct buffer *chk = &ctx->chunk;
-       struct uri_auth *uri;
-
-       BUG_ON(!ctx->http_px);
-       uri = ctx->http_px->uri_auth;
-
-       /* WARNING! This must fit in the first buffer !!! */
-       chunk_appendf(chk,
-                     "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
-                     "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
-                     "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
-                     "<link rel=\"icon\" href=\"data:,\">\n"
-                     "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
-                     "<style type=\"text/css\"><!--\n"
-                     "body {"
-                     " font-family: arial, helvetica, sans-serif;"
-                     " font-size: 12px;"
-                     " font-weight: normal;"
-                     " color: black;"
-                     " background: white;"
-                     "}\n"
-                     "th,td {"
-                     " font-size: 10px;"
-                     "}\n"
-                     "h1 {"
-                     " font-size: x-large;"
-                     " margin-bottom: 0.5em;"
-                     "}\n"
-                     "h2 {"
-                     " font-family: helvetica, arial;"
-                     " font-size: x-large;"
-                     " font-weight: bold;"
-                     " font-style: italic;"
-                     " color: #6020a0;"
-                     " margin-top: 0em;"
-                     " margin-bottom: 0em;"
-                     "}\n"
-                     "h3 {"
-                     " font-family: helvetica, arial;"
-                     " font-size: 16px;"
-                     " font-weight: bold;"
-                     " color: #b00040;"
-                     " background: #e8e8d0;"
-                     " margin-top: 0em;"
-                     " margin-bottom: 0em;"
-                     "}\n"
-                     "li {"
-                     " margin-top: 0.25em;"
-                     " margin-right: 2em;"
-                     "}\n"
-                     ".hr {margin-top: 0.25em;"
-                     " border-color: black;"
-                     " border-bottom-style: solid;"
-                     "}\n"
-                     ".titre   {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
-                     ".total   {background: #20D0D0;color: #ffff80;}\n"
-                     ".frontend        {background: #e8e8d0;}\n"
-                     ".socket  {background: #d0d0d0;}\n"
-                     ".backend {background: #e8e8d0;}\n"
-                     ".active_down             {background: #ff9090;}\n"
-                     ".active_going_up         {background: #ffd020;}\n"
-                     ".active_going_down       {background: #ffffa0;}\n"
-                     ".active_up               {background: #c0ffc0;}\n"
-                     ".active_nolb             {background: #20a0ff;}\n"
-                     ".active_draining         {background: #20a0FF;}\n"
-                     ".active_no_check         {background: #e0e0e0;}\n"
-                     ".backup_down             {background: #ff9090;}\n"
-                     ".backup_going_up         {background: #ff80ff;}\n"
-                     ".backup_going_down       {background: #c060ff;}\n"
-                     ".backup_up               {background: #b0d0ff;}\n"
-                     ".backup_nolb             {background: #90b0e0;}\n"
-                     ".backup_draining         {background: #cc9900;}\n"
-                     ".backup_no_check         {background: #e0e0e0;}\n"
-                     ".maintain        {background: #c07820;}\n"
-                     ".rls      {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
-                     "\n"
-                     "a.px:link {color: #ffff40; text-decoration: none;}"
-                     "a.px:visited {color: #ffff40; text-decoration: none;}"
-                     "a.px:hover {color: #ffffff; text-decoration: none;}"
-                     "a.lfsb:link {color: #000000; text-decoration: none;}"
-                     "a.lfsb:visited {color: #000000; text-decoration: none;}"
-                     "a.lfsb:hover {color: #505050; text-decoration: none;}"
-                     "\n"
-                     "table.tbl { border-collapse: collapse; border-style: none;}\n"
-                     "table.tbl td { text-align: right; border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray; white-space: nowrap;}\n"
-                     "table.tbl td.ac { text-align: center;}\n"
-                     "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
-                     "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
-                     "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
-                     "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
-                     "\n"
-                     "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
-                     "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
-                     "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
-                     "table.det { border-collapse: collapse; border-style: none; }\n"
-                     "table.det th { text-align: left; border-width: 0px; padding: 0px 1px 0px 0px; font-style:normal;font-size:11px;font-weight:bold;font-family: sans-serif;}\n"
-                     "table.det td { text-align: right; border-width: 0px; padding: 0px 0px 0px 4px; white-space: nowrap; font-style:normal;font-size:11px;font-weight:normal;}\n"
-                     "u {text-decoration:none; border-bottom: 1px dotted black;}\n"
-                     "div.tips {\n"
-                     " display:block;\n"
-                     " visibility:hidden;\n"
-                     " z-index:2147483647;\n"
-                     " position:absolute;\n"
-                     " padding:2px 4px 3px;\n"
-                     " background:#f0f060; color:#000000;\n"
-                     " border:1px solid #7040c0;\n"
-                     " white-space:nowrap;\n"
-                     " font-style:normal;font-size:11px;font-weight:normal;\n"
-                     " -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;\n"
-                     " -moz-box-shadow:gray 2px 2px 3px;-webkit-box-shadow:gray 2px 2px 3px;box-shadow:gray 2px 2px 3px;\n"
-                     "}\n"
-                     "u:hover div.tips {visibility:visible;}\n"
-                     "@media (prefers-color-scheme: dark) {\n"
-                     " body { font-family: arial, helvetica, sans-serif; font-size: 12px; font-weight: normal; color: #e8e6e3; background: #131516;}\n"
-                     " h1 { color: #a265e0!important; }\n"
-                     " h2 { color: #a265e0; }\n"
-                     " h3 { color: #ff5190; background-color: #3e3e1f; }\n"
-                     " a { color: #3391ff; }\n"
-                     " input { background-color: #2f3437; }\n"
-                     " .hr { border-color: #8c8273; }\n"
-                     " .titre { background-color: #1aa6a6; color: #e8e6e3; }\n"
-                     " .frontend {background: #2f3437;}\n"
-                     " .socket {background: #2a2d2f;}\n"
-                     " .backend {background: #2f3437;}\n"
-                     " .active_down {background: #760000;}\n"
-                     " .active_going_up {background: #b99200;}\n"
-                     " .active_going_down {background: #6c6c00;}\n"
-                     " .active_up {background: #165900;}\n"
-                     " .active_nolb {background: #006ab9;}\n"
-                     " .active_draining {background: #006ab9;}\n"
-                     " .active_no_check {background: #2a2d2f;}\n"
-                     " .backup_down {background: #760000;}\n"
-                     " .backup_going_up {background: #7f007f;}\n"
-                     " .backup_going_down {background: #580092;}\n"
-                     " .backup_up {background: #2e3234;}\n"
-                     " .backup_nolb {background: #1e3c6a;}\n"
-                     " .backup_draining {background: #a37a00;}\n"
-                     " .backup_no_check {background: #2a2d2f;}\n"
-                     " .maintain {background: #9a601a;}\n"
-                     " a.px:link {color: #d8d83b; text-decoration: none;}\n"
-                     " a.px:visited {color: #d8d83b; text-decoration: none;}\n"
-                     " a.px:hover {color: #ffffff; text-decoration: none;}\n"
-                     " a.lfsb:link {color: #e8e6e3; text-decoration: none;}\n"
-                     " a.lfsb:visited {color: #e8e6e3; text-decoration: none;}\n"
-                     " a.lfsb:hover {color: #b5afa6; text-decoration: none;}\n"
-                     " table.tbl th.empty { background-color: #181a1b; }\n"
-                     " table.tbl th.desc { background: #181a1b; }\n"
-                     " table.tbl th.pxname { background-color: #8d0033; color: #ffff46; }\n"
-                     " table.tbl th { border-color: #808080; }\n"
-                     " table.tbl td { border-color: #808080; }\n"
-                     " u {text-decoration:none; border-bottom: 1px dotted #e8e6e3;}\n"
-                     " div.tips {\n"
-                     "  background:#8e8e0d;\n"
-                     "  color:#e8e6e3;\n"
-                     "  border-color: #4e2c86;\n"
-                     "  -moz-box-shadow: #60686c 2px 2px 3px;\n"
-                     "  -webkit-box-shadow: #60686c 2px 2px 3px;\n"
-                     "  box-shadow: #60686c 2px 2px 3px;\n"
-                     " }\n"
-                     "}\n"
-                     "-->\n"
-                     "</style></head>\n",
-                     (ctx->flags & STAT_SHNODE) ? " on " : "",
-                     (ctx->flags & STAT_SHNODE) ? (uri && uri->node ? uri->node : global.node) : ""
-                     );
-}
-
-/* Dumps the HTML stats information block to chunk ctx buffer and uses the
- * state from stream connector <sc> and per-uri parameter from the parent
- * proxy. The caller is responsible for clearing chunk ctx buffer if needed.
- */
-static void stats_dump_html_info(struct stconn *sc)
-{
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct buffer *chk = &ctx->chunk;
-       unsigned int up = ns_to_sec(now_ns - start_time_ns);
-       char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
-       const char *scope_ptr = stats_scope_ptr(appctx);
-       struct uri_auth *uri;
-       unsigned long long bps;
-       int thr;
-
-       BUG_ON(!ctx->http_px);
-       uri = ctx->http_px->uri_auth;
-       for (bps = thr = 0; thr < global.nbthread; thr++)
-               bps += 32ULL * read_freq_ctr(&ha_thread_ctx[thr].out_32bps);
-
-       /* Turn the bytes per second to bits per second and take care of the
-        * usual ethernet overhead in order to help figure how far we are from
-        * interface saturation since it's the only case which usually matters.
-        * For this we count the total size of an Ethernet frame on the wire
-        * including preamble and IFG (1538) for the largest TCP segment it
-        * transports (1448 with TCP timestamps). This is not valid for smaller
-        * packets (under-estimated), but it gives a reasonably accurate
-        * estimation of how far we are from uplink saturation.
-        */
-       bps = bps * 8 * 1538 / 1448;
-
-       /* WARNING! this has to fit the first packet too.
-        * We are around 3.5 kB, add adding entries will
-        * become tricky if we want to support 4kB buffers !
-        */
-       chunk_appendf(chk,
-                     "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
-                     PRODUCT_NAME "%s</a></h1>\n"
-                     "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
-                     "<hr width=\"100%%\" class=\"hr\">\n"
-                     "<h3>&gt; General process information</h3>\n"
-                     "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
-                     "<p><b>pid = </b> %d (process #%d, nbproc = %d, nbthread = %d)<br>\n"
-                     "<b>uptime = </b> %dd %dh%02dm%02ds; warnings = %u<br>\n"
-                     "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
-                     "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>reached = </b> %llu; <b>maxpipes = </b> %d<br>\n"
-                     "current conns = %d; current pipes = %d/%d; conn rate = %d/sec; bit rate = %.3f %cbps<br>\n"
-                     "Running tasks: %d/%d (%d niced); idle = %d %%<br>\n"
-                     "</td><td align=\"center\" nowrap>\n"
-                     "<table class=\"lgd\"><tr>\n"
-                     "<td class=\"active_up\">&nbsp;</td><td class=\"noborder\">active UP </td>"
-                     "<td class=\"backup_up\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
-                     "</tr><tr>\n"
-                     "<td class=\"active_going_down\"></td><td class=\"noborder\">active UP, going down </td>"
-                     "<td class=\"backup_going_down\"></td><td class=\"noborder\">backup UP, going down </td>"
-                     "</tr><tr>\n"
-                     "<td class=\"active_going_up\"></td><td class=\"noborder\">active DOWN, going up </td>"
-                     "<td class=\"backup_going_up\"></td><td class=\"noborder\">backup DOWN, going up </td>"
-                     "</tr><tr>\n"
-                     "<td class=\"active_down\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
-                     "<td class=\"active_no_check\"></td><td class=\"noborder\">not checked </td>"
-                     "</tr><tr>\n"
-                     "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) &nbsp;</td>"
-                     "</tr><tr>\n"
-                     "<td class=\"active_draining\"></td><td class=\"noborder\" colspan=\"3\">active or backup SOFT STOPPED for maintenance &nbsp;</td>"
-                     "</tr></table>\n"
-                     "Note: \"NOLB\"/\"DRAIN\" = UP with load-balancing disabled."
-                     "</td>"
-                     "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
-                     "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
-                     "",
-                     (ctx->flags & STAT_HIDEVER) ? "" : (stats_version_string),
-                     pid, (ctx->flags & STAT_SHNODE) ? " on " : "",
-                     (ctx->flags & STAT_SHNODE) ? (uri->node ? uri->node : global.node) : "",
-                     (ctx->flags & STAT_SHDESC) ? ": " : "",
-                     (ctx->flags & STAT_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
-                     pid, 1, 1, global.nbthread,
-                     up / 86400, (up % 86400) / 3600,
-                     (up % 3600) / 60, (up % 60),
-                     HA_ATOMIC_LOAD(&tot_warnings),
-                     global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
-                     global.rlimit_memmax ? " MB" : "",
-                     global.rlimit_nofile,
-                     global.maxsock, global.maxconn, HA_ATOMIC_LOAD(&maxconn_reached), global.maxpipes,
-                     actconn, pipes_used, pipes_used+pipes_free, read_freq_ctr(&global.conn_per_sec),
-                     bps >= 1000000000UL ? (bps / 1000000000.0) : bps >= 1000000UL ? (bps / 1000000.0) : (bps / 1000.0),
-                     bps >= 1000000000UL ? 'G' : bps >= 1000000UL ? 'M' : 'k',
-                     total_run_queues(), total_allocated_tasks(), total_niced_running_tasks(), clock_report_idle());
-
-       /* scope_txt = search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
-       memcpy(scope_txt, scope_ptr, ctx->scope_len);
-       scope_txt[ctx->scope_len] = '\0';
-
-       chunk_appendf(chk,
-                     "<li><form method=\"GET\">Scope : <input value=\"%s\" name=\"" STAT_SCOPE_INPUT_NAME "\" size=\"8\" maxlength=\"%d\" tabindex=\"1\"/></form>\n",
-                     (ctx->scope_len > 0) ? scope_txt : "",
-                     STAT_SCOPE_TXT_MAXLEN);
-
-       /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
-       scope_txt[0] = 0;
-       if (ctx->scope_len) {
-               strlcpy2(scope_txt, STAT_SCOPE_PATTERN, sizeof(scope_txt));
-               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
-               scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
-       }
-
-       if (ctx->flags & STAT_HIDE_DOWN)
-               chunk_appendf(chk,
-                             "<li><a href=\"%s%s%s%s\">Show all servers</a><br>\n",
-                             uri->uri_prefix,
-                             "",
-                             (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                             scope_txt);
-       else
-               chunk_appendf(chk,
-                             "<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
-                             uri->uri_prefix,
-                             ";up",
-                             (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                             scope_txt);
-
-       if (uri->refresh > 0) {
-               if (ctx->flags & STAT_NO_REFRESH)
-                       chunk_appendf(chk,
-                                     "<li><a href=\"%s%s%s%s\">Enable refresh</a><br>\n",
-                                     uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     "",
-                                     scope_txt);
-               else
-                       chunk_appendf(chk,
-                                     "<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
-                                     uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     ";norefresh",
-                                     scope_txt);
-       }
-
-       chunk_appendf(chk,
-                     "<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
-                     uri->uri_prefix,
-                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                     scope_txt);
-
-       chunk_appendf(chk,
-                     "<li><a href=\"%s;csv%s%s\">CSV export</a><br>\n",
-                     uri->uri_prefix,
-                     (uri->refresh > 0) ? ";norefresh" : "",
-                     scope_txt);
-
-       chunk_appendf(chk,
-                     "<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(chk,
-                     "</ul></td>"
-                     "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
-                     "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
-                     "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
-                     "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
-                     "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
-                     "</ul>"
-                     "</td>"
-                     "</tr></table>\n"
-                     ""
-                     );
-
-       if (ctx->st_code) {
-               switch (ctx->st_code) {
-               case STAT_STATUS_DONE:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_up>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "Action processed successfully."
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               case STAT_STATUS_NONE:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_going_down>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "Nothing has changed."
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               case STAT_STATUS_PART:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_going_down>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "Action partially processed.<br>"
-                                     "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               case STAT_STATUS_ERRP:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_down>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "Action not processed because of invalid parameters."
-                                     "<ul>"
-                                     "<li>The action is maybe unknown.</li>"
-                                     "<li>Invalid key parameter (empty or too long).</li>"
-                                     "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
-                                     "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
-                                     "</ul>"
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               case STAT_STATUS_EXCD:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_down>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "<b>Action not processed : the buffer couldn't store all the data.<br>"
-                                     "You should retry with less servers at a time.</b>"
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               case STAT_STATUS_DENY:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_down>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "<b>Action denied.</b>"
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               case STAT_STATUS_IVAL:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_down>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "<b>Invalid requests (unsupported method or chunked encoded request).</b>"
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-                       break;
-               default:
-                       chunk_appendf(chk,
-                                     "<p><div class=active_no_check>"
-                                     "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
-                                     "Unexpected result."
-                                     "</div>\n", uri->uri_prefix,
-                                     (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                                     (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                                     scope_txt);
-               }
-               chunk_appendf(chk, "<p>\n");
-       }
-}
-
-/* Dumps the HTML stats trailer block to <out> buffer. The caller is
- * responsible for clearing it if needed.
- */
-static void stats_dump_html_end(struct buffer *out)
-{
-       chunk_appendf(out, "</body></html>\n");
-}
-
 /* Dumps the stats JSON header to <out> buffer. The caller is responsible for
  * clearing it if needed.
  */
@@ -3917,7 +2468,7 @@ static int stats_dump_proxies(struct stconn *sc, struct buffer *buf,
  * or -1 in case of any error. This function is used by both the CLI and the
  * HTTP handlers.
  */
-static int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx)
+int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx *htx)
 {
        struct appctx *appctx = __sc_appctx(sc);
        struct show_stat_ctx *ctx = appctx->svcctx;
@@ -4014,590 +2565,6 @@ static int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, stru
 
 }
 
-/* We reached the stats page through a POST request. The appctx is
- * expected to have already been allocated by the caller.
- * Parse the posted data and enable/disable servers if necessary.
- * Returns 1 if request was parsed or zero if it needs more data.
- */
-static int stats_process_http_post(struct stconn *sc)
-{
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_stat_ctx *ctx = appctx->svcctx;
-
-       struct proxy *px = NULL;
-       struct server *sv = NULL;
-
-       char key[LINESIZE];
-       int action = ST_ADM_ACTION_NONE;
-       int reprocess = 0;
-
-       int total_servers = 0;
-       int altered_servers = 0;
-
-       char *first_param, *cur_param, *next_param, *end_params;
-       char *st_cur_param = NULL;
-       char *st_next_param = NULL;
-
-       struct buffer *temp = get_trash_chunk();
-
-       struct htx *htx = htxbuf(&appctx->inbuf);
-       struct htx_blk *blk;
-
-       /*  we need more data */
-       if (!(htx->flags & HTX_FL_EOM)) {
-               /* check if we can receive more */
-               if (applet_fl_test(appctx, APPCTX_FL_INBLK_FULL)) {
-                       ctx->st_code = STAT_STATUS_EXCD;
-                       goto out;
-               }
-               goto  wait;
-       }
-
-       /* The request was fully received. Copy data */
-       blk = htx_get_head_blk(htx);
-       while (blk) {
-               enum htx_blk_type type = htx_get_blk_type(blk);
-
-               if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
-                       break;
-               if (type == HTX_BLK_DATA) {
-                       struct ist v = htx_get_blk_value(htx, blk);
-
-                       if (!chunk_memcat(temp, v.ptr, v.len)) {
-                               ctx->st_code = STAT_STATUS_EXCD;
-                               goto out;
-                       }
-               }
-               blk = htx_get_next_blk(htx, blk);
-       }
-
-       first_param = temp->area;
-       end_params  = temp->area + temp->data;
-       cur_param = next_param = end_params;
-       *end_params = '\0';
-
-       ctx->st_code = STAT_STATUS_NONE;
-
-       /*
-        * Parse the parameters in reverse order to only store the last value.
-        * From the html form, the backend and the action are at the end.
-        */
-       while (cur_param > first_param) {
-               char *value;
-               int poffset, plen;
-
-               cur_param--;
-
-               if ((*cur_param == '&') || (cur_param == first_param)) {
-               reprocess_servers:
-                       /* Parse the key */
-                       poffset = (cur_param != first_param ? 1 : 0);
-                       plen = next_param - cur_param + (cur_param == first_param ? 1 : 0);
-                       if ((plen > 0) && (plen <= sizeof(key))) {
-                               strncpy(key, cur_param + poffset, plen);
-                               key[plen - 1] = '\0';
-                       } else {
-                               ctx->st_code = STAT_STATUS_ERRP;
-                               goto out;
-                       }
-
-                       /* Parse the value */
-                       value = key;
-                       while (*value != '\0' && *value != '=') {
-                               value++;
-                       }
-                       if (*value == '=') {
-                               /* Ok, a value is found, we can mark the end of the key */
-                               *value++ = '\0';
-                       }
-                       if (url_decode(key, 1) < 0 || url_decode(value, 1) < 0)
-                               break;
-
-                       /* Now we can check the key to see what to do */
-                       if (!px && (strcmp(key, "b") == 0)) {
-                               if ((px = proxy_be_by_name(value)) == NULL) {
-                                       /* the backend name is unknown or ambiguous (duplicate names) */
-                                       ctx->st_code = STAT_STATUS_ERRP;
-                                       goto out;
-                               }
-                       }
-                       else if (!action && (strcmp(key, "action") == 0)) {
-                               if (strcmp(value, "ready") == 0) {
-                                       action = ST_ADM_ACTION_READY;
-                               }
-                               else if (strcmp(value, "drain") == 0) {
-                                       action = ST_ADM_ACTION_DRAIN;
-                               }
-                               else if (strcmp(value, "maint") == 0) {
-                                       action = ST_ADM_ACTION_MAINT;
-                               }
-                               else if (strcmp(value, "shutdown") == 0) {
-                                       action = ST_ADM_ACTION_SHUTDOWN;
-                               }
-                               else if (strcmp(value, "dhlth") == 0) {
-                                       action = ST_ADM_ACTION_DHLTH;
-                               }
-                               else if (strcmp(value, "ehlth") == 0) {
-                                       action = ST_ADM_ACTION_EHLTH;
-                               }
-                               else if (strcmp(value, "hrunn") == 0) {
-                                       action = ST_ADM_ACTION_HRUNN;
-                               }
-                               else if (strcmp(value, "hnolb") == 0) {
-                                       action = ST_ADM_ACTION_HNOLB;
-                               }
-                               else if (strcmp(value, "hdown") == 0) {
-                                       action = ST_ADM_ACTION_HDOWN;
-                               }
-                               else if (strcmp(value, "dagent") == 0) {
-                                       action = ST_ADM_ACTION_DAGENT;
-                               }
-                               else if (strcmp(value, "eagent") == 0) {
-                                       action = ST_ADM_ACTION_EAGENT;
-                               }
-                               else if (strcmp(value, "arunn") == 0) {
-                                       action = ST_ADM_ACTION_ARUNN;
-                               }
-                               else if (strcmp(value, "adown") == 0) {
-                                       action = ST_ADM_ACTION_ADOWN;
-                               }
-                               /* else these are the old supported methods */
-                               else if (strcmp(value, "disable") == 0) {
-                                       action = ST_ADM_ACTION_DISABLE;
-                               }
-                               else if (strcmp(value, "enable") == 0) {
-                                       action = ST_ADM_ACTION_ENABLE;
-                               }
-                               else if (strcmp(value, "stop") == 0) {
-                                       action = ST_ADM_ACTION_STOP;
-                               }
-                               else if (strcmp(value, "start") == 0) {
-                                       action = ST_ADM_ACTION_START;
-                               }
-                               else {
-                                       ctx->st_code = STAT_STATUS_ERRP;
-                                       goto out;
-                               }
-                       }
-                       else if (strcmp(key, "s") == 0) {
-                               if (!(px && action)) {
-                                       /*
-                                        * Indicates that we'll need to reprocess the parameters
-                                        * as soon as backend and action are known
-                                        */
-                                       if (!reprocess) {
-                                               st_cur_param  = cur_param;
-                                               st_next_param = next_param;
-                                       }
-                                       reprocess = 1;
-                               }
-                               else if ((sv = findserver(px, value)) != NULL) {
-                                       HA_SPIN_LOCK(SERVER_LOCK, &sv->lock);
-                                       switch (action) {
-                                       case ST_ADM_ACTION_DISABLE:
-                                               if (!(sv->cur_admin & SRV_ADMF_FMAINT)) {
-                                                       altered_servers++;
-                                                       total_servers++;
-                                                       srv_set_admin_flag(sv, SRV_ADMF_FMAINT, SRV_ADM_STCHGC_STATS_DISABLE);
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_ENABLE:
-                                               if (sv->cur_admin & SRV_ADMF_FMAINT) {
-                                                       altered_servers++;
-                                                       total_servers++;
-                                                       srv_clr_admin_flag(sv, SRV_ADMF_FMAINT);
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_STOP:
-                                               if (!(sv->cur_admin & SRV_ADMF_FDRAIN)) {
-                                                       srv_set_admin_flag(sv, SRV_ADMF_FDRAIN, SRV_ADM_STCHGC_STATS_STOP);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_START:
-                                               if (sv->cur_admin & SRV_ADMF_FDRAIN) {
-                                                       srv_clr_admin_flag(sv, SRV_ADMF_FDRAIN);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_DHLTH:
-                                               if (sv->check.state & CHK_ST_CONFIGURED) {
-                                                       sv->check.state &= ~CHK_ST_ENABLED;
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_EHLTH:
-                                               if (sv->check.state & CHK_ST_CONFIGURED) {
-                                                       sv->check.state |= CHK_ST_ENABLED;
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_HRUNN:
-                                               if (!(sv->track)) {
-                                                       sv->check.health = sv->check.rise + sv->check.fall - 1;
-                                                       srv_set_running(sv, SRV_OP_STCHGC_STATS_WEB);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_HNOLB:
-                                               if (!(sv->track)) {
-                                                       sv->check.health = sv->check.rise + sv->check.fall - 1;
-                                                       srv_set_stopping(sv, SRV_OP_STCHGC_STATS_WEB);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_HDOWN:
-                                               if (!(sv->track)) {
-                                                       sv->check.health = 0;
-                                                       srv_set_stopped(sv, SRV_OP_STCHGC_STATS_WEB);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_DAGENT:
-                                               if (sv->agent.state & CHK_ST_CONFIGURED) {
-                                                       sv->agent.state &= ~CHK_ST_ENABLED;
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_EAGENT:
-                                               if (sv->agent.state & CHK_ST_CONFIGURED) {
-                                                       sv->agent.state |= CHK_ST_ENABLED;
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_ARUNN:
-                                               if (sv->agent.state & CHK_ST_ENABLED) {
-                                                       sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
-                                                       srv_set_running(sv, SRV_OP_STCHGC_STATS_WEB);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_ADOWN:
-                                               if (sv->agent.state & CHK_ST_ENABLED) {
-                                                       sv->agent.health = 0;
-                                                       srv_set_stopped(sv, SRV_OP_STCHGC_STATS_WEB);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       case ST_ADM_ACTION_READY:
-                                               srv_adm_set_ready(sv);
-                                               altered_servers++;
-                                               total_servers++;
-                                               break;
-                                       case ST_ADM_ACTION_DRAIN:
-                                               srv_adm_set_drain(sv);
-                                               altered_servers++;
-                                               total_servers++;
-                                               break;
-                                       case ST_ADM_ACTION_MAINT:
-                                               srv_adm_set_maint(sv);
-                                               altered_servers++;
-                                               total_servers++;
-                                               break;
-                                       case ST_ADM_ACTION_SHUTDOWN:
-                                               if (!(px->flags & (PR_FL_DISABLED|PR_FL_STOPPED))) {
-                                                       srv_shutdown_streams(sv, SF_ERR_KILLED);
-                                                       altered_servers++;
-                                                       total_servers++;
-                                               }
-                                               break;
-                                       }
-                                       HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock);
-                               } else {
-                                       /* the server name is unknown or ambiguous (duplicate names) */
-                                       total_servers++;
-                               }
-                       }
-                       if (reprocess && px && action) {
-                               /* Now, we know the backend and the action chosen by the user.
-                                * We can safely restart from the first server parameter
-                                * to reprocess them
-                                */
-                               cur_param  = st_cur_param;
-                               next_param = st_next_param;
-                               reprocess = 0;
-                               goto reprocess_servers;
-                       }
-
-                       next_param = cur_param;
-               }
-       }
-
-       if (total_servers == 0) {
-               ctx->st_code = STAT_STATUS_NONE;
-       }
-       else if (altered_servers == 0) {
-               ctx->st_code = STAT_STATUS_ERRP;
-       }
-       else if (altered_servers == total_servers) {
-               ctx->st_code = STAT_STATUS_DONE;
-       }
-       else {
-               ctx->st_code = STAT_STATUS_PART;
-       }
- out:
-       return 1;
- wait:
-       ctx->st_code = STAT_STATUS_NONE;
-       return 0;
-}
-
-
-static int stats_send_http_headers(struct stconn *sc, struct htx *htx)
-{
-       struct uri_auth *uri;
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct htx_sl *sl;
-       unsigned int flags;
-
-       BUG_ON(!ctx->http_px);
-       uri = ctx->http_px->uri_auth;
-
-       flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_ENC|HTX_SL_F_XFER_LEN|HTX_SL_F_CHNK);
-       sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("200"), ist("OK"));
-       if (!sl)
-               goto full;
-       sl->info.res.status = 200;
-
-       if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")))
-               goto full;
-       if (ctx->flags & STAT_FMT_HTML) {
-               if (!htx_add_header(htx, ist("Content-Type"), ist("text/html")))
-                       goto full;
-       }
-       else if (ctx->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;
-       }
-
-       if (uri->refresh > 0 && !(ctx->flags & STAT_NO_REFRESH)) {
-               const char *refresh = U2A(uri->refresh);
-               if (!htx_add_header(htx, ist("Refresh"), ist(refresh)))
-                       goto full;
-       }
-
-       if (ctx->flags & STAT_CHUNKED) {
-               if (!htx_add_header(htx, ist("Transfer-Encoding"), ist("chunked")))
-                       goto full;
-       }
-
-       if (!htx_add_endof(htx, HTX_BLK_EOH))
-               goto full;
-       return 1;
-
-  full:
-       htx_reset(htx);
-       applet_set_eos(appctx);
-       applet_set_error(appctx);
-       return 0;
-}
-
-
-static int stats_send_http_redirect(struct stconn *sc, struct htx *htx)
-{
-       char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
-       struct uri_auth *uri;
-       struct appctx *appctx = __sc_appctx(sc);
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct htx_sl *sl;
-       unsigned int flags;
-
-       BUG_ON(!ctx->http_px);
-       uri = ctx->http_px->uri_auth;
-
-       /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
-       scope_txt[0] = 0;
-       if (ctx->scope_len) {
-               const char *scope_ptr = stats_scope_ptr(appctx);
-
-               strlcpy2(scope_txt, STAT_SCOPE_PATTERN, sizeof(scope_txt));
-               memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), scope_ptr, ctx->scope_len);
-               scope_txt[strlen(STAT_SCOPE_PATTERN) + ctx->scope_len] = 0;
-       }
-
-       /* We don't want to land on the posted stats page because a refresh will
-        * repost the data. We don't want this to happen on accident so we redirect
-        * the browse to the stats page with a GET.
-        */
-       chunk_printf(&trash, "%s;st=%s%s%s%s",
-                    uri->uri_prefix,
-                    ((ctx->st_code > STAT_STATUS_INIT) &&
-                     (ctx->st_code < STAT_STATUS_SIZE) &&
-                     stat_status_codes[ctx->st_code]) ?
-                    stat_status_codes[ctx->st_code] :
-                    stat_status_codes[STAT_STATUS_UNKN],
-                    (ctx->flags & STAT_HIDE_DOWN) ? ";up" : "",
-                    (ctx->flags & STAT_NO_REFRESH) ? ";norefresh" : "",
-                    scope_txt);
-
-       flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_CLEN|HTX_SL_F_CHNK);
-       sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, ist("HTTP/1.1"), ist("303"), ist("See Other"));
-       if (!sl)
-               goto full;
-       sl->info.res.status = 303;
-
-       if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
-           !htx_add_header(htx, ist("Content-Type"), ist("text/plain")) ||
-           !htx_add_header(htx, ist("Content-Length"), ist("0")) ||
-           !htx_add_header(htx, ist("Location"), ist2(trash.area, trash.data)))
-               goto full;
-
-       if (!htx_add_endof(htx, HTX_BLK_EOH))
-               goto full;
-
-       return 1;
-
-  full:
-       htx_reset(htx);
-       applet_set_eos(appctx);
-       applet_set_error(appctx);
-       return 0;
-}
-
-static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf, size_t count, unsigned int flags)
-{
-       struct stconn *sc = appctx_sc(appctx);
-       size_t ret = 0;
-
-       ret = b_data(buf);
-       if (stats_dump_stat_to_buffer(sc, buf, NULL)) {
-               se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
-               applet_fl_clr(appctx, APPCTX_FL_FASTFWD);
-               appctx->st0 = STAT_HTTP_DONE;
-       }
-
-       ret = b_data(buf) - ret;
-       return ret;
-}
-
-/* This I/O handler runs as an applet embedded in a stream connector. It is
- * used to send HTTP stats over a TCP socket. The mechanism is very simple.
- * appctx->st0 contains the operation in progress (dump, done). The handler
- * automatically unregisters itself once transfer is complete.
- */
-static void http_stats_io_handler(struct appctx *appctx)
-{
-       struct show_stat_ctx *ctx = appctx->svcctx;
-       struct stconn *sc = appctx_sc(appctx);
-       struct htx *res_htx = NULL;
-
-       /* only proxy stats are available via http */
-       ctx->domain = STATS_DOMAIN_PROXY;
-
-       if (applet_fl_test(appctx, APPCTX_FL_INBLK_ALLOC|APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
-               goto out;
-
-       if (applet_fl_test(appctx, APPCTX_FL_FASTFWD) && se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD))
-               goto out;
-
-       if (!appctx_get_buf(appctx, &appctx->outbuf)) {
-               applet_fl_set(appctx, APPCTX_FL_OUTBLK_ALLOC);
-               goto out;
-       }
-
-       res_htx = htx_from_buf(&appctx->outbuf);
-
-       if (unlikely(applet_fl_test(appctx, APPCTX_FL_EOS|APPCTX_FL_ERROR))) {
-               appctx->st0 = STAT_HTTP_END;
-               goto out;
-       }
-
-       /* all states are processed in sequence */
-       if (appctx->st0 == STAT_HTTP_HEAD) {
-               if (stats_send_http_headers(sc, res_htx)) {
-                       struct ist meth = htx_sl_req_meth(http_get_stline(htxbuf(&appctx->inbuf)));
-
-                       if (find_http_meth(istptr(meth), istlen(meth)) == HTTP_METH_HEAD)
-                               appctx->st0 = STAT_HTTP_DONE;
-                       else {
-                               if (!(global.tune.no_zero_copy_fwd & NO_ZERO_COPY_FWD_APPLET))
-                                       se_fl_set(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
-                               appctx->st0 = STAT_HTTP_DUMP;
-                       }
-               }
-       }
-
-       if (appctx->st0 == STAT_HTTP_DUMP) {
-               ctx->chunk = b_make(trash.area, appctx->outbuf.size, 0, 0);
-               /* adjust buffer size to take htx overhead into account,
-                * make sure to perform this call on an empty buffer
-                */
-               ctx->chunk.size = buf_room_for_htx_data(&ctx->chunk);
-               if (stats_dump_stat_to_buffer(sc, NULL, res_htx))
-                       appctx->st0 = STAT_HTTP_DONE;
-       }
-
-       if (appctx->st0 == STAT_HTTP_POST) {
-               if (stats_process_http_post(sc))
-                       appctx->st0 = STAT_HTTP_LAST;
-       }
-
-       if (appctx->st0 == STAT_HTTP_LAST) {
-               if (stats_send_http_redirect(sc, res_htx))
-                       appctx->st0 = STAT_HTTP_DONE;
-       }
-
-       if (appctx->st0 == STAT_HTTP_DONE) {
-               /* no more data are expected. If the response buffer is empty,
-                * be sure to add something (EOT block in this case) to have
-                * something to send. It is important to be sure the EOM flags
-                * will be handled by the endpoint.
-                */
-               if (htx_is_empty(res_htx)) {
-                       if (!htx_add_endof(res_htx, HTX_BLK_EOT)) {
-                               applet_fl_set(appctx, APPCTX_FL_OUTBLK_FULL);
-                               goto out;
-                       }
-               }
-               res_htx->flags |= HTX_FL_EOM;
-               applet_set_eoi(appctx);
-               se_fl_clr(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD);
-               applet_fl_clr(appctx, APPCTX_FL_FASTFWD);
-               appctx->st0 = STAT_HTTP_END;
-       }
-
-       if (appctx->st0 == STAT_HTTP_END) {
-               applet_set_eos(appctx);
-               applet_will_consume(appctx);
-       }
-
- out:
-       /* we have left the request in the buffer for the case where we
-        * process a POST, and this automatically re-enables activity on
-        * read. It's better to indicate that we want to stop reading when
-        * we're sending, so that we know there's at most one direction
-        * deciding to wake the applet up. It saves it from looping when
-        * emitting large blocks into small TCP windows.
-        */
-       if (res_htx)
-               htx_to_buf(res_htx, &appctx->outbuf);
-
-       if (appctx->st0 == STAT_HTTP_END) {
-               /* eat the whole request */
-               b_reset(&appctx->inbuf);
-               applet_fl_clr(appctx, APPCTX_FL_INBLK_FULL);
-               appctx->sedesc->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
-       }
-       else if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_FULL))
-               applet_wont_consume(appctx);
-}
-
 /* Dump all fields from <info> into <out> using the "show info" format (name: value) */
 static int stats_dump_info_fields(struct buffer *out,
                                   const struct field *info,
@@ -5056,14 +3023,6 @@ static int stats_dump_json_schema_to_buffer(struct appctx *appctx)
        return 1;
 }
 
-static void http_stats_release(struct appctx *appctx)
-{
-       struct show_stat_ctx *ctx = appctx->svcctx;
-
-       if (ctx->px_st == STAT_PX_ST_SV)
-               srv_drop(ctx->obj2);
-}
-
 static int cli_parse_clear_counters(char **args, char *payload, struct appctx *appctx, void *private)
 {
        struct proxy *px;
@@ -5545,16 +3504,6 @@ static struct cli_kw_list cli_kws = {{ },{
 
 INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
 
-struct applet http_stats_applet = {
-       .obj_type = OBJ_TYPE_APPLET,
-       .name = "<STATS>", /* used for logging */
-       .fct = http_stats_io_handler,
-       .rcv_buf = appctx_htx_rcv_buf,
-       .snd_buf = appctx_htx_snd_buf,
-       .fastfwd = http_stats_fastfwd,
-       .release = http_stats_release,
-};
-
 /*
  * Local variables:
  *  c-indent-level: 8