]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] http: split request waiter from request processor
authorWilly Tarreau <w@1wt.eu>
Tue, 7 Jul 2009 08:14:51 +0000 (10:14 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 7 Jul 2009 08:14:51 +0000 (10:14 +0200)
We want to split several steps in HTTP processing so that
we can call individual analysers depending on what processing
we want to perform. The first step consists in splitting the
part that waits for a request from the rest.

include/proto/proto_http.h
include/types/buffers.h
src/cfgparse.c
src/proto_http.c
src/session.c

index 175eb04794406306e4e25b0492bd056260f97a6f..8620bf45bb5d013e1f87576cddaf4de107d49f11 100644 (file)
@@ -61,6 +61,7 @@ int event_accept(int fd);
 int process_cli(struct session *t);
 int process_srv_data(struct session *t);
 int process_srv_conn(struct session *t);
+int http_wait_for_request(struct session *s, struct buffer *req);
 int http_process_request(struct session *t, struct buffer *req);
 int http_process_tarpit(struct session *s, struct buffer *req);
 int http_process_request_body(struct session *s, struct buffer *req);
index faad019b2e02465aa47ff54872d5a45592a8ae37..a895477d16ec8af9d79d6890626876cf08eadb40 100644 (file)
 #define AN_REQ_HTTP_TARPIT      0x00000008  /* wait for end of HTTP tarpit */
 #define AN_RTR_HTTP_HDR         0x00000010  /* inspect HTTP response headers */
 #define AN_REQ_UNIX_STATS       0x00000020  /* process unix stats socket request */
+#define AN_REQ_WAIT_HTTP        0x00000040  /* wait for an HTTP request */
 
 /* describes a chunk of string */
 struct chunk {
index ce6b266b892852c8c7694065e809fcf98c5b28e0..3852c459ea0d2cb5057d62bea7d8dae62f801a67 100644 (file)
@@ -3777,7 +3777,7 @@ int check_config_validity()
                        listener->handler = process_session;
 
                        if (curproxy->mode == PR_MODE_HTTP)
-                               listener->analysers |= AN_REQ_HTTP_HDR;
+                               listener->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_HDR;
 
                        /* smart accept mode is automatic in HTTP mode */
                        if ((curproxy->options2 & PR_O2_SMARTACC) ||
index 77092e0240206e5d5de6740211c3770305306e6b..7d43770ff6e3d384e2c443eec20944b69cda9609 100644 (file)
@@ -1502,29 +1502,14 @@ void http_msg_analyzer(struct buffer *buf, struct http_msg *msg, struct hdr_idx
        return;
 }
 
-/* This function performs all the processing enabled for the current request.
- * It returns 1 if the processing can continue on next analysers, or zero if it
- * needs more data, encounters an error, or wants to immediately abort the
- * request. It relies on buffers flags, and updates s->req->analysers. Its
- * behaviour is rather simple:
- *  - all enabled analysers are called in turn from the lower to the higher
- *    bit.
- *  - the analyser must check for errors and timeouts, and react as expected.
- *    It does not have to close anything upon error, the caller will.
- *  - if the analyser does not have enough data, it must return 0without calling
- *    other ones. It should also probably do a buffer_write_dis() to ensure
- *    that unprocessed data will not be forwarded. But that probably depends on
- *    the protocol.
- *  - if an analyser has enough data, it just has to pass on to the next
- *    analyser without using buffer_write_dis() (enabled by default).
- *  - if an analyser thinks it has no added value anymore staying here, it must
- *    reset its bit from the analysers flags in order not to be called anymore.
- *
- * In the future, analysers should be able to indicate that they want to be
- * called after XXX bytes have been received (or transfered), and the min of
- * all's wishes will be used to ring back (unless a special condition occurs).
+/* This stream analyser waits for a complete HTTP request. It returns 1 if the
+ * processing can continue on next analysers, or zero if it either needs more
+ * data or wants to immediately abort the request (eg: timeout, error, ...). It
+ * is tied to AN_REQ_WAIT_HTTP and may may remove itself from s->req->analysers
+ * when it has nothing left to do, and may remove any analyser when it wants to
+ * abort.
  */
-int http_process_request(struct session *s, struct buffer *req)
+int http_wait_for_request(struct session *s, struct buffer *req)
 {
        /*
         * We will parse the partial (or complete) lines.
@@ -1540,12 +1525,16 @@ int http_process_request(struct session *s, struct buffer *req)
         *   req->data + req->eol  = end of current header or line (LF or CRLF)
         *   req->lr = first non-visited byte
         *   req->r  = end of data
+        *
+        * At end of parsing, we may perform a capture of the error (if any), and
+        * we will set a few fields (msg->sol, txn->meth, sn->flags/SN_REDIRECTABLE).
+        * We also check for monitor-uri, logging, HTTP/0.9 to 1.0 conversion, and
+        * finally headers capture.
         */
 
        int cur_idx;
        struct http_txn *txn = &s->txn;
        struct http_msg *msg = &txn->req;
-       struct proxy *cur_proxy;
 
        DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
                now_ms, __FUNCTION__,
@@ -1669,19 +1658,17 @@ int http_process_request(struct session *s, struct buffer *req)
                return 0;
        }
 
+       /* OK now we have a complete HTTP request with indexed headers. Let's
+        * complete the request parsing by setting a few fields we will need
+        * later.
+        */
 
-       /****************************************************************
-        * More interesting part now : we know that we have a complete  *
-        * request which at least looks like HTTP. We have an indicator *
-        * of each header's length, so we can parse them quickly.       *
-        ****************************************************************/
-
-       if (msg->err_pos >= 0)
+       /* Maybe we found in invalid header name while we were configured not
+        * to block on that, so we have to capture it now.
+        */
+       if (unlikely(msg->err_pos >= 0))
                http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
 
-       req->analysers &= ~AN_REQ_HTTP_HDR;
-       req->analyse_exp = TICK_ETERNITY;
-
        /* ensure we keep this pointer to the beginning of the message */
        msg->sol = req->data + msg->som;
 
@@ -1708,13 +1695,12 @@ int http_process_request(struct session *s, struct buffer *req)
                 * We have found the monitor URI
                 */
                struct acl_cond *cond;
-               cur_proxy = s->fe;
 
                s->flags |= SN_MONITOR;
 
                /* Check if we want to fail this monitor request or not */
-               list_for_each_entry(cond, &cur_proxy->mon_fail_cond, list) {
-                       int ret = acl_exec_cond(cond, cur_proxy, s, txn, ACL_DIR_REQ);
+               list_for_each_entry(cond, &s->fe->mon_fail_cond, list) {
+                       int ret = acl_exec_cond(cond, s->fe, s, txn, ACL_DIR_REQ);
 
                        ret = acl_pass(ret);
                        if (cond->pol == ACL_COND_UNLESS)
@@ -1793,6 +1779,60 @@ int http_process_request(struct session *s, struct buffer *req)
                capture_headers(req->data + msg->som, &txn->hdr_idx,
                                txn->req.cap, s->fe->req_cap);
 
+       /* end of job, return OK */
+       req->analysers &= ~AN_REQ_WAIT_HTTP;
+       req->analyse_exp = TICK_ETERNITY;
+       return 1;
+
+ return_bad_req:
+       /* We centralize bad requests processing here */
+       if (unlikely(msg->msg_state == HTTP_MSG_ERROR) || msg->err_pos >= 0) {
+               /* we detected a parsing error. We want to archive this request
+                * in the dedicated proxy area for later troubleshooting.
+                */
+               http_capture_bad_message(&s->fe->invalid_req, s, req, msg, s->fe);
+       }
+
+       txn->req.msg_state = HTTP_MSG_ERROR;
+       txn->status = 400;
+       stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_400));
+       s->fe->failed_req++;
+
+ return_prx_cond:
+       if (!(s->flags & SN_ERR_MASK))
+               s->flags |= SN_ERR_PRXCOND;
+       if (!(s->flags & SN_FINST_MASK))
+               s->flags |= SN_FINST_R;
+
+       req->analysers = 0;
+       req->analyse_exp = TICK_ETERNITY;
+       return 0;
+}
+
+/* This function performs all the processing enabled for the current request.
+ * It returns 1 if the processing can continue on next analysers, or zero if it
+ * needs more data, encounters an error, or wants to immediately abort the
+ * request. It relies on buffers flags, and updates s->req->analysers.
+ */
+int http_process_request(struct session *s, struct buffer *req)
+{
+       int cur_idx;
+       struct http_txn *txn = &s->txn;
+       struct http_msg *msg = &txn->req;
+       struct proxy *cur_proxy;
+
+       req->analysers &= ~AN_REQ_HTTP_HDR;
+       req->analyse_exp = TICK_ETERNITY;
+
+       DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
+               now_ms, __FUNCTION__,
+               s,
+               req,
+               req->rex, req->wex,
+               req->flags,
+               req->l,
+               req->analysers);
+
        /*
         * 6: we will have to evaluate the filters.
         * As opposed to version 1.2, now they will be evaluated in the
index d3f3710c90aeac8183a849996ba4e82c814966b3..0f82639b8505d5d336abbaa3e087351e5e85e47c 100644 (file)
@@ -730,6 +730,12 @@ resync_stream_interface:
                                                break;
                                }
 
+                               if (s->req->analysers & AN_REQ_WAIT_HTTP) {
+                                       last_ana |= AN_REQ_WAIT_HTTP;
+                                       if (!http_wait_for_request(s, s->req))
+                                               break;
+                               }
+
                                if (s->req->analysers & AN_REQ_HTTP_HDR) {
                                        last_ana |= AN_REQ_HTTP_HDR;
                                        if (!http_process_request(s, s->req))