]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] http: complete splitting of the remaining stages
authorWilly Tarreau <w@1wt.eu>
Tue, 7 Jul 2009 13:10:31 +0000 (15:10 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 7 Jul 2009 13:10:31 +0000 (15:10 +0200)
The HTTP processing has been splitted into 7 steps, one of which
is not anymore HTTP-specific (content-switching). That way, it
becomes possible to use "use_backend" rules in TCP mode. A new
"use_server" directive should follow soon.

doc/configuration.txt
include/proto/proto_http.h
include/proto/proxy.h
include/types/buffers.h
src/cfgparse.c
src/client.c
src/proto_http.c
src/proxy.c
src/session.c

index 89525e8228ea22a5928a34eb43925acb3f5f2bca..bcd7c27fa1a3926cc03c456ccca5f1e4d17c598f 100644 (file)
@@ -1440,11 +1440,6 @@ default_backend <backend>
   used when no rule has matched. It generally is the dynamic backend which
   will catch all undetermined requests.
 
-  The "default_backend" keyword is also supported in TCP mode frontends to
-  facilitate the ordering of configurations in frontends and backends,
-  eventhough it does not make much more sense in case of TCP due to the fact
-  that use_backend currently does not work in TCP mode.
-
   Example :
 
         use_backend     dynamic  if  url_dyn
@@ -4254,7 +4249,7 @@ transparent (deprecated)
 
 use_backend <backend> if <condition>
 use_backend <backend> unless <condition>
-  Switch to a specific backend if/unless a Layer 7 condition is matched.
+  Switch to a specific backend if/unless an ACL-based condition is matched.
   May be used in sections :   defaults | frontend | listen | backend
                                   no   |    yes   |   yes  |   no
   Arguments :
@@ -4265,7 +4260,10 @@ use_backend <backend> unless <condition>
   When doing content-switching, connections arrive on a frontend and are then
   dispatched to various backends depending on a number of conditions. The
   relation between the conditions and the backends is described with the
-  "use_backend" keyword. This is supported only in HTTP mode.
+  "use_backend" keyword. While it is normally used with HTTP processing, it can
+  also be used in pure TCP, either without content using stateless ACLs (eg:
+  source address validation) or combined with a "tcp-request" rule to wait for
+  some payload.
 
   There may be as many "use_backend" rules as desired. All of these rules are
   evaluated in their declaration order, and the first one which matches will
@@ -4278,7 +4276,7 @@ use_backend <backend> unless <condition>
   used (in case of a "listen" section) or, in case of a frontend, no server is
   used and a 503 service unavailable response is returned.
 
-  See also: "default_backend" and section 7 about ACLs.
+  See also: "default_backend", "tcp-request", and section 7 about ACLs.
   
 
 5. Server options
index d70603c83257fc1780ad0431fd0d2d67736de36b..35a216c10018c1e7295f63948a30d167306b3acb 100644 (file)
@@ -62,6 +62,7 @@ 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 an_bit);
+int http_process_req_common(struct session *s, struct buffer *req, int an_bit, struct proxy *px);
 int http_process_request(struct session *t, struct buffer *req, int an_bit);
 int http_process_tarpit(struct session *s, struct buffer *req, int an_bit);
 int http_process_request_body(struct session *s, struct buffer *req, int an_bit);
index 8a2789df1ee706593c71c3bfb7ec18fd539a1b7d..36360e5cf5bf718dd47c05cbf30fe7f64676c11b 100644 (file)
@@ -35,6 +35,7 @@ void pause_proxy(struct proxy *p);
 void stop_proxy(struct proxy *p);
 void pause_proxies(void);
 void listen_proxies(void);
+void session_set_backend(struct session *s, struct proxy *be);
 
 const char *proxy_cap_str(int cap);
 const char *proxy_mode_str(int mode);
index a895477d16ec8af9d79d6890626876cf08eadb40..234020f7efc3f7c7e517c335be144c8f1edc8a34 100644 (file)
  * afterwards.
  */
 #define AN_REQ_INSPECT          0x00000001  /* inspect request contents */
