]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] udpated the stats page to clearly distinguish FEs and BEs
authorWilly Tarreau <w@1wt.eu>
Mon, 1 Jan 2007 20:38:07 +0000 (21:38 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 1 Jan 2007 20:38:07 +0000 (21:38 +0100)
The stats page could not tell the difference between a FE and a BE.
It has been revamped to indicate all relevant information. The font
is also slightly smaller in order for all the info to fit into small
screens. The data output path has been greatly simplified to use
string chunks.

include/common/version.h
include/proto/buffers.h
include/proto/proto_http.h
include/types/httperr.h
include/types/proto_http.h
include/types/proxy.h
src/buffers.c
src/log.c
src/proto_http.c

index d137b4f8cc89d576293ce4383e21a98f67c0813c..c8fed3db9f4650f0b5149c8123b02e85a8072d58 100644 (file)
 #define PRODUCT_NAME    "HAProxy"
 #endif
 
+#ifdef  CONFIG_PRODUCT_BRANCH
+#define PRODUCT_BRANCH    CONFIG_PRODUCT_BRANCH
+#else
+#define PRODUCT_BRANCH   "1.3"
+#endif
+
+#ifdef  CONFIG_PRODUCT_URL
+#define PRODUCT_URL    CONFIG_PRODUCT_URL
+#else
+#define PRODUCT_URL    "http://haproxy.1wt.eu/"
+#endif
+
+#ifdef  CONFIG_PRODUCT_URL_UPD
+#define PRODUCT_URL_UPD  CONFIG_PRODUCT_URL_UPD
+#else
+#define PRODUCT_URL_UPD "http://haproxy.1wt.eu/#down"
+#endif
+
+#ifdef  CONFIG_PRODUCT_URL_DOC
+#define PRODUCT_URL_DOC  CONFIG_PRODUCT_URL_DOC
+#else
+#define PRODUCT_URL_DOC "http://haproxy.1wt.eu/#docs"
+#endif
+
 #ifdef CONFIG_HAPROXY_VERSION
 #define HAPROXY_VERSION CONFIG_HAPROXY_VERSION
 #else
index ca88b1671e535f85562c67edb50821deb00da6b7..657786026a266d1a551d9b2272b99b78dbb20ff5 100644 (file)
@@ -83,8 +83,10 @@ static inline int buffer_realign(struct buffer *buf)
 
 
 int buffer_write(struct buffer *buf, const char *msg, int len);
+int buffer_write_chunk(struct buffer *buf, struct chunk *chunk);
 int buffer_replace(struct buffer *b, char *pos, char *end, char *str);
 int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len);
+int chunk_printf(struct chunk *chk, int size, const char *fmt, ...);
 
 /*
  * frees the destination chunk if already allocated, allocates a new string,
index 82f68a19d5931534db11eab95f45c8799cb7e966..45a42d7929bfcf89178d64e9ad854bb42cb18574 100644 (file)
@@ -46,6 +46,8 @@ void srv_close_with_err(struct session *t, int err, int finst,
                        int status, const struct chunk *msg);
 
 int produce_content(struct session *s);
+int produce_content_stats(struct session *s);
+int produce_content_stats_proxy(struct session *s, struct proxy *px);
 void debug_hdr(const char *dir, struct session *t, const char *start, const char *end);
 void get_srv_from_appsession(struct session *t, const char *begin, const char *end);
 void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_exp *exp);
index c466328f4da189eecbc83aeaf07fb300435c9136..48c013a142f63d1d081bf064ad83705543d6f845 100644 (file)
 
 #include <common/config.h>
 
-/* various data sources for the responses */
-#define DATA_SRC_NONE  0
-#define DATA_SRC_STATS 1
-
-/* data transmission states for the responses */
-#define DATA_ST_INIT   0
-#define DATA_ST_DATA   1
-
 /*
  * All implemented return codes
  */
index 6e6472e6fa2a9097ca9a3a229819f9b599c0fcfc..82c9692829f2616243a6d0dbcab3e4729bab1541 100644 (file)
                                 * an LF is expected before entering the
                                 * designated state. */
 
+/* various data sources for the responses */
+#define DATA_SRC_NONE  0
+#define DATA_SRC_STATS 1
+
+/* data transmission states for the stats responses */
+enum {
+       DATA_ST_INIT = 0,
+       DATA_ST_HEAD,
+       DATA_ST_INFO,
+       DATA_ST_LIST,
+       DATA_ST_END,
+       DATA_ST_FIN,
+};
+
+/* data transmission states for the stats responses inside a proxy */
+enum {
+       DATA_ST_PX_INIT = 0,
+       DATA_ST_PX_TH,
+       DATA_ST_PX_FE,
+       DATA_ST_PX_SV,
+       DATA_ST_PX_BE,
+       DATA_ST_PX_END,
+       DATA_ST_PX_FIN,
+};
+
 
 #endif /* _TYPES_PROTO_HTTP_H */
 
