[MEDIUM] stats: make HTTP stats use an I/O handler

Doing this, we can remove the last BF_HIJACK user and remove
produce_content(). s->data_source could also be removed but
it is currently used to detect if the stats or a server was
used.
This commit is contained in:
Willy Tarreau 2009-10-04 15:56:38 +02:00
parent 65671abd32
commit b0c9bc4f95
5 changed files with 86 additions and 78 deletions

View File

@ -59,6 +59,7 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri); int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri);
int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep); int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep);
int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep); int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep);
void http_stats_io_handler(struct stream_interface *si);
#endif /* _PROTO_DUMPSTATS_H */ #endif /* _PROTO_DUMPSTATS_H */

View File

@ -1,22 +1,22 @@
/* /*
include/proto/proto_http.h * include/proto/proto_http.h
This file contains HTTP protocol definitions. * This file contains HTTP protocol definitions.
*
Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu * Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu
*
This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1 * License as published by the Free Software Foundation, version 2.1
exclusively. * exclusively.
*
This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details. * Lesser General Public License for more details.
*
You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef _PROTO_PROTO_HTTP_H #ifndef _PROTO_PROTO_HTTP_H
@ -68,9 +68,6 @@ int http_process_tarpit(struct session *s, struct buffer *req, int an_bit);
int http_process_request_body(struct session *s, struct buffer *req, int an_bit); int http_process_request_body(struct session *s, struct buffer *req, int an_bit);
int process_response(struct session *t); int process_response(struct session *t);
void produce_content(struct session *s, struct buffer *rep);
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 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, int len); void get_srv_from_appsession(struct session *t, const char *begin, int len);
int apply_filter_to_req_headers(struct session *t, struct buffer *req, struct hdr_exp *exp); int apply_filter_to_req_headers(struct session *t, struct buffer *req, struct hdr_exp *exp);

View File

@ -211,7 +211,7 @@ struct session {
int ptr; /* <0: headers, >=0 : text pointer to restart from */ int ptr; /* <0: headers, >=0 : text pointer to restart from */
int bol; /* pointer to beginning of current line */ int bol; /* pointer to beginning of current line */
} errors; } errors;
} data_ctx; /* used by produce_content to dump the stats right now */ } data_ctx; /* used by stats I/O handlers to dump the stats */
unsigned int uniq_id; /* unique ID used for the traces */ unsigned int uniq_id; /* unique ID used for the traces */
}; };

View File

@ -587,7 +587,6 @@ int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep)
/* skip the disabled proxies and non-networked ones */ /* skip the disabled proxies and non-networked ones */
if (px->state != PR_STSTOPPED && if (px->state != PR_STSTOPPED &&
(px->cap & (PR_CAP_FE | PR_CAP_BE))) { (px->cap & (PR_CAP_FE | PR_CAP_BE))) {
rep->flags |= BF_READ_PARTIAL; /* remove this once stats_dump_proxy uses buffer_feed */
if (stats_dump_proxy(s, px, NULL) == 0) if (stats_dump_proxy(s, px, NULL) == 0)
return 0; return 0;
} }
@ -616,6 +615,57 @@ int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep)
} }
/* 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->st0 becomes non-zero once the transfer is finished. The handler
* automatically unregisters itself once transfer is complete.
*/
void http_stats_io_handler(struct stream_interface *si)
{
struct session *s = si->private;
struct buffer *req = si->ob;
struct buffer *res = si->ib;
if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
goto out;
/* check that the output is not closed */
if (res->flags & (BF_SHUTW|BF_SHUTW_NOW))
si->st0 = 1;
if (!si->st0) {
if (stats_dump_http(s, res, s->be->uri_auth)) {
si->st0 = 1;
si->shutw(si);
} else {
/* buffer full */
si->flags |= SI_FL_WAIT_ROOM;
}
}
if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST))
si->shutw(si);
if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && si->st0) {
si->shutr(si);
res->flags |= BF_READ_NULL;
}
/* update all other flags and resync with the other side */
si->update(si);
/* we don't want to expire timeouts while we're processing requests */
si->ib->rex = TICK_ETERNITY;
si->ob->wex = TICK_ETERNITY;
out:
if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
/* check that we have released everything then unregister */
stream_int_unregister_handler(si);
}
}
/* /*
* Produces statistics data for the session <s>. Expects to be called with * Produces statistics data for the session <s>. Expects to be called with
* client socket shut down on input. It stops by itself by unsetting the * client socket shut down on input. It stops by itself by unsetting the
@ -650,11 +700,9 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
chunk_printf(&msg, "\r\n"); chunk_printf(&msg, "\r\n");
s->txn.status = 200; s->txn.status = 200;
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
msg.len = 0;
if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is 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 s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
if (!(s->flags & SN_FINST_MASK)) if (!(s->flags & SN_FINST_MASK))
@ -663,7 +711,6 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
if (s->txn.meth == HTTP_METH_HEAD) { if (s->txn.meth == HTTP_METH_HEAD) {
/* that's all we return in case of HEAD request */ /* that's all we return in case of HEAD request */
s->data_state = DATA_ST_FIN; s->data_state = DATA_ST_FIN;
buffer_stop_hijack(rep);
return 1; return 1;
} }
@ -754,7 +801,7 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
} else { } else {
print_csv_header(&msg); print_csv_header(&msg);
} }
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
s->data_state = DATA_ST_INFO; s->data_state = DATA_ST_INFO;
@ -866,7 +913,7 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
"" ""
); );
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} }
@ -895,7 +942,7 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
case DATA_ST_END: case DATA_ST_END:
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, "</body></html>\n"); chunk_printf(&msg, "</body></html>\n");
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} }
@ -903,12 +950,11 @@ int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
/* fall through */ /* fall through */
case DATA_ST_FIN: case DATA_ST_FIN:
buffer_stop_hijack(rep);
return 1; return 1;
default: default:
/* unknown state ! */ /* unknown state ! */
buffer_stop_hijack(rep); s->data_state = DATA_ST_FIN;
return -1; return -1;
} }
} }
@ -994,7 +1040,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
px->id, px->id,
px->desc ? "desc" : "empty", px->desc ? px->desc : ""); px->desc ? "desc" : "empty", px->desc ? px->desc : "");
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} }
@ -1075,7 +1121,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
px->fe_sps_lim, px->fe_sps_max); px->fe_sps_lim, px->fe_sps_max);
} }
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} }
@ -1339,7 +1385,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
/* finish with EOL */ /* finish with EOL */
chunk_printf(&msg, "\n"); chunk_printf(&msg, "\n");
} }
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} /* for sv */ } /* for sv */
@ -1452,7 +1498,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
read_freq_ctr(&px->be_sess_per_sec), read_freq_ctr(&px->be_sess_per_sec),
px->be_sps_max); px->be_sps_max);
} }
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} }
@ -1463,7 +1509,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) { if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, "</table><p>\n"); chunk_printf(&msg, "</table><p>\n");
if (buffer_write_chunk(rep, &msg) >= 0) if (buffer_feed_chunk(rep, &msg) >= 0)
return 0; return 0;
} }