-#define AN_REQ_HTTP_HDR         0x00000002  /* inspect HTTP request headers */
-#define AN_REQ_HTTP_BODY        0x00000004  /* inspect HTTP request body */
-#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 */
+#define AN_REQ_WAIT_HTTP        0x00000002  /* wait for an HTTP request */
+#define AN_REQ_HTTP_PROCESS_FE  0x00000004  /* process the frontend's HTTP part */
+#define AN_REQ_SWITCHING_RULES  0x00000008  /* apply the switching rules */
+#define AN_REQ_HTTP_PROCESS_BE  0x00000010  /* process the backend's HTTP part */
+#define AN_REQ_HTTP_INNER       0x00000020  /* inner processing of HTTP request */
+#define AN_REQ_HTTP_TARPIT      0x00000040  /* wait for end of HTTP tarpit */
+#define AN_REQ_HTTP_BODY        0x00000080  /* inspect HTTP request body */
+#define AN_REQ_UNIX_STATS       0x00000100  /* process unix stats socket request */
+
+#define AN_RTR_HTTP_HDR         0x00000200  /* inspect HTTP response headers */
 
 /* describes a chunk of string */
 struct chunk {
index 3852c459ea0d2cb5057d62bea7d8dae62f801a67..5afb11e7d99dae7f62c8750aafb6cac6ec7e6efa 100644 (file)
@@ -3775,9 +3775,11 @@ int check_config_validity()
                        listener->accept = event_accept;
                        listener->private = curproxy;
                        listener->handler = process_session;
+                       /* both TCP and HTTP must check switching rules */
+                       listener->analysers |= AN_REQ_SWITCHING_RULES;
 
                        if (curproxy->mode == PR_MODE_HTTP)
-                               listener->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_HDR;
+                               listener->analysers |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_FE | AN_REQ_HTTP_INNER | AN_REQ_HTTP_PROCESS_BE;
 
                        /* smart accept mode is automatic in HTTP mode */
                        if ((curproxy->options2 & PR_O2_SMARTACC) ||
@@ -3789,6 +3791,7 @@ int check_config_validity()
                            !LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules))
                                listener->analysers |= AN_REQ_INSPECT;
 
+                       /* We want the use_backend and default_backend rules to apply */
                        listener = listener->next;
                }
 