index bf946a3cd3f3e610758bcef04b86b36bbeaa1666..d710be8d55a42fc6b42f42c15dbbf2a7e779f5c9 100644 (file)
@@ -108,7 +108,8 @@ struct proxy {
        unsigned int maxconn;                   /* max # of active sessions on the frontend */
        unsigned int fullconn;                  /* #conns on backend above which servers are used at full load */
        unsigned failed_conns, failed_resp;     /* failed connect() and responses */
-       unsigned failed_secu;                   /* blocked responses because of security concerns */
+       unsigned denied_req, denied_resp;       /* blocked requests/responses because of security concerns */
+       unsigned failed_req;                    /* failed requests (eg: invalid or timeout) */
        int conn_retries;                       /* maximum number of connect retries */
        int options;                            /* PR_O_REDISP, PR_O_TRANSP, ... */
        int mode;                               /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
index 5e71b532cc6174b2efa087ef6bf7e3fd376b1db9..def98562306e1823bbd527e1178e6e4bcd89e320 100644 (file)
@@ -10,6 +10,8 @@
  *
  */
 
+#include <stdarg.h>
+#include <stdio.h>
 #include <string.h>
 
 #include <common/config.h>
@@ -38,6 +40,31 @@ int buffer_write(struct buffer *buf, const char *msg, int len)
        return 0;
 }
 
+/* writes the chunk <chunk> to buffer <buf>. Returns 0 in case of
+ * success, or the number of bytes available otherwise. If the chunk
+ * has been written, its size is automatically reset to zero.
+ */
+int buffer_write_chunk(struct buffer *buf, struct chunk *chunk)
+{
+       int max;
+
+       if (chunk->len == 0)
+               return 0;
+
+       max = buffer_realign(buf);
+
+       if (chunk->len > max)
+               return max;
+
+       memcpy(buf->r, chunk->str, chunk->len);
+       buf->l += chunk->len;
+       buf->r += chunk->len;
+       if (buf->r == buf->data + BUFSIZE)
+               buf->r = buf->data;
+       chunk->len = 0;
+       return 0;
+}
+
 /*
  * this function writes the string <str> at position <pos> which must be in buffer <b>,
  * and moves <end> just after the end of <str>.
@@ -110,6 +137,22 @@ int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len)
 }
 
 
+/*
+ * Does an snprintf() at the end of chunk <chk>, respecting the limit of
+ * at most <size> chars. If the size is over, nothing is added. Returns
+ * the new chunk size.
+ */
+int chunk_printf(struct chunk *chk, int size, const char *fmt, ...)
+{
+       va_list argp;
+
+       va_start(argp, fmt);
+       chk->len += vsnprintf(chk->str + chk->len, size - chk->len, fmt, argp);
+       va_end(argp);
+       return chk->len;
+}
+
+
 /*
  * Local variables:
  *  c-indent-level: 8
index 851cba4328994a13c2f8673692e2d56ed195ba56..ba795dc2a0d801c94980db9ce79f3687670b2b30 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -26,8 +26,8 @@
 
 #include <types/backend.h>
 #include <types/global.h>
-#include <types/httperr.h>
 #include <types/log.h>
+#include <types/proto_http.h>
 #include <types/proxy.h>
 #include <types/session.h>
 
index 21ea4c91078f9b7c68f6a7a37fd9c99ab65ffb71..4d6076f004ef7b5a629d539baf0584b4ba865a46 100644 (file)
@@ -928,6 +928,7 @@ int process_cli(struct session *t)
                                tv_eternity(&req->rex);
                                fd_delete(t->cli_fd);
                                t->cli_state = CL_STCLOSE;
+                               t->fe->failed_req++;
                                if (!(t->flags & SN_ERR_MASK))
                                        t->flags |= SN_ERR_CLICL;
                                if (!(t->flags & SN_FINST_MASK))
@@ -940,6 +941,7 @@ int process_cli(struct session *t)
                                /* read timeout : give up with an error message. */
                                t->logs.status = 408;
                                client_retnclose(t, error_message(t, HTTP_ERR_408));
+                               t->fe->failed_req++;
                                if (!(t->flags & SN_ERR_MASK))
                                        t->flags |= SN_ERR_CLITO;
                                if (!(t->flags & SN_FINST_MASK))
@@ -1266,6 +1268,7 @@ int process_cli(struct session *t)
                t->hreq.hdr_state = HTTP_PA_ERROR;
                t->logs.status = 400;
                client_retnclose(t, error_message(t, HTTP_ERR_400));
+               t->fe->failed_req++;
        return_prx_cond:
                if (!(t->flags & SN_ERR_MASK))
                        t->flags |= SN_ERR_PRXCOND;
@@ -1817,7 +1820,7 @@ int process_srv(struct session *t)
                                                        t->srv->cur_sess--;
                                                        t->srv->failed_secu++;
                                                }
-                                               t->be->failed_secu++;
+                                               t->be->beprm->denied_resp++;
                                                t->srv_state = SV_STCLOSE;
                                                t->logs.status = 502;
                                                client_return(t, error_message(t, HTTP_ERR_502));
@@ -1848,7 +1851,7 @@ int process_srv(struct session *t)
                                                t->srv->cur_sess--;
                                                t->srv->failed_secu++;
                                        }
-                                       t->be->failed_secu++;
+                                       t->be->beprm->denied_resp++;
                                        t->srv_state = SV_STCLOSE;
                                        t->logs.status = 502;
                                        client_return(t, error_message(t, HTTP_ERR_502));
