]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] completely implement the server redirection method
authorWilly Tarreau <w@1wt.eu>
Tue, 12 Feb 2008 23:45:24 +0000 (00:45 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 12 Feb 2008 23:55:49 +0000 (00:55 +0100)
Now when a server has "redir <prefix>" on its config line, any HEAD or GET
request addressing it will lead to a 302 with Location set to "<prefix>"
immediately followed by the relative URI of the incoming request. This makes
it very easy to send redirect to browsers to check remote static servers, as
well as to provide redirection for remote sites when the local one is down.

include/types/session.h
src/backend.c
src/proto_http.c

index 46569a5719bdbaedf9f1bb593d52688057a0fcab..9437198d9591db5977c486b7caeaf815934b10e1 100644 (file)
@@ -50,7 +50,7 @@
 #define SN_FRT_ADDR_SET        0x00000080      /* set if the frontend address has been filled */
 #define SN_REDISP      0x00000100      /* set if this session was redispatched from one server to another */
 #define SN_CONN_TAR    0x00000200      /* set if this session is turning around before reconnecting */
-/* unused:              0x00000400 */
+#define SN_REDIRECTABLE        0x00000400      /* set if this session is redirectable (GET or HEAD) */
 /* unused:              0x00000800 */
 
 /* session termination conditions, bits values 0x1000 to 0x7000 (0-7 shift 12) */
index 5ea6590d892db85cf3d5829ac22934d0c3c35c53..20e3a040da6250f0a0244501e029ddc3f99b7889 100644 (file)
@@ -1059,6 +1059,13 @@ int assign_server_and_queue(struct session *s)
                return SRV_STATUS_INTERNAL;
 
        if (s->flags & SN_ASSIGNED) {
+               if ((s->flags & SN_REDIRECTABLE) && s->srv && s->srv->rdr_len) {
+                       /* server scheduled for redirection, and already assigned. We
+                        * don't want to go further nor check the queue.
+                        */
+                       return SRV_STATUS_OK;
+               }
+
                if (s->srv && s->srv->maxqueue > 0 && s->srv->nbpend >= s->srv->maxqueue) {
                        s->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
                        s->srv = NULL;
@@ -1084,6 +1091,13 @@ int assign_server_and_queue(struct session *s)
        err = assign_server(s);
        switch (err) {
        case SRV_STATUS_OK:
+               if ((s->flags & SN_REDIRECTABLE) && s->srv && s->srv->rdr_len) {
+                       /* server supporting redirection and it is possible.
+                        * Let's report that and ignore maxconn !
+                        */
+                       return SRV_STATUS_OK;
+               }
+
                /* in balance mode, we might have servers with connection limits */
                if (s->srv &&
                    s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
index 16b2f075d369ca4b94e6f2974528163a34d6cbfe..429f57142227e84de1c737ec8e86e28a415d1532 100644 (file)
@@ -592,6 +592,54 @@ static http_meth_t find_http_meth(const char *str, const int len)
 
 }
 
+/* Parse the URI from the given transaction (which is assumed to be in request
+ * phase) and look for the "/" beginning the PATH. If not found, return NULL.
+ * It is returned otherwise.
+ */
+static char *
+http_get_path(struct http_txn *txn)
+{
+       char *ptr, *end;
+
+       ptr = txn->req.sol + txn->req.sl.rq.u;
+       end = ptr + txn->req.sl.rq.u_l;
+
+       if (ptr >= end)
+               return NULL;
+
+       /* RFC2616, par. 5.1.2 :
+        * Request-URI = "*" | absuri | abspath | authority
+        */
+
+       if (*ptr == '*')
+               return NULL;
+
+       if (isalpha((unsigned char)*ptr)) {
+               /* this is a scheme as described by RFC3986, par. 3.1 */
+               ptr++;
+               while (ptr < end &&
+                      (isalnum((unsigned char)*ptr) || *ptr == '+' || *ptr == '-' || *ptr == '.'))
+                       ptr++;
+               /* skip '://' */
+               if (ptr == end || *ptr++ != ':')
+                       return NULL;
+               if (ptr == end || *ptr++ != '/')
+                       return NULL;
+               if (ptr == end || *ptr++ != '/')
+                       return NULL;
+       }
+       /* skip [user[:passwd]@]host[:[port]] */
+
+       while (ptr < end && *ptr != '/')
+               ptr++;
+
+       if (ptr == end)
+               return NULL;
+
+       /* OK, we got the '/' ! */
+       return ptr;
+}
+
 /* Processes the client and server 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
@@ -2451,9 +2499,59 @@ int process_srv(struct session *t)
 
                        do {
                                /* first, get a connection */
+                               if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
+                                       t->flags |= SN_REDIRECTABLE;
+
                                if (srv_redispatch_connect(t))
                                        return t->srv_state != SV_STIDLE;
 
+                               if ((t->flags & SN_REDIRECTABLE) && t->srv && t->srv->rdr_len) {
+                                       /* Server supporting redirection and it is possible.
+                                        * Invalid requests are reported as such. It concerns all
+                                        * the largest ones.
+                                        */
+                                       struct chunk rdr;
+                                       char *path;
+                                       int len;
+
+                                       /* 1: create the response header */
+                                       rdr.len = strlen(HTTP_302);
+                                       rdr.str = trash;
+                                       memcpy(rdr.str, HTTP_302, rdr.len);
+
+                                       /* 2: add the server's prefix */
+                                       if (rdr.len + t->srv->rdr_len > sizeof(trash))
+                                               goto cancel_redir;
+
+                                       memcpy(rdr.str + rdr.len, t->srv->rdr_pfx, t->srv->rdr_len);
+                                       rdr.len += t->srv->rdr_len;
+
+                                       /* 3: add the request URI */
+                                       path = http_get_path(txn);
+                                       if (!path)
+                                               goto cancel_redir;
+                                       len = txn->req.sl.rq.u_l + (txn->req.sol+txn->req.sl.rq.u) - path;
+                                       if (rdr.len + len > sizeof(trash) - 4) /* 4 for CRLF-CRLF */
+                                               goto cancel_redir;
+
+                                       memcpy(rdr.str + rdr.len, path, len);
+                                       rdr.len += len;
+                                       memcpy(rdr.str + rdr.len, "\r\n\r\n", 4);
+                                       rdr.len += 4;
+
+                                       srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C, 302, &rdr);
+                                       /* FIXME: we should increase a counter of redirects per server and per backend. */
+                                       if (t->srv)
+                                               t->srv->cum_sess++;
+                                       return 1;
+                               cancel_redir:
+                                       txn->status = 400;
+                                       t->fe->failed_req++;
+                                       srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_C,
+                                                          400, error_message(t, HTTP_ERR_400));
+                                       return 1;
+                               }
+
                                /* try to (re-)connect to the server, and fail if we expire the
                                 * number of retries.
                                 */
@@ -5226,39 +5324,9 @@ acl_fetch_path(struct proxy *px, struct session *l4, void *l7, int dir,
                /* ensure the indexes are not affected */
                return 0;
 
-       ptr = txn->req.sol + txn->req.sl.rq.u;
-       end = ptr + txn->req.sl.rq.u_l;
-
-       if (ptr >= end)
-               return 0;
-
-       /* RFC2616, par. 5.1.2 :
-        * Request-URI = "*" | absuri | abspath | authority
-        */
-
-       if (*ptr == '*')
-               return 0;
-
-       if (isalpha((unsigned char)*ptr)) {
-               /* this is a scheme as described by RFC3986, par. 3.1 */
-               ptr++;
-               while (ptr < end &&
-                      (isalnum((unsigned char)*ptr) || *ptr == '+' || *ptr == '-' || *ptr == '.'))
-                       ptr++;
-               /* skip '://' */
-               if (ptr == end || *ptr++ != ':')
-                       return 0;
-               if (ptr == end || *ptr++ != '/')
-                       return 0;
-               if (ptr == end || *ptr++ != '/')
-                       return 0;
-       }
-       /* skip [user[:passwd]@]host[:[port]] */
-
-       while (ptr < end && *ptr != '/')
-               ptr++;
-
-       if (ptr == end)
+       end = txn->req.sol + txn->req.sl.rq.u + txn->req.sl.rq.u_l;
+       ptr = http_get_path(txn);
+       if (!ptr)
                return 0;
 
        /* OK, we got the '/' ! */