MEDIUM: stats: reimplement HTTP keep-alive on the stats page

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.
This commit is contained in:
Willy Tarreau 2014-04-22 22:19:53 +02:00
parent 5a8ba60fe1
commit af3cf70d7c
3 changed files with 69 additions and 5 deletions

View 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

View 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);

View 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;
}