@@ -2719,396 +2722,521 @@ int process_srv(struct session *t)
  */
 int produce_content(struct session *s)
 {
-       struct buffer *rep = s->rep;
-       struct proxy *px;
-       struct server *sv;
-       struct chunk msg;
-
        if (s->data_source == DATA_SRC_NONE) {
                s->flags &= ~SN_SELF_GEN;
                return 1;
        }
        else if (s->data_source == DATA_SRC_STATS) {
+               /* dump server statistics */
+               return produce_content_stats(s);
+       }
+       else {
+               /* unknown data source */
+               s->logs.status = 500;
+               client_retnclose(s, error_message(s, HTTP_ERR_500));
+               if (!(s->flags & SN_ERR_MASK))
+                       s->flags |= SN_ERR_PRXCOND;
+               if (!(s->flags & SN_FINST_MASK))
+                       s->flags |= SN_FINST_R;
+               s->flags &= ~SN_SELF_GEN;
+               return 1;
+       }
+}
+
+
+/*
+ * Produces statistics data for the session <s>. Expects to be called with
+ * s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN
+ * flag from the session, which it uses to keep on being called when there is
+ * free space in the buffer, of simply by letting an empty buffer upon return.
+ * It returns 1 if it changes the session state from CL_STSHUTR, otherwise 0.
+ */
+int produce_content_stats(struct session *s)
+{
+       struct buffer *rep = s->rep;
+       struct proxy *px;
+       struct chunk msg;
+       unsigned int up;
+
+       msg.len = 0;
+       msg.str = trash;
+
+       switch (s->data_state) {
+       case DATA_ST_INIT:
+               /* the function had not been called yet */
+               s->flags |= SN_SELF_GEN;  // more data will follow
+
+               chunk_printf(&msg, sizeof(trash),
+                            "HTTP/1.0 200 OK\r\n"
+                            "Cache-Control: no-cache\r\n"
+                            "Connection: close\r\n"
+                            "Content-Type: text/html\r\n"
+                            "\r\n");
+
+               s->logs.status = 200;
+               client_retnclose(s, &msg); // send the start of the response.
                msg.len = 0;
-               msg.str = trash;
 
-               if (s->data_state == DATA_ST_INIT) { /* the function had not been called yet */
-                       unsigned int up;
+               if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
+                       s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
+               if (!(s->flags & SN_FINST_MASK))
+                       s->flags |= SN_FINST_R;
+
+               s->data_state = DATA_ST_HEAD; /* let's start producing data */
+               /* fall through */
+
+       case DATA_ST_HEAD:
+               /* WARNING! This must fit in the first buffer !!! */        
+               chunk_printf(&msg, sizeof(trash),
+                            "<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
+                            "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
+                            "<style type=\"text/css\"><!--\n"
+                            "body {"
+                            " font-family: helvetica, arial;"
+                            " font-size: 12px;"
+                            " font-weight: normal;"
+                            " color: black;"
+                            " background: white;"
+                            "}\n"
+                            "th,td {"
+                            " font-size: 0.8em;"
+                            " align: center;"
+                            "}"
+                            "h1 {"
+                            " font-size: xx-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"
+                            ".pxname   {background: #b00040;color: #ffff40;font-weight: bold;}\n"
+                            ".titre    {background: #20D0D0;color: #000000;font-weight: bold;}\n"
+                            ".total    {background: #20D0D0;color: #ffff80;}\n"
+                            ".frontend {background: #e8e8d0;}\n"
+                            ".backend  {background: #e8e8d0;}\n"
+                            ".active0  {background: #ff9090;}\n"
+                            ".active1  {background: #ffd020;}\n"
+                            ".active2  {background: #ffffa0;}\n"
+                            ".active3  {background: #c0ffc0;}\n"
+                            ".active4  {background: #e0e0e0;}\n"
+                            ".backup0  {background: #ff9090;}\n"
+                            ".backup1  {background: #ff80ff;}\n"
+                            ".backup2  {background: #c060ff;}\n"
+                            ".backup3  {background: #b0d0ff;}\n"
+                            ".backup4  {background: #e0e0e0;}\n"
+                            "table.tbl { border-collapse: collapse; border-style: none;}\n"
+                            "table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; border-color: gray;}\n"
+                            "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
+                            "table.tbl th.empty { border-style: none; empty-cells: hide;}\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"
+                            "-->"
+                            "</style></head>");
+                       
+               if (buffer_write_chunk(rep, &msg) != 0)
+                       return 0;
 
-                       s->flags |= SN_SELF_GEN;  // more data will follow
+               s->data_state = DATA_ST_INFO;
+               /* fall through */
 
-                       /* send the start of the HTTP response */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "HTTP/1.0 200 OK\r\n"
-                                          "Cache-Control: no-cache\r\n"
-                                          "Connection: close\r\n"
-                                          "Content-Type: text/html\r\n"
-                                          "\r\n");
-           
-                       s->logs.status = 200;
-                       client_retnclose(s, &msg); // send the start of the response.
-                       msg.len = 0;
-
-                       if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
-                               s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
-                       if (!(s->flags & SN_FINST_MASK))
-                               s->flags |= SN_FINST_R;
-
-                       /* WARNING! This must fit in the first buffer !!! */        
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
-                                          "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
-                                          "<style type=\"text/css\"><!--\n"
-                                          "body {"
-                                          "  font-family: helvetica, arial;"
-                                          "  font-size: 12px;"
-                                          "  font-weight: normal;"
-                                          "  color: black;"
-                                          "  background: white;"
-                                          "}\n"
-                                          "td {"
-                                          "  font-size: 12px;"
-                                          "  align: center;"
-                                          "}\n"
-                                          "h1 {"
-                                          "  font-size: xx-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"
-                                          "table.tbl { border-collapse: collapse; border-width: 1px; border-style: solid; border-color: gray;}\n"
-                                          "table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; border-color: gray; }\n"
-                                          "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray; }\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"
-                                          "-->"
-                                          "</style></head>");
-
-                       if (buffer_write(rep, trash, msg.len) != 0)
-                               return 0;
-                       msg.len = 0;
-
-                       up = (now.tv_sec - start_date.tv_sec);
-
-                       /* WARNING! this has to fit the first packet too */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<body><h1>" PRODUCT_NAME "</h1>\n"
-                                          "<h2>Statistics Report for pid %d</h2>\n"
-                                          "<hr width=\"100%%\" class=\"hr\">\n"
-                                          "<h3>&gt; General process information</h3>\n"
-                                          "<table border=0><tr><td align=\"left\">\n"
-                                          "<p><b>pid = </b> %d (nbproc = %d)<br>\n"
-                                          "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
-                                          "<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
-                                          "<b>maxsock = </b> %d<br>\n"
-                                          "<b>maxconn = </b> %d (current conns = %d)<br>\n"
-                                          "</td><td width=\"10%%\">\n"
-                                          "</td><td align=\"right\">\n"
-                                          "<table class=\"lgd\">"
-                                          "<tr><td bgcolor=\"#C0FFC0\">&nbsp;</td><td style=\"border-style: none;\">active UP </td>"
-                                          "<td bgcolor=\"#B0D0FF\">&nbsp;</td><td style=\"border-style: none;\">backup UP </td></tr>"
-                                          "<tr><td bgcolor=\"#FFFFA0\"></td><td style=\"border-style: none;\">active UP, going down </td>"
-                                          "<td bgcolor=\"#C060FF\"></td><td style=\"border-style: none;\">backup UP, going down </td></tr>"
-                                          "<tr><td bgcolor=\"#FFD020\"></td><td style=\"border-style: none;\">active DOWN, going up </td>"
-                                          "<td bgcolor=\"#FF80FF\"></td><td style=\"border-style: none;\">backup DOWN, going up </td></tr>"
-                                          "<tr><td bgcolor=\"#FF9090\"></td><td style=\"border-style: none;\">active or backup DOWN &nbsp;</td>"
-                                          "<td bgcolor=\"#E0E0E0\"></td><td style=\"border-style: none;\">not checked </td></tr>"
-                                          "</table>\n"
-                                          "</tr></table>\n"
-                                          "",
-                                          pid, pid, global.nbproc,
-                                          up / 86400, (up % 86400) / 3600,
-                                          (up % 3600) / 60, (up % 60),
-                                          global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
-                                          global.rlimit_memmax ? " MB" : "",
-                                          global.rlimit_nofile,
-                                          global.maxsock,
-                                          global.maxconn,
-                                          actconn
-                                          );
+       case DATA_ST_INFO:
+               up = (now.tv_sec - start_date.tv_sec);
+
+               /* 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_printf(&msg, sizeof(trash),
+                            "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
+                            PRODUCT_NAME "</a></h1>\n"
+                            "<h2>Statistics Report for pid %d</h2>\n"
+                            "<hr width=\"100%%\" class=\"hr\">\n"
+                            "<h3>&gt; General process information</h3>\n"
+                            "<table border=0 cols=3><tr><td align=\"left\" nowrap width=\"1%%\">\n"
+                            "<p><b>pid = </b> %d (nbproc = %d)<br>\n"
+                            "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
+                            "<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
+                            "<b>maxsock = </b> %d<br>\n"
+                            "<b>maxconn = </b> %d (current conns = %d)<br>\n"
+                            "</td><td align=\"center\" nowrap>\n"
+                            "<table class=\"lgd\"><tr>"
+                            "<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
+                            "<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
+                            "</tr><tr>"
+                            "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
+                            "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
+                            "</tr><tr>"
+                            "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
+                            "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
+                            "</tr><tr>"
+                            "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
+                            "<td class=\"active4\"></td><td class=\"noborder\">not checked </td>"
+                            "</tr></table>\n"
+                            "</td>"
+                            "<td align=\"left\" nowrap width=\"1%%\">"
+                            "<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">"
+                            "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>"
+                            "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>"
+                            "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>"
+                            "</ul>"
+                            "</td>"
+                            "</tr></table>\n"
+                            "",
+                            pid, pid, global.nbproc,
+                            up / 86400, (up % 86400) / 3600,
+                            (up % 3600) / 60, (up % 60),
+                            global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
+                            global.rlimit_memmax ? " MB" : "",
+                            global.rlimit_nofile,
+                            global.maxsock,
+                            global.maxconn,
+                            actconn
+                            );
            
-                       if (buffer_write(rep, trash, msg.len) != 0)
-                               return 0;
-                       msg.len = 0;
+               if (buffer_write_chunk(rep, &msg) != 0)
+                       return 0;
 
-                       s->data_state = DATA_ST_DATA;
-                       memset(&s->data_ctx, 0, sizeof(s->data_ctx));
+               memset(&s->data_ctx, 0, sizeof(s->data_ctx));
 
-                       px = s->data_ctx.stats.px = proxy;
-                       s->data_ctx.stats.px_st = DATA_ST_INIT;
-               }
+               s->data_ctx.stats.px = proxy;
+               s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
+               s->data_state = DATA_ST_LIST;
+               /* fall through */
 
