]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: stats: move the HTTP header injection to proto_http
authorWilly Tarreau <w@1wt.eu>
Sat, 22 Dec 2012 21:03:39 +0000 (22:03 +0100)
committerWilly Tarreau <w@1wt.eu>
Sat, 22 Dec 2012 21:50:01 +0000 (22:50 +0100)
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.

src/dumpstats.c
src/proto_http.c

index 661aa3f539601acb0e4beb553985fad365aabee2..e9e7d14b26ddc37b69ceeb2aeb91c0d04790b352 100644 (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);
                }
        }
 
index f6535f24d46cad4340ee6d05d9b362be5f16ad14..f664b8ee38a0d868a1ca9a33ffce0d7cf5bad034 100644 (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;
-                       }
-               }
-
-               /* 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;
-                       }
+       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);
                }
-
-               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 */