mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-01 11:01:46 +00:00
MEDIUM: stats: move request argument processing to the final step
At the moment, stats require some preliminary storage just to store some flags and codes that are parsed very early and used later. In fact that doesn't make much sense and makes it very hard to allocate the applet dynamically. This patch changes this. Now stats_check_uri() only checks for the validity of the request and the fact that it matches the stats uri. It's handle_stats() which parses it. It makes more sense because handle_stats() used to already perform some preliminary processing such as verifying that POST contents are not missing, etc... There is only one minor hiccup in doing so : the reqrep rules might be processed in between. This has been addressed by moving http_handle_stats() just after stats_check_uri() and setting s->target at the same time. Now that s->target is totally operational, it's used to mark the current request as being targetted at the stats, and this information is used after the request processing to remove the HTTP analysers and only let the applet handle the request. Thus we guarantee that the storage for the applet is filled with the relevant information and not overwritten when we switch to the applet.
This commit is contained in:
parent
347a35d19e
commit
414e9bb806
194
src/proto_http.c
194
src/proto_http.c
@ -2794,7 +2794,8 @@ int http_wait_for_request(struct session *s, struct channel *req, int an_bit)
|
||||
* "100-continue" expectation, check that admin rules are met for POST requests,
|
||||
* and program a response message if something was unexpected. It cannot fail
|
||||
* and always relies on the stats applet to complete the job. It does not touch
|
||||
* analysers nor counters.
|
||||
* analysers nor counters, which are left to the caller. It does not touch
|
||||
* s->target which is supposed to already point to the stats applet.
|
||||
*/
|
||||
int http_handle_stats(struct session *s, struct channel *req)
|
||||
{
|
||||
@ -2802,9 +2803,90 @@ int http_handle_stats(struct session *s, struct channel *req)
|
||||
struct stream_interface *si = s->rep->prod;
|
||||
struct http_txn *txn = &s->txn;
|
||||
struct http_msg *msg = &txn->req;
|
||||
struct uri_auth *uri_auth = s->be->uri_auth;
|
||||
const char *uri, *h, *lookup;
|
||||
|
||||
memset(&si->applet.ctx.stats, 0, sizeof(si->applet.ctx.stats));
|
||||
si->applet.ctx.stats.st_code = STAT_STATUS_INIT;
|
||||
si->applet.ctx.stats.flags |= STAT_FMT_HTML; /* assume HTML mode by default */
|
||||
|
||||
uri = msg->chn->buf->p + msg->sl.rq.u;
|
||||
lookup = uri + uri_auth->uri_len;
|
||||
|
||||
for (h = lookup; h <= uri + msg->sl.rq.u_l - 3; h++) {
|
||||
if (memcmp(h, ";up", 3) == 0) {
|
||||
si->applet.ctx.stats.flags |= STAT_HIDE_DOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uri_auth->refresh) {
|
||||
for (h = lookup; h <= uri + msg->sl.rq.u_l - 10; h++) {
|
||||
if (memcmp(h, ";norefresh", 10) == 0) {
|
||||
si->applet.ctx.stats.flags |= STAT_NO_REFRESH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (h = lookup; h <= uri + msg->sl.rq.u_l - 4; h++) {
|
||||
if (memcmp(h, ";csv", 4) == 0) {
|
||||
si->applet.ctx.stats.flags &= ~STAT_FMT_HTML;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (h = lookup; h <= uri + msg->sl.rq.u_l - 8; h++) {
|
||||
if (memcmp(h, ";st=", 4) == 0) {
|
||||
int i;
|
||||
h += 4;
|
||||
si->applet.ctx.stats.st_code = STAT_STATUS_UNKN;
|
||||
for (i = STAT_STATUS_INIT + 1; i < STAT_STATUS_SIZE; i++) {
|
||||
if (strncmp(stat_status_codes[i], h, 4) == 0) {
|
||||
si->applet.ctx.stats.st_code = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
si->applet.ctx.stats.scope_str = 0;
|
||||
si->applet.ctx.stats.scope_len = 0;
|
||||
for (h = lookup; h <= uri + msg->sl.rq.u_l - 8; h++) {
|
||||
if (memcmp(h, STAT_SCOPE_INPUT_NAME "=", strlen(STAT_SCOPE_INPUT_NAME) + 1) == 0) {
|
||||
int itx = 0;
|
||||
const char *h2;
|
||||
char scope_txt[STAT_SCOPE_TXT_MAXLEN + 1];
|
||||
const char *err;
|
||||
|
||||
h += strlen(STAT_SCOPE_INPUT_NAME) + 1;
|
||||
h2 = h;
|
||||
si->applet.ctx.stats.scope_str = h2 - msg->chn->buf->p;
|
||||
while (*h != ';' && *h != '\0' && *h != '&' && *h != ' ' && *h != '\n') {
|
||||
itx++;
|
||||
h++;
|
||||
}
|
||||
|
||||
if (itx > STAT_SCOPE_TXT_MAXLEN)
|
||||
itx = STAT_SCOPE_TXT_MAXLEN;
|
||||
si->applet.ctx.stats.scope_len = itx;
|
||||
|
||||
/* scope_txt = search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||
memcpy(scope_txt, h2, itx);
|
||||
scope_txt[itx] = '\0';
|
||||
err = invalid_char(scope_txt);
|
||||
if (err) {
|
||||
/* bad char in search text => clear scope */
|
||||
si->applet.ctx.stats.scope_str = 0;
|
||||
si->applet.ctx.stats.scope_len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
list_for_each_entry(stats_admin_rule, &uri_auth->admin_rules, list) {
|
||||
int ret = 1;
|
||||
|
||||
if (stats_admin_rule->cond) {
|
||||
@ -2854,7 +2936,6 @@ int http_handle_stats(struct session *s, struct channel *req)
|
||||
|
||||
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->applet.st1 = s->rep->prod->applet.st2 = 0;
|
||||
return 1;
|
||||
}
|
||||
@ -3325,7 +3406,6 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
|
||||
struct http_req_rule *http_req_last_rule = NULL;
|
||||
struct redirect_rule *rule;
|
||||
struct cond_wordlist *wl;
|
||||
int do_stats;
|
||||
|
||||
if (unlikely(msg->msg_state < HTTP_MSG_BODY)) {
|
||||
/* we need more data */
|
||||
@ -3371,12 +3451,13 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
|
||||
|
||||
/* evaluate stats http-request rules only if http-request is OK */
|
||||
if (!http_req_last_rule) {
|
||||
do_stats = stats_check_uri(s->rep->prod, txn, px);
|
||||
if (do_stats)
|
||||
if (stats_check_uri(s->rep->prod, txn, px)) {
|
||||
s->target = &http_stats_applet.obj_type;
|
||||
/* parse the whole stats request and extract the relevant information */
|
||||
http_handle_stats(s, req);
|
||||
http_req_last_rule = http_req_get_intercept_rule(px, &px->uri_auth->http_req_rules, s, txn);
|
||||
}
|
||||
}
|
||||
else
|
||||
do_stats = 0;
|
||||
|
||||
/* only apply req{,i}{rep/deny/tarpit} if the request was not yet
|
||||
* blocked by an http-request rule.
|
||||
@ -3485,7 +3566,7 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
|
||||
char *realm = http_req_last_rule->arg.auth.realm;
|
||||
|
||||
if (!realm)
|
||||
realm = do_stats?STATS_DEFAULT_REALM:px->id;
|
||||
realm = (objt_applet(s->target) == &http_stats_applet) ? STATS_DEFAULT_REALM : px->id;
|
||||
|
||||
chunk_printf(&trash, (txn->flags & TX_USE_PX_CONN) ? HTTP_407_fmt : HTTP_401_fmt, realm);
|
||||
txn->status = 401;
|
||||
@ -3520,10 +3601,8 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit,
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unlikely(do_stats)) {
|
||||
if (unlikely(objt_applet(s->target) == &http_stats_applet)) {
|
||||
/* process the stats request now */
|
||||
http_handle_stats(s, req);
|
||||
|
||||
if (s->fe == s->be) /* report it if the request was intercepted by the frontend */
|
||||
s->fe->fe_counters.intercepted_req++;
|
||||
|
||||
@ -7657,7 +7736,6 @@ int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct pr
|
||||
struct uri_auth *uri_auth = backend->uri_auth;
|
||||
struct http_msg *msg = &txn->req;
|
||||
const char *uri = msg->chn->buf->p+ msg->sl.rq.u;
|
||||
const char *h;
|
||||
|
||||
if (!uri_auth)
|
||||
return 0;
|
||||
@ -7665,101 +7743,13 @@ int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct pr
|
||||
if (txn->meth != HTTP_METH_GET && txn->meth != HTTP_METH_HEAD && txn->meth != HTTP_METH_POST)
|
||||
return 0;
|
||||
|
||||
memset(&si->applet.ctx.stats, 0, sizeof(si->applet.ctx.stats));
|
||||
si->applet.ctx.stats.st_code = STAT_STATUS_INIT;
|
||||
si->applet.ctx.stats.flags |= STAT_FMT_HTML; /* assume HTML mode by default */
|
||||
|
||||
/* check URI size */
|
||||
if (uri_auth->uri_len > msg->sl.rq.u_l)
|
||||
return 0;
|
||||
|
||||
h = uri;
|
||||
if (memcmp(h, uri_auth->uri_prefix, uri_auth->uri_len) != 0)
|
||||
if (memcmp(uri, uri_auth->uri_prefix, uri_auth->uri_len) != 0)
|
||||
return 0;
|
||||
|
||||
h += uri_auth->uri_len;
|
||||
while (h <= uri + msg->sl.rq.u_l - 3) {
|
||||
if (memcmp(h, ";up", 3) == 0) {
|
||||
si->applet.ctx.stats.flags |= STAT_HIDE_DOWN;
|
||||
break;
|
||||
}
|
||||
h++;
|
||||
}
|
||||
|
||||
if (uri_auth->refresh) {
|
||||
h = uri + uri_auth->uri_len;
|
||||
while (h <= uri + msg->sl.rq.u_l - 10) {
|
||||
if (memcmp(h, ";norefresh", 10) == 0) {
|
||||
si->applet.ctx.stats.flags |= STAT_NO_REFRESH;
|
||||
break;
|
||||
}
|
||||
h++;
|
||||
}
|
||||
}
|
||||
|
||||
h = uri + uri_auth->uri_len;
|
||||
while (h <= uri + msg->sl.rq.u_l - 4) {
|
||||
if (memcmp(h, ";csv", 4) == 0) {
|
||||
si->applet.ctx.stats.flags &= ~STAT_FMT_HTML;
|
||||
break;
|
||||
}
|
||||
h++;
|
||||
}
|
||||
|
||||
h = uri + uri_auth->uri_len;
|
||||
while (h <= uri + msg->sl.rq.u_l - 8) {
|
||||
if (memcmp(h, ";st=", 4) == 0) {
|
||||
int i;
|
||||
h += 4;
|
||||
si->applet.ctx.stats.st_code = STAT_STATUS_UNKN;
|
||||
for (i = STAT_STATUS_INIT + 1; i < STAT_STATUS_SIZE; i++) {
|
||||
if (strncmp(stat_status_codes[i], h, 4) == 0) {
|
||||
si->applet.ctx.stats.st_code = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
h++;
|
||||
}
|
||||
|
||||
si->applet.ctx.stats.scope_str = 0;
|
||||
si->applet.ctx.stats.scope_len = 0;
|
||||
h = uri + uri_auth->uri_len;
|
||||
while (h <= uri + msg->sl.rq.u_l - 8) {
|
||||
if (memcmp(h, STAT_SCOPE_INPUT_NAME "=", strlen(STAT_SCOPE_INPUT_NAME) + 1) == 0) {
|
||||
int itx = 0;
|
||||
const char *h2;
|
||||
char scope_txt[STAT_SCOPE_TXT_MAXLEN + 1];
|
||||
const char *err;
|
||||
|
||||
h += strlen(STAT_SCOPE_INPUT_NAME) + 1;
|
||||
h2 = h;
|
||||
si->applet.ctx.stats.scope_str = h2 - msg->chn->buf->p;
|
||||
while (*h != ';' && *h != '\0' && *h != '&' && *h != ' ' && *h != '\n') {
|
||||
itx++;
|
||||
h++;
|
||||
}
|
||||
|
||||
if (itx > STAT_SCOPE_TXT_MAXLEN)
|
||||
itx = STAT_SCOPE_TXT_MAXLEN;
|
||||
si->applet.ctx.stats.scope_len = itx;
|
||||
|
||||
/* scope_txt = search query, si->applet.ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
|
||||
memcpy(scope_txt, h2, itx);
|
||||
scope_txt[itx] = '\0';
|
||||
err = invalid_char(scope_txt);
|
||||
if (err) {
|
||||
/* bad char in search text => clear scope */
|
||||
si->applet.ctx.stats.scope_str = 0;
|
||||
si->applet.ctx.stats.scope_len = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
h++;
|
||||
}
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user