+       case DATA_ST_LIST:
+               /* dump proxies */
                while (s->data_ctx.stats.px) {
-                       int dispatch_sess, dispatch_cum;
-                       int failed_checks, down_trans;
-                       int failed_secu, failed_conns, failed_resp;
-
-                       if (s->data_ctx.stats.px_st == DATA_ST_INIT) {
-                               /* we are on a new proxy */
-                               px = s->data_ctx.stats.px;
+                       px = s->data_ctx.stats.px;
+                       /* skip the disabled proxies and non-networked ones */
+                       if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
+                               if (produce_content_stats_proxy(s, px) == 0)
+                                       return 0;
 
-                               /* skip the disabled proxies */
-                               if (px->state == PR_STSTOPPED)
-                                       goto next_proxy;
+                       s->data_ctx.stats.px = px->next;
+                       s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
+               }
+               /* here, we just have reached the last proxy */
 
-                               if (s->be->fiprm->uri_auth && s->be->fiprm->uri_auth->scope) {
-                                       /* we have a limited scope, we have to check the proxy name */
-                                       struct stat_scope *scope;
-                                       int len;
+               s->data_state = DATA_ST_END;
+               /* fall through */
 
-                                       len = strlen(px->id);
-                                       scope = s->be->fiprm->uri_auth->scope;
+       case DATA_ST_END:
+               chunk_printf(&msg, sizeof(trash), "</body></html>");
+               if (buffer_write_chunk(rep, &msg) != 0)
+                       return 0;
 
-                                       while (scope) {
-                                               /* match exact proxy name */
-                                               if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
-                                                       break;
+               s->data_state = DATA_ST_FIN;
+               /* fall through */
 
-                                               /* match '.' which means 'self' proxy */
-                                               if (!strcmp(scope->px_id, ".") && px == s->fe)
-                                                       break;
-                                               scope = scope->next;
-                                       }
+       case DATA_ST_FIN:
+               s->flags &= ~SN_SELF_GEN;
+               return 1;
 
-                                       /* proxy name not found */
-                                       if (scope == NULL)
-                                               goto next_proxy;
-                               }
+       default:
+               /* unknown state ! */
+               s->logs.status = 500;
+               client_retnclose(s, error_message(s, HTTP_ERR_500));
+               if (!(s->flags & SN_ERR_MASK))
+                       s->flags |= SN_ERR_PRXCOND;
+               if (!(s->flags & SN_FINST_MASK))
+                       s->flags |= SN_FINST_R;
+               s->flags &= ~SN_SELF_GEN;
+               return 1;
+       }
+}
 
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                  "<h3>&gt; Proxy instance %s : "
-                                                  "%d front conns (max=%d), %d back, "
-                                                  "%d queued (%d unassigned), %d total front conns, %d back</h3>\n"
-                                                  "",
-                                                  px->id,
-                                                  px->feconn, px->maxconn, px->beconn,
-                                                  px->totpend, px->nbpend, px->cum_feconn, px->cum_beconn);
-               
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                  "<table cols=\"16\" class=\"tbl\">\n"
-                                                  "<tr align=\"center\" bgcolor=\"#20C0C0\">"
-                                                  "<th colspan=5>Server</th>"
-                                                  "<th colspan=2>Queue</th>"
-                                                  "<th colspan=4>Sessions</th>"
-                                                  "<th colspan=5>Errors</th></tr>\n"
-                                                  "<tr align=\"center\" bgcolor=\"#20C0C0\">"
-                                                  "<th>Name</th><th>Weight</th><th>Status</th><th>Act.</th><th>Bck.</th>"
-                                                  "<th>Curr.</th><th>Max.</th>"
-                                                  "<th>Curr.</th><th>Max.</th><th>Limit</th><th>Cumul.</th>"
-                                                  "<th>Conn.</th><th>Resp.</th><th>Sec.</th><th>Check</th><th>Down</th></tr>\n");
-               
-                               if (buffer_write(rep, trash, msg.len) != 0)
-                                       return 0;
-                               msg.len = 0;
 
