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.
diff --git a/src/proto_http.c b/src/proto_http.c
index dd8d123..2234b91 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2794,7 +2794,8 @@
* "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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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;
}