]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: stats: move request argument processing to the final step
authorWilly Tarreau <w@1wt.eu>
Fri, 22 Nov 2013 23:30:38 +0000 (00:30 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 9 Dec 2013 14:40:22 +0000 (15:40 +0100)
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.

src/proto_http.c

index dd8d123bae6c4de85a5b91d168359a30140eda4c..2234b9106819b946ad4c30faed4282e34343c372 100644 (file)
@@ -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;
 }