-                               s->data_ctx.stats.sv = px->srv;
-                               s->data_ctx.stats.px_st = DATA_ST_DATA;
-                       }
+/*
+ * Dumps statistics for a proxy.
+ * Returns 0 if it had to stop dumping data because of lack of buffer space,
+ * ot non-zero if everything completed.
+ */
+int produce_content_stats_proxy(struct session *s, struct proxy *px)
+{
+       struct buffer *rep = s->rep;
+       struct server *sv;
+       struct chunk msg;
 
-                       px = s->data_ctx.stats.px;
+       msg.len = 0;
+       msg.str = trash;
 
-                       /* stats.sv has been initialized above */
-                       while (s->data_ctx.stats.sv != NULL) {
-                               static char *act_tab_bg[5] = { /*down*/"#FF9090", /*rising*/"#FFD020", /*failing*/"#FFFFA0", /*up*/"#C0FFC0", /*unchecked*/"#E0E0E0" };
-                               static char *bck_tab_bg[5] = { /*down*/"#FF9090", /*rising*/"#FF80ff", /*failing*/"#C060FF", /*up*/"#B0D0FF", /*unchecked*/"#E0E0E0" };
-                               static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d &uarr;", "UP %d/%d &darr;", "UP", "<i>no check</i>" };
-                               int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP */
-
-                               sv = s->data_ctx.stats.sv;
-
-                               /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
-                               if (!(sv->state & SRV_CHECKED))
-                                       sv_state = 4;
-                               else if (sv->state & SRV_RUNNING)
-                                       if (sv->health == sv->rise + sv->fall - 1)
-                                               sv_state = 3; /* UP */
-                                       else
-                                               sv_state = 2; /* going down */
-                               else
-                                       if (sv->health)
-                                               sv_state = 1; /* going up */
-                                       else
-                                               sv_state = 0; /* DOWN */
-
-                               /* name, weight */
-                               msg.len += snprintf(trash, sizeof(trash),
-                                                  "<tr align=center bgcolor=\"%s\"><td>%s</td><td>%d</td><td>",
-                                                  (sv->state & SRV_BACKUP) ? bck_tab_bg[sv_state] : act_tab_bg[sv_state],
-                                                  sv->id, sv->uweight+1);
-                               /* status */
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, srv_hlt_st[sv_state],
-                                                  (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
-                                                  (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
-
-                               /* act, bck */
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                  "</td><td>%s</td><td>%s</td>",
-                                                  (sv->state & SRV_BACKUP) ? "-" : "Y",
-                                                  (sv->state & SRV_BACKUP) ? "Y" : "-");
-
-                               /* queue : current, max */
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                  "<td align=right>%d</td><td align=right>%d</td>",
-                                                  sv->nbpend, sv->nbpend_max);
-
-                               /* sessions : current, max, limit, cumul */
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                  "<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>",
-                                                  sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess);
-
-                               /* errors : connect, response, security */
-                               msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                  "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>\n",
-                                                  sv->failed_conns, sv->failed_resp, sv->failed_secu);
-
-                               /* check failures : unique, fatal */
-                               if (sv->state & SRV_CHECKED)
-                                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                          "<td align=right>%d</td><td align=right>%d</td></tr>\n",
-                                                          sv->failed_checks, sv->down_trans);
-                               else
-                                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                                          "<td align=right>-</td><td align=right>-</td></tr>\n");
+       switch (s->data_ctx.stats.px_st) {
+       case DATA_ST_PX_INIT:
+               /* we are on a new proxy */
 
-                               if (buffer_write(rep, trash, msg.len) != 0)
-                                       return 0;
-                               msg.len = 0;
+               if (s->be->fiprm->uri_auth && s->be->fiprm->uri_auth->scope) {
+                       /* we have a limited scope, we have to check the proxy name */
+                       struct stat_scope *scope;
+                       int len;
 
-                               s->data_ctx.stats.sv = sv->next;
-                       } /* while sv */
+                       len = strlen(px->id);
+                       scope = s->be->fiprm->uri_auth->scope;
 
-                       /* now we are past the last server, we'll dump information about the dispatcher */
+                       while (scope) {
+                               /* match exact proxy name */
+                               if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
+                                       break;
 
-                       /* We have to count down from the proxy to the servers to tell how
-                        * many sessions are on the dispatcher, and how many checks have
-                        * failed. We cannot count this during the servers dump because it
-                        * might be interrupted multiple times.
-                        */
-                       dispatch_sess = px->beconn;
-                       dispatch_cum  = px->cum_beconn;
-                       failed_secu   = px->failed_secu;
-                       failed_conns  = px->failed_conns;
-                       failed_resp   = px->failed_resp;
-                       failed_checks = down_trans = 0;
-
-                       sv = px->srv;
-                       while (sv) {
-                               dispatch_sess -= sv->cur_sess;
-                               dispatch_cum  -= sv->cum_sess;
-                               failed_conns  -= sv->failed_conns;
-                               failed_resp   -= sv->failed_resp;
-                               failed_secu   -= sv->failed_secu;
-                               if (sv->state & SRV_CHECKED) {
-                                       failed_checks += sv->failed_checks;
-                                       down_trans    += sv->down_trans;
-                               }
-                               sv = sv->next;
+                               /* match '.' which means 'self' proxy */
+                               if (!strcmp(scope->px_id, ".") && px == s->fe)
+                                       break;
+                               scope = scope->next;
                        }
 
-                       /* name, weight, status, act, bck */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash),
-                                          "<tr align=center bgcolor=\"#e8e8d0\">"
-                                          "<td>Dispatcher</td><td>-</td>"
-                                          "<td>%s</td><td>-</td><td>-</td>",
-                                          px->state == PR_STRUN ? "UP" : "DOWN");
+                       /* proxy name not found : don't dump anything */
+                       if (scope == NULL)
+                               return 1;
+               }
 
