]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stats: reimplement HTTP keep-alive on the stats page
authorWilly Tarreau <w@1wt.eu>
Tue, 22 Apr 2014 20:19:53 +0000 (22:19 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 24 Apr 2014 15:24:56 +0000 (17:24 +0200)
This basically reimplements commit f3221f9 ("MEDIUM: stats: add support
for HTTP keep-alive on the stats page") which was reverted by commit
51437d2 after Igor Chan reported a broken stats page caused by the bug
fix by previous commit.

include/proto/dumpstats.h
src/dumpstats.c
src/proto_http.c

index 78ff6f76cfb47c5d4a7ffe980918e0c7f0c8137f..61e39bf9d7b0be10f182641868309635243f8ed0 100644 (file)
@@ -31,6 +31,7 @@
 #define STAT_HIDE_DOWN  0x00000008     /* hide 'down' servers in the stats page */
 #define STAT_NO_REFRESH 0x00000010     /* do not automatically refresh the stats page */
 #define STAT_ADMIN      0x00000020     /* indicate a stats admin level */
+#define STAT_CHUNKED    0x00000040      /* use chunked encoding (HTTP/1.1) */
 #define STAT_BOUND      0x00800000     /* bound statistics to selected proxies/types/services */
 
 #define STATS_TYPE_FE  0
index d20682ee8ed205fb00312572c9d07ed2a0633e38..b35a5a85d3b65947b64e1f284f91e9e5b888a45a 100644 (file)
@@ -4357,17 +4357,23 @@ static int stats_send_http_headers(struct stream_interface *si)
        struct appctx *appctx = objt_appctx(si->end);
 
        chunk_printf(&trash,
-                    "HTTP/1.0 200 OK\r\n"
+                    "HTTP/1.%c 200 OK\r\n"
                     "Cache-Control: no-cache\r\n"
                     "Connection: close\r\n"
                     "Content-Type: %s\r\n",
+                    (appctx->ctx.stats.flags & STAT_CHUNKED) ? '1' : '0',
                     (appctx->ctx.stats.flags & STAT_FMT_HTML) ? "text/html" : "text/plain");
 
        if (uri->refresh > 0 && !(appctx->ctx.stats.flags & STAT_NO_REFRESH))
                chunk_appendf(&trash, "Refresh: %d\r\n",
                              uri->refresh);
 
-       chunk_appendf(&trash, "\r\n");
+       /* we don't send the CRLF in chunked mode, it will be sent with the first chunk's size */
+
+       if (appctx->ctx.stats.flags & STAT_CHUNKED)
+               chunk_appendf(&trash, "Transfer-Encoding: chunked\r\n");
+       else
+               chunk_appendf(&trash, "\r\n");
 
        s->txn.status = 200;
        s->logs.tv_request = now;
@@ -4453,8 +4459,53 @@ static void http_stats_io_handler(struct stream_interface *si)
        }
 
        if (appctx->st0 == STAT_HTTP_DUMP) {
+               unsigned int prev_len = si->ib->buf->i;
+               unsigned int data_len;
+               unsigned int last_len;
+               unsigned int last_fwd;
+
+               if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+                       /* One difficulty we're facing is that we must prevent
+                        * the input data from being automatically forwarded to
+                        * the output area. For this, we temporarily disable
+                        * forwarding on the channel.
+                        */
+                       last_fwd = si->ib->to_forward;
+                       si->ib->to_forward = 0;
+                       chunk_printf(&trash, "\r\n000000\r\n");
+                       if (bi_putchk(si->ib, &trash) == -1) {
+                               si->ib->to_forward = last_fwd;
+                               goto fail;
+                       }
+               }
+
+               data_len = si->ib->buf->i;
                if (stats_dump_stat_to_buffer(si, s->be->uri_auth))
                        appctx->st0 = STAT_HTTP_DONE;
+
+               last_len = si->ib->buf->i;
+
+               /* Now we must either adjust or remove the chunk size. This is
+                * not easy because the chunk size might wrap at the end of the
+                * buffer, so we pretend we have nothing in the buffer, we write
+                * the size, then restore the buffer's contents. Note that we can
+                * only do that because no forwarding is scheduled on the stats
+                * applet.
+                */
+               if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+                       si->ib->total  -= (last_len - prev_len);
+                       si->ib->buf->i -= (last_len - prev_len);
+
+                       if (last_len != data_len) {
+                               chunk_printf(&trash, "\r\n%06x\r\n", (last_len - data_len));
+                               bi_putchk(si->ib, &trash);
+
+                               si->ib->total  += (last_len - data_len);
+                               si->ib->buf->i += (last_len - data_len);
+                       }
+                       /* now re-enable forwarding */
+                       channel_forward(si->ib, last_fwd);
+               }
        }
 
        if (appctx->st0 == STAT_HTTP_POST) {
@@ -4469,8 +4520,17 @@ static void http_stats_io_handler(struct stream_interface *si)
                        appctx->st0 = STAT_HTTP_DONE;
        }
 
-       if (appctx->st0 == STAT_HTTP_DONE)
-               si_shutw(si);
+       if (appctx->st0 == STAT_HTTP_DONE) {
+               if (appctx->ctx.stats.flags & STAT_CHUNKED) {
+                       chunk_printf(&trash, "\r\n0\r\n\r\n");
+                       if (bi_putchk(si->ib, &trash) == -1)
+                               goto fail;
+               }
+               /* eat the whole request */
+               bo_skip(si->ob, si->ob->buf->o);
+               res->flags |= CF_READ_NULL;
+               si_shutr(si);
+       }
 
        if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
                si_shutw(si);
@@ -4482,6 +4542,7 @@ static void http_stats_io_handler(struct stream_interface *si)
                }
        }
 
+ fail:
        /* update all other flags and resync with the other side */
        si_update(si);
 
index 5e14d58f6e9be625f7775a5b843b06c291498822..f813e1cf6bd6340a20c2a489152de98f1ce893b7 100644 (file)
@@ -2962,6 +2962,8 @@ int http_handle_stats(struct session *s, struct channel *req)
        appctx->st1 = appctx->st2 = 0;
        appctx->ctx.stats.st_code = STAT_STATUS_INIT;
        appctx->ctx.stats.flags |= STAT_FMT_HTML; /* assume HTML mode by default */
+       if ((msg->flags & HTTP_MSGF_VER_11) && (s->txn.meth != HTTP_METH_HEAD))
+               appctx->ctx.stats.flags |= STAT_CHUNKED;
 
        uri = msg->chn->buf->p + msg->sl.rq.u;
        lookup = uri + uri_auth->uri_len;
@@ -3798,7 +3800,7 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
                        s->flags |= SN_FINST_R;
 
                req->analyse_exp = TICK_ETERNITY;
-               req->analysers = 0;
+               req->analysers = AN_REQ_HTTP_XFER_BODY;
                return 1;
        }