View File

@ -3238,40 +3238,6 @@ int process_response(struct session *t)
return 0; return 0;
} }
/*
* Produces data for the session <s> depending on its source. Expects to be
* called with client socket shut down on input. Right now, only statistics can
* be produced. It stops by itself by unsetting the BF_HIJACK flag from the
* buffer, which it uses to keep on being called when there is free space in
* the buffer, or simply by letting an empty buffer upon return.
*/
void produce_content(struct session *s, struct buffer *rep)
{
if (s->data_source == DATA_SRC_NONE) {
buffer_stop_hijack(rep);
return;
}
else if (s->data_source == DATA_SRC_STATS) {
/* dump server statistics */
int ret;
ret = stats_dump_http(s, rep, s->be->uri_auth);
if (ret >= 0)
return;
/* -1 indicates an error */
}
/* unknown data source or internal error */
s->txn.status = 500;
stream_int_retnclose(rep->cons, 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;
buffer_stop_hijack(rep);
return;
}
/* Iterate the same filter through all request headers. /* Iterate the same filter through all request headers.
* Returns 1 if this filter can be stopped upon return, otherwise 0. * Returns 1 if this filter can be stopped upon return, otherwise 0.
* Since it can manage the switch to another backend, it updates the per-proxy * Since it can manage the switch to another backend, it updates the per-proxy
@ -4508,7 +4474,7 @@ void get_srv_from_appsession(struct session *t, const char *begin, int len)
* *
* It is assumed that the request is either a HEAD or GET and that the * It is assumed that the request is either a HEAD or GET and that the
* t->be->uri_auth field is valid. An HTTP/401 response may be sent, or * t->be->uri_auth field is valid. An HTTP/401 response may be sent, or
* produce_content() can be called to start sending data. * the stats I/O handler will be registered to start sending data.
* *
* Returns 1 if the session's state changes, otherwise 0. * Returns 1 if the session's state changes, otherwise 0.
*/ */
@ -4625,15 +4591,13 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend)
/* The request is valid, the user is authenticated. Let's start sending /* The request is valid, the user is authenticated. Let's start sending
* data. * data.
*/ */
buffer_dont_connect(t->req);
buffer_shutw_now(t->req);
buffer_shutr_now(t->rep);
stream_int_retnclose(t->rep->cons, NULL);
t->logs.tv_request = now; t->logs.tv_request = now;
t->data_source = DATA_SRC_STATS; t->data_source = DATA_SRC_STATS;
t->data_state = DATA_ST_INIT; t->data_state = DATA_ST_INIT;
t->task->nice = -32; /* small boost for HTTP statistics */ t->task->nice = -32; /* small boost for HTTP statistics */
buffer_install_hijacker(t, t->rep, produce_content); stream_int_register_handler(t->rep->prod, http_stats_io_handler);
t->rep->prod->private = t;
t->rep->prod->st0 = t->rep->prod->st1 = 0;
return 1; return 1;
} }