-                       /* queue : current, max */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right>%d</td><td align=right>%d</td>",
-                                          px->nbpend, px->nbpend_max);
+               s->data_ctx.stats.px_st = DATA_ST_PX_TH;
+               /* fall through */
+
+       case DATA_ST_PX_TH:
+               /* print a new table */
+               chunk_printf(&msg, sizeof(trash),
+                            "<table cols=\"20\" class=\"tbl\" width=\"100%%\">\n"
+                            "<tr align=\"center\" class=\"titre\">"
+                            "<th colspan=2 class=\"pxname\">%s</th>"
+                            "<th colspan=18 class=\"empty\"></th>"
+                            "</tr>\n"
+                            "<tr align=\"center\" class=\"titre\">"
+                            "<th rowspan=2></th>"
+                            "<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
+                            "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
+                            "<th colspan=3>Errors</th><th colspan=6>Server</th>"
+                            "</tr>\n"
+                            "<tr align=\"center\" class=\"titre\">"
+                            "<th>Curr.</th><th>Max.</th><th>Curr.</th><th>Max.</th>"
+                            "<th>Limit</th><th>Cumul.</th><th>In</th><th>Out</th>"
+                            "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
+                            "<th>Resp</th><th>Status</th><th>Weight</th><th>Act</th>"
+                            "<th>Bck</th><th>Check</th><th>Down</th></tr>\n"
+                            "",
+                            px->id);
+               
+               if (buffer_write_chunk(rep, &msg) != 0)
+                       return 0;
+
+               s->data_ctx.stats.px_st = DATA_ST_PX_FE;
+               /* fall through */
+
+       case DATA_ST_PX_FE:
+               /* print the frontend */
+               if (px->cap & PR_CAP_FE) {
+                       /* name, queue */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>");
 
                        /* sessions : current, max, limit, cumul. */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right>%d</td><td align=right>%d</td><td align=right>-</td><td align=right>%d</td>",
-                                          dispatch_sess, px->beconn_max, dispatch_cum);
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>",
+                                    px->feconn, px->feconn_max, px->maxconn, px->cum_feconn);
 