index 3e2099eedd7e9b0812ae77b1b0ae5af2009dbc2e..abf4d351a7222a0148e73d75964ac0cae750d5e3 100644 (file)
@@ -165,18 +165,12 @@ int event_accept(int fd) {
 
                s->task = t;
                s->listener = l;
-               s->be = s->fe = p;
 
-               /* in HTTP mode, content switching requires that the backend
-                * first points to the same proxy as the frontend. However, in
-                * TCP mode there will be no header processing so any default
-                * backend must be assigned if set.
+               /* Note: initially, the session's backend points to the frontend.
+                * This changes later when switching rules are executed or
+                * when the default backend is assigned.
                 */
-               if (p->mode == PR_MODE_TCP) {
-                       if (p->defbe.be)
-                               s->be = p->defbe.be;
-                       s->flags |= SN_BE_ASSIGNED;
-               }
+               s->be = s->fe = p;
 
                s->ana_state = 0;  /* analysers may change it but must reset it upon exit */
                s->req = s->rep = NULL; /* will be allocated later */
@@ -459,12 +453,6 @@ int event_accept(int fd) {
                if (p->feconn > p->feconn_max)
                        p->feconn_max = p->feconn;
 
-               if (s->flags & SN_BE_ASSIGNED) {
-                       proxy_inc_be_ctr(s->be);
-                       s->be->beconn++;
-                       if (s->be->beconn > s->be->beconn_max)
-                               s->be->beconn_max = s->be->beconn;
-               }
                actconn++;
                totalconn++;
 
index 33e94664c6311960692ebd633b58a124e47f567d..64e93d3eed5a229cd9660be41e47ed3b0a60d756 100644 (file)
@@ -354,10 +354,6 @@ const char http_is_ver_token[256] = {
 };
 
 
-#ifdef DEBUG_FULL
-static char *cli_stnames[4] = { "DAT", "SHR", "SHW", "CLS" };
-#endif
-
 /*
  * Adds a header and its CRLF at the tail of buffer <b>, just before the last
  * CRLF. Text length is measured first, so it cannot be NULL.
@@ -1809,17 +1805,20 @@ int http_wait_for_request(struct session *s, struct buffer *req, int an_bit)
        return 0;
 }
 
-/* This function performs all the processing enabled for the current request.
+/* This stream analyser runs all HTTP request processing which is common to
+ * frontends and backends, which means blocking ACLs, filters, connection-close,
+ * reqadd, stats and redirects. This is performed for the designated proxy.
  * 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.
+ * either needs more data or wants to immediately abort the request (eg: deny,
+ * error, ...).
  */
-int http_process_request(struct session *s, struct buffer *req, int an_bit)
+int http_process_req_common(struct session *s, struct buffer *req, int an_bit, struct proxy *px)
 {
-       int cur_idx;
        struct http_txn *txn = &s->txn;
        struct http_msg *msg = &txn->req;
-       struct proxy *cur_proxy;
+       struct acl_cond *cond;
+       struct redirect_rule *rule;
+       int cur_idx;
 
        req->analysers &= ~an_bit;
        req->analyse_exp = TICK_ETERNITY;
@@ -1833,70 +1832,27 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
                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
-        * filters order and not in the header order. This means that
-        * each filter has to be validated among all headers.
-        *
-        * We can now check whether we want to switch to another
-        * backend, in which case we will re-check the backend's
-        * filters and various options. In order to support 3-level
-        * switching, here's how we should proceed :
-        *
-        *  a) run be.
-        *     if (switch) then switch ->be to the new backend.
-        *  b) run be if (be != fe).
-        *     There cannot be any switch from there, so ->be cannot be
-        *     changed anymore.
-        *
-        * => filters always apply to ->be, then ->be may change.
-        *
-        * The response path will be able to apply either ->be, or
-        * ->be then ->fe filters in order to match the reverse of
-        * the forward sequence.
-        */
-
-       do {
-               struct acl_cond *cond;
-               struct redirect_rule *rule;
-               struct proxy *rule_set = s->be;
-               cur_proxy = s->be;
-
-               /* first check whether we have some ACLs set to block this request */
-               list_for_each_entry(cond, &cur_proxy->block_cond, list) {
-                       int ret = acl_exec_cond(cond, cur_proxy, s, txn, ACL_DIR_REQ);
+       /* first check whether we have some ACLs set to block this request */
+       list_for_each_entry(cond, &px->block_cond, list) {
+               int ret = acl_exec_cond(cond, px, s, txn, ACL_DIR_REQ);
 
-                       ret = acl_pass(ret);
-                       if (cond->pol == ACL_COND_UNLESS)
-                               ret = !ret;
+               ret = acl_pass(ret);
+               if (cond->pol == ACL_COND_UNLESS)
+                       ret = !ret;
 
-                       if (ret) {
-                               txn->status = 403;
-                               /* let's log the request time */
-                               s->logs.tv_request = now;
-                               stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
-                               goto return_prx_cond;
-                       }
-               }
-
-               /* try headers filters */
-               if (rule_set->req_exp != NULL) {
-                       if (apply_filters_to_request(s, req, rule_set->req_exp) < 0)
-                               goto return_bad_req;
+               if (ret) {
+                       txn->status = 403;
+                       /* let's log the request time */
+                       s->logs.tv_request = now;
+                       stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
+                       goto return_prx_cond;
                }
+       }
 
-               if (!(s->flags & SN_BE_ASSIGNED) && (s->be != cur_proxy)) {
-                       /* to ensure correct connection accounting on
-                        * the backend, we count the connection for the
-                        * one managing the queue.
-                        */
-                       s->be->beconn++;
-                       if (s->be->beconn > s->be->beconn_max)
-                               s->be->beconn_max = s->be->beconn;
-                       proxy_inc_be_ctr(s->be);
-                       s->flags |= SN_BE_ASSIGNED;
-               }
+       /* try headers filters */
+       if (px->req_exp != NULL) {
+               if (apply_filters_to_request(s, req, px->req_exp) < 0)
+                       goto return_bad_req;
 
                /* has the request been denied ? */
                if (txn->flags & TX_CLDENY) {
@@ -1907,249 +1863,235 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
                        stream_int_retnclose(req->prod, error_message(s, HTTP_ERR_403));
                        goto return_prx_cond;
                }
+       }
 
-               /* We might have to check for "Connection:" */
-               if (((s->fe->options | s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
-                   !(s->flags & SN_CONN_CLOSED)) {
-                       char *cur_ptr, *cur_end, *cur_next;
-                       int cur_idx, old_idx, delta, val;
-                       struct hdr_idx_elem *cur_hdr;
-
-                       cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
-                       old_idx = 0;
-
-                       while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
-                               cur_hdr  = &txn->hdr_idx.v[cur_idx];
-                               cur_ptr  = cur_next;
-                               cur_end  = cur_ptr + cur_hdr->len;
-                               cur_next = cur_end + cur_hdr->cr + 1;
-
-                               val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
-                               if (val) {
-                                       /* 3 possibilities :
-                                        * - we have already set Connection: close,
-                                        *   so we remove this line.
-                                        * - we have not yet set Connection: close,
-                                        *   but this line indicates close. We leave
-                                        *   it untouched and set the flag.
-                                        * - we have not yet set Connection: close,
-                                        *   and this line indicates non-close. We
-                                        *   replace it.
-                                        */
-                                       if (s->flags & SN_CONN_CLOSED) {
-                                               delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
-                                               txn->req.eoh += delta;
+       /* We might have to check for "Connection:" */
+       if (((s->fe->options | s->be->options) & (PR_O_HTTP_CLOSE|PR_O_FORCE_CLO)) &&
+           !(s->flags & SN_CONN_CLOSED)) {
+               char *cur_ptr, *cur_end, *cur_next;
+               int old_idx, delta, val;
+               struct hdr_idx_elem *cur_hdr;
+
+               cur_next = req->data + txn->req.som + hdr_idx_first_pos(&txn->hdr_idx);
+               old_idx = 0;
+
+               while ((cur_idx = txn->hdr_idx.v[old_idx].next)) {
+                       cur_hdr  = &txn->hdr_idx.v[cur_idx];
+                       cur_ptr  = cur_next;
+                       cur_end  = cur_ptr + cur_hdr->len;
+                       cur_next = cur_end + cur_hdr->cr + 1;
+
+                       val = http_header_match2(cur_ptr, cur_end, "Connection", 10);
+                       if (val) {
+                               /* 3 possibilities :
+                                * - we have already set Connection: close,
+                                *   so we remove this line.
+                                * - we have not yet set Connection: close,
+                                *   but this line indicates close. We leave
+                                *   it untouched and set the flag.
+                                * - we have not yet set Connection: close,
+                                *   and this line indicates non-close. We
+                                *   replace it.
+                                */
+                               if (s->flags & SN_CONN_CLOSED) {
+                                       delta = buffer_replace2(req, cur_ptr, cur_next, NULL, 0);
+                                       txn->req.eoh += delta;
+                                       cur_next += delta;
+                                       txn->hdr_idx.v[old_idx].next = cur_hdr->next;
+                                       txn->hdr_idx.used--;
+                                       cur_hdr->len = 0;
+                               } else {
+                                       if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
+                                               delta = buffer_replace2(req, cur_ptr + val, cur_end,
+                                                                       "close", 5);
                                                cur_next += delta;
-                                               txn->hdr_idx.v[old_idx].next = cur_hdr->next;
-                                               txn->hdr_idx.used--;
-                                               cur_hdr->len = 0;
-                                       } else {
-                                               if (strncasecmp(cur_ptr + val, "close", 5) != 0) {
-                                                       delta = buffer_replace2(req, cur_ptr + val, cur_end,
-                                                                               "close", 5);
-                                                       cur_next += delta;
-                                                       cur_hdr->len += delta;
-                                                       txn->req.eoh += delta;
-                                               }
-                                               s->flags |= SN_CONN_CLOSED;
+                                               cur_hdr->len += delta;
+                                               txn->req.eoh += delta;
                                        }
+                                       s->flags |= SN_CONN_CLOSED;
                                }
-                               old_idx = cur_idx;
                        }
+                       old_idx = cur_idx;
                }
-               /* add request headers from the rule sets in the same order */
-               for (cur_idx = 0; cur_idx < rule_set->nb_reqadd; cur_idx++) {
-                       if (unlikely(http_header_add_tail(req,
-                                                         &txn->req,
-                                                         &txn->hdr_idx,
-                                                         rule_set->req_add[cur_idx])) < 0)
-                               goto return_bad_req;
-               }
+       }
+       /* add request headers from the rule sets in the same order */
+       for (cur_idx = 0; cur_idx < px->nb_reqadd; cur_idx++) {
+               if (unlikely(http_header_add_tail(req,
+                                                 &txn->req,
+                                                 &txn->hdr_idx,
+                                                 px->req_add[cur_idx])) < 0)
+                       goto return_bad_req;
+       }
 
-               /* check if stats URI was requested, and if an auth is needed */
-               if (rule_set->uri_auth != NULL &&
-                   (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {
-                       /* we have to check the URI and auth for this request.
-                        * FIXME!!! that one is rather dangerous, we want to
-                        * make it follow standard rules (eg: clear req->analysers).
-                        */
-                       if (stats_check_uri_auth(s, rule_set)) {
-                               req->analysers = 0;
-                               return 0;
-                       }
+       /* check if stats URI was requested, and if an auth is needed */
+       if (px->uri_auth != NULL &&
+           (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)) {
+               /* we have to check the URI and auth for this request.
+                * FIXME!!! that one is rather dangerous, we want to
+                * make it follow standard rules (eg: clear req->analysers).
+                */
+               if (stats_check_uri_auth(s, px)) {
+                       req->analysers = 0;
+                       return 0;
                }
+       }
 
-               /* first check whether we have some ACLs set to redirect this request */
-               list_for_each_entry(rule, &cur_proxy->redirect_rules, list) {
-                       int ret = acl_exec_cond(rule->cond, cur_proxy, s, txn, ACL_DIR_REQ);
+       /* check whether we have some ACLs set to redirect this request */
+       list_for_each_entry(rule, &px->redirect_rules, list) {
+               int ret = acl_exec_cond(rule->cond, px, s, txn, ACL_DIR_REQ);
 
-                       ret = acl_pass(ret);
-                       if (rule->cond->pol == ACL_COND_UNLESS)
-                               ret = !ret;
+               ret = acl_pass(ret);
+               if (rule->cond->pol == ACL_COND_UNLESS)
+                       ret = !ret;
 
-                       if (ret) {
-                               struct chunk rdr = { trash, 0 };
-                               const char *msg_fmt;
-
-                               /* build redirect message */
-                               switch(rule->code) {
-                               case 303:
-                                       rdr.len = strlen(HTTP_303);
-                                       msg_fmt = HTTP_303;
-                                       break;
-                               case 301:
-                                       rdr.len = strlen(HTTP_301);
-                                       msg_fmt = HTTP_301;
-                                       break;
-                               case 302:
-                               default:
-                                       rdr.len = strlen(HTTP_302);
-                                       msg_fmt = HTTP_302;
-                                       break;
-                               }
+               if (ret) {
+                       struct chunk rdr = { trash, 0 };
+                       const char *msg_fmt;
 
-                               if (unlikely(rdr.len > sizeof(trash)))
-                                       goto return_bad_req;
-                               memcpy(rdr.str, msg_fmt, rdr.len);
-
-                               switch(rule->type) {
-                               case REDIRECT_TYPE_PREFIX: {
-                                       const char *path;
-                                       int pathlen;
-
-                                       path = http_get_path(txn);
-                                       /* build message using path */
-                                       if (path) {
-                                               pathlen = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
-                                               if (rule->flags & REDIRECT_FLAG_DROP_QS) {
-                                                       int qs = 0;
-                                                       while (qs < pathlen) {
-                                                               if (path[qs] == '?') {
-                                                                       pathlen = qs;
-                                                                       break;
-                                                               }
-                                                               qs++;
+                       /* build redirect message */
+                       switch(rule->code) {
+                       case 303:
+                               rdr.len = strlen(HTTP_303);
+                               msg_fmt = HTTP_303;
+                               break;
+                       case 301:
+                               rdr.len = strlen(HTTP_301);
+                               msg_fmt = HTTP_301;
+                               break;
+                       case 302:
+                       default:
+                               rdr.len = strlen(HTTP_302);
+                               msg_fmt = HTTP_302;
+                               break;
+                       }
+
+                       if (unlikely(rdr.len > sizeof(trash)))
+                               goto return_bad_req;
+                       memcpy(rdr.str, msg_fmt, rdr.len);
+
+                       switch(rule->type) {
+                       case REDIRECT_TYPE_PREFIX: {
+                               const char *path;
+                               int pathlen;
+
+                               path = http_get_path(txn);
+                               /* build message using path */
+                               if (path) {
+                                       pathlen = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
+                                       if (rule->flags & REDIRECT_FLAG_DROP_QS) {
+                                               int qs = 0;
+                                               while (qs < pathlen) {
+                                                       if (path[qs] == '?') {
+                                                               pathlen = qs;
+                                                               break;
                                                        }
+                                                       qs++;
                                                }
-                                       } else {
-                                               path = "/";
-                                               pathlen = 1;
-                                       }
-
-                                       if (rdr.len + rule->rdr_len + pathlen > sizeof(trash) - 4)
-                                               goto return_bad_req;
-
-                                       /* add prefix. Note that if prefix == "/", we don't want to
-                                        * add anything, otherwise it makes it hard for the user to
-                                        * configure a self-redirection.
-                                        */
-                                       if (rule->rdr_len != 1 || *rule->rdr_str != '/') {
-                                               memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
-                                               rdr.len += rule->rdr_len;
                                        }
-
-                                       /* add path */
-                                       memcpy(rdr.str + rdr.len, path, pathlen);
-                                       rdr.len += pathlen;
-                                       break;
+                               } else {
+                                       path = "/";
+                                       pathlen = 1;
                                }
-                               case REDIRECT_TYPE_LOCATION:
-                               default:
-                                       if (rdr.len + rule->rdr_len > sizeof(trash) - 4)
-                                               goto return_bad_req;
 
-                                       /* add location */
+                               if (rdr.len + rule->rdr_len + pathlen > sizeof(trash) - 4)
+                                       goto return_bad_req;
+
+                               /* add prefix. Note that if prefix == "/", we don't want to
+                                * add anything, otherwise it makes it hard for the user to
+                                * configure a self-redirection.
+                                */
+                               if (rule->rdr_len != 1 || *rule->rdr_str != '/') {
                                        memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
                                        rdr.len += rule->rdr_len;
-                                       break;
-                               }
-
-                               if (rule->cookie_len) {
-                                       memcpy(rdr.str + rdr.len, "\r\nSet-Cookie: ", 14);
-                                       rdr.len += 14;
-                                       memcpy(rdr.str + rdr.len, rule->cookie_str, rule->cookie_len);
-                                       rdr.len += rule->cookie_len;
-                                       memcpy(rdr.str + rdr.len, "\r\n", 2);
-                                       rdr.len += 2;
                                }
 
-                               /* add end of headers */
-                               memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
-                               rdr.len += 4;
+                               /* add path */
+                               memcpy(rdr.str + rdr.len, path, pathlen);
+                               rdr.len += pathlen;
+                               break;
+                       }
+                       case REDIRECT_TYPE_LOCATION:
+                       default:
+                               if (rdr.len + rule->rdr_len > sizeof(trash) - 4)
+                                       goto return_bad_req;
 
-                               txn->status = rule->code;
-                               /* let's log the request time */
-                               s->logs.tv_request = now;
-                               stream_int_retnclose(req->prod, &rdr);
-                               goto return_prx_cond;
+                               /* add location */
+                               memcpy(rdr.str + rdr.len, rule->rdr_str, rule->rdr_len);
+                               rdr.len += rule->rdr_len;
+                               break;
                        }
-               }
 
-               /* now check whether we have some switching rules for this request */
-               if (!(s->flags & SN_BE_ASSIGNED)) {
-                       struct switching_rule *rule;
-
-                       list_for_each_entry(rule, &cur_proxy->switching_rules, list) {
-                               int ret;
-
-                               ret = acl_exec_cond(rule->cond, cur_proxy, s, txn, ACL_DIR_REQ);
-
-                               ret = acl_pass(ret);
-                               if (rule->cond->pol == ACL_COND_UNLESS)
-                                       ret = !ret;
-
-                               if (ret) {
-                                       s->be = rule->be.backend;
-                                       s->be->beconn++;
-                                       if (s->be->beconn > s->be->beconn_max)
-                                               s->be->beconn_max = s->be->beconn;
-                                       proxy_inc_be_ctr(s->be);
-
-                                       /* assign new parameters to the session from the new backend */
-                                       s->rep->rto = s->req->wto = s->be->timeout.server;
-                                       s->req->cto = s->be->timeout.connect;
-                                       s->conn_retries = s->be->conn_retries;
-                                       if (s->be->options2 & PR_O2_RSPBUG_OK)
-                                               s->txn.rsp.err_pos = -1; /* let buggy responses pass */
-                                       s->flags |= SN_BE_ASSIGNED;
-                                       break;
-                               }
+                       if (rule->cookie_len) {
+                               memcpy(rdr.str + rdr.len, "\r\nSet-Cookie: ", 14);
+                               rdr.len += 14;
+                               memcpy(rdr.str + rdr.len, rule->cookie_str, rule->cookie_len);
+                               rdr.len += rule->cookie_len;
+                               memcpy(rdr.str + rdr.len, "\r\n", 2);
+                               rdr.len += 2;
                        }
-               }
 
-               if (!(s->flags & SN_BE_ASSIGNED) && cur_proxy->defbe.be) {
-                       /* No backend was set, but there was a default
-                        * backend set in the frontend, so we use it and
-                        * loop again.
-                        */
-                       s->be = cur_proxy->defbe.be;
-                       s->be->beconn++;
-                       if (s->be->beconn > s->be->beconn_max)
-                               s->be->beconn_max = s->be->beconn;
-                       proxy_inc_be_ctr(s->be);
-
-                       /* assign new parameters to the session from the new backend */
-                       s->rep->rto = s->req->wto = s->be->timeout.server;
-                       s->req->cto = s->be->timeout.connect;
-                       s->conn_retries = s->be->conn_retries;
-                       if (s->be->options2 & PR_O2_RSPBUG_OK)
-                               s->txn.rsp.err_pos = -1; /* let buggy responses pass */
-                       s->flags |= SN_BE_ASSIGNED;
+                       /* add end of headers */
+                       memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
+                       rdr.len += 4;
+
+                       txn->status = rule->code;
+                       /* let's log the request time */
+                       s->logs.tv_request = now;
+                       stream_int_retnclose(req->prod, &rdr);
+                       goto return_prx_cond;
                }
-       } while (s->be != cur_proxy);  /* we loop only if s->be has changed */
+       }
+
+       /* that's OK for us now, let's move on to next analysers */
+       return 1;
 
-       if (!(s->flags & SN_BE_ASSIGNED)) {
-               /* To ensure correct connection accounting on
-                * the backend, we count the connection for the
-                * one managing the queue.
+ 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.
                 */
-               s->be->beconn++;
-               if (s->be->beconn > s->be->beconn_max)
-                       s->be->beconn_max = s->be->beconn;
-               proxy_inc_be_ctr(s->be);
-               s->flags |= SN_BE_ASSIGNED;
+               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 an_bit)
+{
+       struct http_txn *txn = &s->txn;
+       struct http_msg *msg = &txn->req;
+
+       req->analysers &= ~an_bit;
+       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);
+
        /*
         * Right now, we know that we have processed the entire headers
         * and that unwanted requests have been filtered out. We can do
@@ -2436,7 +2378,6 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
        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))
@@ -3142,18 +3083,7 @@ int apply_filter_to_req_headers(struct session *t, struct buffer *req, struct hd
                                        break;
 
                                /* Swithing Proxy */
-                               t->be = (struct proxy *) exp->replace;
-
-                               /* right now, the backend switch is not overly complicated
-                                * because we have associated req_cap and rsp_cap to the
-                                * frontend, and the beconn will be updated later.
-                                */
-
-                               t->rep->rto = t->req->wto = t->be->timeout.server;
-                               t->req->cto = t->be->timeout.connect;
-                               t->conn_retries = t->be->conn_retries;
-                               if (t->be->options2 & PR_O2_RSPBUG_OK)
-                                       t->txn.rsp.err_pos = -1; /* let buggy responses pass */
+                               session_set_backend(t, (struct proxy *)exp->replace);
                                last_hdr = 1;
                                break;
 
@@ -3265,18 +3195,7 @@ int apply_filter_to_req_line(struct session *t, struct buffer *req, struct hdr_e
                                break;
 
                        /* Swithing Proxy */
-                       t->be = (struct proxy *) exp->replace;
-
-                       /* right now, the backend switch is not too much complicated
-                        * because we have associated req_cap and rsp_cap to the
-                        * frontend, and the beconn will be updated later.
-                        */
-
-                       t->rep->rto = t->req->wto = t->be->timeout.server;
-                       t->req->cto = t->be->timeout.connect;
-                       t->conn_retries = t->be->conn_retries;
-                       if (t->be->options2 & PR_O2_RSPBUG_OK)
-                               t->txn.rsp.err_pos = -1; /* let buggy responses pass */
+                       session_set_backend(t, (struct proxy *)exp->replace);
                        done = 1;
                        break;
 
index 69b070e2b4c195000ad6ce04728516b6a81717f2..dcfb12d879e1a5608ad7123639d866e0ca80fdf2 100644 (file)
@@ -631,6 +631,30 @@ void listen_proxies(void)
        }
 }
 
+/* Set current session's backend to <be>. Nothing is done if the
+ * session already had a backend assigned, which is indicated by
+ * s->flags & SN_BE_ASSIGNED.
+ * All flags, stats and counters which need be updated are updated.
+ */
+void session_set_backend(struct session *s, struct proxy *be)
+{
+       if (s->flags & SN_BE_ASSIGNED)
+               return;
+       s->be = be;
+       be->beconn++;
+       if (be->beconn > be->beconn_max)
+               be->beconn_max = be->beconn;
+       proxy_inc_be_ctr(be);
+
+       /* assign new parameters to the session from the new backend */
+       s->rep->rto = s->req->wto = be->timeout.server;
+       s->req->cto = be->timeout.connect;
+       s->conn_retries = be->conn_retries;
+       if (be->options2 & PR_O2_RSPBUG_OK)
+               s->txn.rsp.err_pos = -1; /* let buggy responses pass */
+       s->flags |= SN_BE_ASSIGNED;
+}
+
 static struct cfg_kw_list cfg_kws = {{ },{
        { CFG_LISTEN, "timeout", proxy_parse_timeout },
        { CFG_LISTEN, "clitimeout", proxy_parse_timeout },
index 1d223d080baf4bc1984656ea42d2d53f49f72799..d4817ac2c380fe95d080278bddd8458c106ca548 100644 (file)
@@ -19,6 +19,7 @@
 #include <types/capture.h>
 #include <types/global.h>
 
+#include <proto/acl.h>
 #include <proto/backend.h>
 #include <proto/buffers.h>
 #include <proto/hdr_idx.h>
@@ -27,6 +28,7 @@
 #include <proto/pipe.h>
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
+#include <proto/proxy.h>
 #include <proto/queue.h>
 #include <proto/server.h>
 #include <proto/stream_interface.h>
@@ -552,6 +554,59 @@ static void sess_prepare_conn_req(struct session *s, struct stream_interface *si
        si->state = SI_ST_ASS;
 }
 
+/* This stream analyser checks the switching rules and changes the backend
+ * if appropriate. The default_backend rule is also considered.
+ * 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.
+ */
+int process_switching_rules(struct session *s, struct buffer *req, int an_bit)
+{
+       req->analysers &= ~an_bit;
+       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);
+
+       /* now check whether we have some switching rules for this request */
+       if (!(s->flags & SN_BE_ASSIGNED)) {
+               struct switching_rule *rule;
+
+               list_for_each_entry(rule, &s->fe->switching_rules, list) {
+                       int ret;
+
+                       ret = acl_exec_cond(rule->cond, s->fe, s, &s->txn, ACL_DIR_REQ);
+                       ret = acl_pass(ret);
+                       if (rule->cond->pol == ACL_COND_UNLESS)
+                               ret = !ret;
+
+                       if (ret) {
+                               session_set_backend(s, rule->be.backend);
+                               break;
+                       }
+               }
+
+               /* To ensure correct connection accounting on the backend, we
+                * have to assign one if it was not set (eg: a listen). This
+                * measure also takes care of correctly setting the default
+                * backend if any.
+                */
+               if (!(s->flags & SN_BE_ASSIGNED))
+                       session_set_backend(s, s->fe->defbe.be ? s->fe->defbe.be : s->be);
+       }
+
+       /* we don't want to run the HTTP filters again if the backend has not changed */
+       if (s->fe == s->be)
+               s->req->analysers &= ~AN_REQ_HTTP_PROCESS_BE;
+
+       return 1;
+}
+
 /* Processes the client, server, request and response jobs of a session task,
  * then puts it back to the wait queue in a clean state, or cleans up its
  * resources if it must be deleted. Returns in <next> the date the task wants
@@ -736,9 +791,27 @@ resync_stream_interface:
                                                break;
                                }
 
-                               if (s->req->analysers & AN_REQ_HTTP_HDR) {
-                                       last_ana |= AN_REQ_HTTP_HDR;
-                                       if (!http_process_request(s, s->req, AN_REQ_HTTP_HDR))
+                               if (s->req->analysers & AN_REQ_HTTP_PROCESS_FE) {
+                                       last_ana |= AN_REQ_HTTP_PROCESS_FE;
+                                       if (!http_process_req_common(s, s->req, AN_REQ_HTTP_PROCESS_FE, s->fe))
+                                               break;
+                               }
+
+                               if (s->req->analysers & AN_REQ_SWITCHING_RULES) {
+                                       last_ana |= AN_REQ_SWITCHING_RULES;
+                                       if (!process_switching_rules(s, s->req, AN_REQ_SWITCHING_RULES))
+                                               break;
+                               }
+
+                               if (s->req->analysers & AN_REQ_HTTP_PROCESS_BE) {
+                                       last_ana |= AN_REQ_HTTP_PROCESS_BE;
+                                       if (!http_process_req_common(s, s->req, AN_REQ_HTTP_PROCESS_BE, s->be))
+                                               break;
+                               }
+
+                               if (s->req->analysers & AN_REQ_HTTP_INNER) {
+                                       last_ana |= AN_REQ_HTTP_INNER;
+                                       if (!http_process_request(s, s->req, AN_REQ_HTTP_INNER))
                                                break;
                                }