REORG: stats: move the HTTP header injection to proto_http

The HTTP header injection that are performed in dumpstats when responding
or when redirecting a POST request have nothing to do in dumpstats. They
do not use any state from the stats, and are 100% HTTP. Let's make the
headers there in the HTTP core, and have dumpstats only produce stats.
This commit is contained in:
Willy Tarreau 2012-12-22 22:03:39 +01:00
parent d9bdcd5139
commit 1facd6d67e
2 changed files with 145 additions and 150 deletions

View File

@ -87,8 +87,7 @@ static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri);
-> stats_dump_px_end()
http_stats_io_handler()
-> stats_http_redir()
-> stats_dump_http() // also emits the HTTP headers
-> stats_dump_http()
-> stats_dump_html_head() // emits the HTML headers
-> stats_dump_csv_header() // emits the CSV headers (same as above)
-> stats_dump_http_info() // note: ignores non-HTML output
@ -3311,7 +3310,6 @@ static int stats_dump_http_end(struct stream_interface *si, struct proxy *px, st
*/
static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri)
{
struct session *s = si->conn->xprt_ctx;
struct channel *rep = si->ib;
struct proxy *px;
@ -3319,34 +3317,6 @@ static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri)
switch (si->conn->xprt_st) {
case STAT_ST_INIT:
chunk_appendf(&trash,
"HTTP/1.0 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: %s\r\n",
(si->applet.ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
if (uri->refresh > 0 && !(si->applet.ctx.stats.flags & STAT_NO_REFRESH))
chunk_appendf(&trash, "Refresh: %d\r\n",
uri->refresh);
chunk_appendf(&trash, "\r\n");
s->txn.status = 200;
if (bi_putchk(rep, &trash) == -1)
return 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;
if (s->txn.meth == HTTP_METH_HEAD) {
/* that's all we return in case of HEAD request */
si->conn->xprt_st = STAT_ST_FIN;
return 1;
}
si->conn->xprt_st = STAT_ST_HEAD; /* let's start producing data */
/* fall through */
@ -3412,48 +3382,6 @@ static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri)
}
}
/* 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.
*/
static int stats_http_redir(struct stream_interface *si, struct uri_auth *uri)
{
struct session *s = si->conn->xprt_ctx;
chunk_reset(&trash);
switch (si->conn->xprt_st) {
case STAT_ST_INIT:
chunk_appendf(&trash,
"HTTP/1.0 303 See Other\r\n"
"Cache-Control: no-cache\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n"
"Location: %s;st=%s",
uri->uri_prefix,
((si->applet.ctx.stats.st_code > STAT_STATUS_INIT) &&
(si->applet.ctx.stats.st_code < STAT_STATUS_SIZE) &&
stat_status_codes[si->applet.ctx.stats.st_code]) ?
stat_status_codes[si->applet.ctx.stats.st_code] :
stat_status_codes[STAT_STATUS_UNKN]);
chunk_appendf(&trash, "\r\n\r\n");
if (bi_putchk(si->ib, &trash) == -1)
return 0;
s->txn.status = 303;
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;
si->conn->xprt_st = STAT_ST_FIN;
return 1;
}
return 1;
}
/* This I/O handler runs as an applet embedded in a stream interface. It is
* used to send HTTP stats over a TCP socket. The mechanism is very simple.
* si->applet.st0 becomes non-zero once the transfer is finished. The handler
@ -3473,16 +3401,9 @@ static void http_stats_io_handler(struct stream_interface *si)
si->applet.st0 = 1;
if (!si->applet.st0) {
if (s->txn.meth == HTTP_METH_POST) {
if (stats_http_redir(si, s->be->uri_auth)) {
si->applet.st0 = 1;
si_shutw(si);
}
} else {
if (stats_dump_http(si, s->be->uri_auth)) {
si->applet.st0 = 1;
si_shutw(si);
}
if (stats_dump_http(si, s->be->uri_auth)) {
si->applet.st0 = 1;
si_shutw(si);
}
}

View File

@ -2927,6 +2927,140 @@ int http_process_req_stat_post(struct stream_interface *si, struct http_txn *txn
return 1;
}
/* This function checks whether we need to enable a POST analyser to parse a
* stats request, and also registers the stats I/O handler. It returns zero
* if it needs to come back again, otherwise non-zero if it finishes.
*/
int http_handle_stats(struct session *s, struct channel *req)
{
struct stats_admin_rule *stats_admin_rule;
struct stream_interface *si = s->rep->prod;
struct http_txn *txn = &s->txn;
struct http_msg *msg = &txn->req;
struct uri_auth *uri = s->be->uri_auth;
/* now check whether we have some admin rules for this request */
list_for_each_entry(stats_admin_rule, &s->be->uri_auth->admin_rules, list) {
int ret = 1;
if (stats_admin_rule->cond) {
ret = acl_exec_cond(stats_admin_rule->cond, s->be, s, &s->txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
ret = acl_pass(ret);
if (stats_admin_rule->cond->pol == ACL_COND_UNLESS)
ret = !ret;
}
if (ret) {
/* no rule, or the rule matches */
s->rep->prod->applet.ctx.stats.flags |= STAT_ADMIN;
break;
}
}
/* Was the status page requested with a POST ? */
if (unlikely(txn->meth == HTTP_METH_POST)) {
if (si->applet.ctx.stats.flags & STAT_ADMIN) {
if (msg->msg_state < HTTP_MSG_100_SENT) {
/* If we have HTTP/1.1 and Expect: 100-continue, then we must
* send an HTTP/1.1 100 Continue intermediate response.
*/
if (msg->flags & HTTP_MSGF_VER_11) {
struct hdr_ctx ctx;
ctx.idx = 0;
/* Expect is allowed in 1.1, look for it */
if (http_find_header2("Expect", 6, req->buf->p, &txn->hdr_idx, &ctx) &&
unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0)) {
bo_inject(s->rep, http_100_chunk.str, http_100_chunk.len);
}
}
msg->msg_state = HTTP_MSG_100_SENT;
s->logs.tv_request = now; /* update the request timer to reflect full request */
}
if (!http_process_req_stat_post(si, txn, req))
return 0; /* we need more data */
}
else
si->applet.ctx.stats.st_code = STAT_STATUS_DENY;
/* 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,
"HTTP/1.0 303 See Other\r\n"
"Cache-Control: no-cache\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n"
"Location: %s;st=%s\r\n"
"\r\n",
uri->uri_prefix,
((si->applet.ctx.stats.st_code > STAT_STATUS_INIT) &&
(si->applet.ctx.stats.st_code < STAT_STATUS_SIZE) &&
stat_status_codes[si->applet.ctx.stats.st_code]) ?
stat_status_codes[si->applet.ctx.stats.st_code] :
stat_status_codes[STAT_STATUS_UNKN]);
s->txn.status = 303;
s->logs.tv_request = now;
stream_int_retnclose(req->prod, &trash);
s->target = &http_stats_applet.obj_type; /* just for logging the applet name */
if (s->fe == s->be) /* report it if the request was intercepted by the frontend */
s->fe->fe_counters.intercepted_req++;
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;
return 1;
}
/* OK, let's go on now */
chunk_printf(&trash,
"HTTP/1.0 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: %s\r\n",
(si->applet.ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
if (uri->refresh > 0 && !(si->applet.ctx.stats.flags & STAT_NO_REFRESH))
chunk_appendf(&trash, "Refresh: %d\r\n",
uri->refresh);
chunk_appendf(&trash, "\r\n");
s->txn.status = 200;
s->logs.tv_request = now;
if (s->fe == s->be) /* report it if the request was intercepted by the frontend */
s->fe->fe_counters.intercepted_req++;
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;
if (s->txn.meth == HTTP_METH_HEAD) {
/* that's all we return in case of HEAD request, so let's immediately close. */
stream_int_retnclose(req->prod, &trash);
s->target = &http_stats_applet.obj_type; /* just for logging the applet name */
return 1;
}
/* OK, push the response and hand over to the stats I/O handler */
bi_putchk(s->rep, &trash);
s->task->nice = -32; /* small boost for HTTP statistics */
stream_int_register_handler(s->rep->prod, &http_stats_applet);
s->target = s->rep->prod->conn->target; // for logging only
s->rep->prod->conn->xprt_ctx = s;
s->rep->prod->applet.st0 = s->rep->prod->applet.st1 = 0;
req->analysers = 0;
return 1;
}
/* returns a pointer to the first rule which forbids access (deny or http_auth),
* or NULL if everything's OK.
*/
@ -3165,74 +3299,14 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
goto return_bad_req;
}
if (do_stats) {
struct stats_admin_rule *stats_admin_rule;
/* We need to provide stats for this request.
* FIXME!!! that one is rather dangerous, we want to
* make it follow standard rules (eg: clear req->analysers).
*/
/* now check whether we have some admin rules for this request */
list_for_each_entry(stats_admin_rule, &s->be->uri_auth->admin_rules, list) {
int ret = 1;
if (stats_admin_rule->cond) {
ret = acl_exec_cond(stats_admin_rule->cond, s->be, s, &s->txn, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
ret = acl_pass(ret);
if (stats_admin_rule->cond->pol == ACL_COND_UNLESS)
ret = !ret;
}
if (ret) {
/* no rule, or the rule matches */
s->rep->prod->applet.ctx.stats.flags |= STAT_ADMIN;
break;
}
if (unlikely(do_stats)) {
/* process the stats request now */
if (!http_handle_stats(s, req)) {
/* we need more data, let's come back here later */
req->analysers |= an_bit;
channel_dont_connect(req);
}
/* Was the status page requested with a POST ? */
if (txn->meth == HTTP_METH_POST) {
if (s->rep->prod->applet.ctx.stats.flags & STAT_ADMIN) {
if (msg->msg_state < HTTP_MSG_100_SENT) {
/* If we have HTTP/1.1 and Expect: 100-continue, then we must
* send an HTTP/1.1 100 Continue intermediate response.
*/
if (msg->flags & HTTP_MSGF_VER_11) {
struct hdr_ctx ctx;
ctx.idx = 0;
/* Expect is allowed in 1.1, look for it */
if (http_find_header2("Expect", 6, req->buf->p, &txn->hdr_idx, &ctx) &&
unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0)) {
bo_inject(s->rep, http_100_chunk.str, http_100_chunk.len);
}
}
msg->msg_state = HTTP_MSG_100_SENT;
s->logs.tv_request = now; /* update the request timer to reflect full request */
}
if (!http_process_req_stat_post(s->rep->prod, txn, req)) {
/* we need more data */
req->analysers |= an_bit;
channel_dont_connect(req);
return 0;
}
} else {
s->rep->prod->applet.ctx.stats.st_code = STAT_STATUS_DENY;
}
}
s->logs.tv_request = now;
s->task->nice = -32; /* small boost for HTTP statistics */
stream_int_register_handler(s->rep->prod, &http_stats_applet);
s->target = s->rep->prod->conn->target; // for logging only
s->rep->prod->conn->xprt_ctx = s;
s->rep->prod->applet.st0 = s->rep->prod->applet.st1 = 0;
req->analysers = 0;
if (s->fe == s->be) /* report it if the request was intercepted by the frontend */
s->fe->fe_counters.intercepted_req++;
return 0;
return 1;
}
/* check whether we have some ACLs set to redirect this request */