-                       /* errors : connect, response, security */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>\n",
-                                          failed_conns, failed_resp, failed_secu);
+                       /* bytes : in, out */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right></td><td align=right></td>");
 
-                       /* check failures : unique, fatal */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right>-</td><td align=right>-</td></tr>\n");
+                       /* denied: req, resp */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td>",
+                                    px->denied_req, px->denied_resp);
 
+                       /* errors : request, connect, response */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right></td><td align=right></td>",
+                                    px->failed_req);
 
-                       /* now the summary for the whole proxy */
-                       /* name, weight, status, act, bck */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash),
-                                          "<tr align=center style=\"color: #ffff80;  background: #20C0C0;\">"
-                                          "<td><b>Total</b></td><td>-</td>"
-                                          "<td><b>%s</b></td><td><b>%d</b></td><td><b>%d</b></td>",
-                                          (px->state == PR_STRUN && ((px->srv == NULL) || px->srv_act || px->srv_bck)) ? "UP" : "DOWN",
-                                          px->srv_act, px->srv_bck);
+                       /* server status : reflect backend status */
+                       chunk_printf(&msg, sizeof(trash), "<td align=center>%s</td>",
+                                    px->state == PR_STRUN ? "OPEN" :
+                                    px->state == PR_STIDLE ? "FULL" : "STOP");
+
+                       /* rest of server: nothing */
+                       chunk_printf(&msg, sizeof(trash), "<td align=center colspan=5></td></tr>");
+
+                       if (buffer_write_chunk(rep, &msg) != 0)
+                               return 0;
+               }
+
+               s->data_ctx.stats.sv = px->srv; /* may be NULL */
+               s->data_ctx.stats.px_st = DATA_ST_PX_SV;
+               /* fall through */
+
+       case DATA_ST_PX_SV:
+               /* stats.sv has been initialized above */
+               while (s->data_ctx.stats.sv != NULL) {
+                       static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d &uarr;", "UP %d/%d &darr;", "UP", "<i>no check</i>" };
+                       int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4=unchecked */
+
+                       sv = s->data_ctx.stats.sv;
+
+                       /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
+                       if (!(sv->state & SRV_CHECKED))
+                               sv_state = 4;
+                       else if (sv->state & SRV_RUNNING)
+                               if (sv->health == sv->rise + sv->fall - 1)
+                                       sv_state = 3; /* UP */
+                               else
+                                       sv_state = 2; /* going down */
+                       else
+                               if (sv->health)
+                                       sv_state = 1; /* going up */
+                               else
+                                       sv_state = 0; /* DOWN */
+
+                       /* name */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<tr align=\"center\" class=\"%s%d\"><td>%s</td>",
+                                    (sv->state & SRV_BACKUP) ? "active" : "backup",
+                                    sv_state, sv->id);
 
                        /* queue : current, max */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right><b>%d</b></td><td align=right><b>%d</b></td>",
-                                          px->totpend, px->nbpend_max);
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td>",
+                                    sv->nbpend, sv->nbpend_max);
 
                        /* sessions : current, max, limit, cumul */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right><b>%d</b></td><td align=right><b>%d</b></td><td align=right><b>-</b></td><td align=right><b>%d</b></td>",
-                                          px->beconn, px->beconn_max, px->cum_beconn);
-
-                       /* errors : connect, response, security */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>\n",
-                                          px->failed_conns, px->failed_resp, px->failed_secu);
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>",
+                                    sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess);
+
+                       /* bytes : in, out */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right></td><td align=right></td>");
+
+                       /* denied: req, resp */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right></td><td align=right>%d</td>",
+                                    sv->failed_secu);
+
+                       /* errors : request, connect, response */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n",
+                                    sv->failed_conns, sv->failed_resp);
+
+                       /* status */
+                       chunk_printf(&msg, sizeof(trash), "<td nowrap>");
+                       chunk_printf(&msg, sizeof(trash),
+                                    srv_hlt_st[sv_state],
+                                    (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
+                                    (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
+
+                       /* weight */
+                       chunk_printf(&msg, sizeof(trash), "<td>%d</td>", sv->uweight+1);
+
+                       /* act, bck */
+                       chunk_printf(&msg, sizeof(trash), "<td>%s</td><td>%s</td>",
+                                    (sv->state & SRV_BACKUP) ? "-" : "Y",
+                                    (sv->state & SRV_BACKUP) ? "Y" : "-");
 
                        /* check failures : unique, fatal */
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len,
-                                          "<td align=right>%d</td><td align=right>%d</td></tr>\n",
-                                          failed_checks, down_trans);
+                       if (sv->state & SRV_CHECKED)
+                               chunk_printf(&msg, sizeof(trash),
+                                            "<td align=right>%d</td><td align=right>%d</td></tr>\n",
+                                            sv->failed_checks, sv->down_trans);
+                       else
+                               chunk_printf(&msg, sizeof(trash),
+                                            "<td colspan=2></td></tr>\n");
+
+                       if (buffer_write_chunk(rep, &msg) != 0)
+                               return 0;
 
-                       msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "</table><p>\n");
+                       s->data_ctx.stats.sv = sv->next;
+               } /* while sv */
 
-                       if (buffer_write(rep, trash, msg.len) != 0)
+               s->data_ctx.stats.px_st = DATA_ST_PX_BE;
+               /* fall through */
+
+       case DATA_ST_PX_BE:
+               /* print the backend */
+               if (px->cap & PR_CAP_BE) {
+                       /* name */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<tr align=center class=\"backend\"><td>Backend</td>");
+
+                       /* queue : current, max */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td>",
+                                    px->nbpend /* or px->totpend ? */, px->nbpend_max);
+
+                       /* sessions : current, max, limit, cumul. */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>",
+                                    px->beconn, px->beconn_max, px->fullconn, px->cum_beconn);
+
+                       /* bytes : in, out */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right></td><td align=right></td>");
+
+                       /* denied: req, resp */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right>%d</td><td align=right>%d</td>",
+                                    px->denied_req, px->denied_resp);
+
+                       /* errors : request, connect, response */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n",
+                                    px->failed_conns, px->failed_resp);
+
+                       /* server 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). Tthen we display the total weight, number of
+                        * active and backups. */
+                       chunk_printf(&msg, sizeof(trash),
+                                    "<td align=center>%s</td>"
+                                    "<td align=center>%d</td>"
+                                    "<td align=center>%d</td><td align=center>%d</td>",
+                                    (px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN",
+                                    px->srv_map_sz, px->srv_act, px->srv_bck);
+
+                       /* rest of server: nothing */
+                       chunk_printf(&msg, sizeof(trash), "<td align=center colspan=2></td></tr>");
+
+                       if (buffer_write_chunk(rep, &msg) != 0)
                                return 0;
-                       msg.len = 0;
-           
-                       s->data_ctx.stats.px_st = DATA_ST_INIT;
-               next_proxy:
-                       s->data_ctx.stats.px = px->next;
-               } /* proxy loop */
-               /* here, we just have reached the sv == NULL and px == NULL */
-               s->flags &= ~SN_SELF_GEN;
+               }
+               
+               s->data_ctx.stats.px_st = DATA_ST_PX_END;
+               /* fall through */
+
+       case DATA_ST_PX_END:
+               chunk_printf(&msg, sizeof(trash), "</table><p>\n");
+
+               if (buffer_write_chunk(rep, &msg) != 0)
+                       return 0;
+
+               s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
+               /* fall through */
+
+       case DATA_ST_PX_FIN:
                return 1;
-       }
-       else {
-               /* unknown data source */
-               s->logs.status = 500;
-               client_retnclose(s, error_message(s, HTTP_ERR_500));
-               if (!(s->flags & SN_ERR_MASK))
-                       s->flags |= SN_ERR_PRXCOND;
-               if (!(s->flags & SN_FINST_MASK))
-                       s->flags |= SN_FINST_R;
-               s->flags &= SN_SELF_GEN;
+
+       default:
+               /* unknown state, we should put an abort() here ! */
                return 1;
        }
 }
@@ -3243,12 +3371,14 @@ void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_
                                                t->flags |= SN_CLDENY;
                                                abort_filt = 1;
                                        }
+                                       t->be->beprm->denied_req++;
                                        break;
                                case ACT_TARPIT:
                                        if (!(t->flags & (SN_CLALLOW | SN_CLDENY))) {
                                                t->flags |= SN_CLTARPIT;
                                                abort_filt = 1;
                                        }
+                                       t->be->beprm->denied_req++;
                                        break;
                                        //case ACT_PASS: /* FIXME: broken as of now. We should mark the header as "ignored". */
                                        //      break;