]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
Revert "[BUILD] backend.c and checks.c did not build without tproxy !"
authorWilly Tarreau <w@1wt.eu>
Thu, 14 Feb 2008 19:25:24 +0000 (20:25 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 14 Feb 2008 19:25:24 +0000 (20:25 +0100)
This reverts commit 3c3c0122f84d72eae1c4ef4b1826bfdbef7d95e6.
This commit was buggy as it also removed previous tproxy changes !

doc/configuration.txt
include/types/server.h
include/types/session.h
src/backend.c
src/cfgparse.c
src/checks.c
src/proto_http.c
tests/test-redir.cfg [new file with mode: 0644]

index 0e409881e84fe06c00059f772ada16db2311350a..900b9a719d8d4ac1019ea15960bdb3f53d163913 100644 (file)
@@ -3844,6 +3844,24 @@ port <port>
   inetd for instance. This parameter is ignored if the "check" parameter is not
   set. See also the "addr" parameter.
 
+redir <prefix>
+  The "redir" parameter enables the redirection mode for all GET and HEAD
+  requests addressing this server. This means that instead of having HAProxy
+  forward the request to the server, it will send an "HTTP 302" response with
+  the "Location" header composed of this prefix immediately followed by the
+  requested URI beginning at the leading '/' of the path component. That means
+  that no trailing slash should be used after <prefix>. All invalid requests
+  will be rejected, and all non-GET or HEAD requests will be normally served by
+  the server. Note that since the response is completely forged, no header
+  mangling nor cookie insertion is possible in the respose. However, cookies in
+  requests are still analysed, making this solution completely usable to direct
+  users to a remote location in case of local disaster. Main use consists in
+  increasing bandwidth for static servers by having the clients directly
+  connect to them. Note: never use a relative location here, it would cause a
+  loop between the client and HAProxy!
+
+  Example :  server srv1 192.168.1.1:80 redir http://image1.mydomain.com check
+
 rise <count>
   The "rise" parameter states that a server will be considered as operational
   after <count> consecutive successful health checks. This value defaults to 2
index cfc4d7d832515b761174f5355d66569d3e2855b8..c93236cb45a5984b27e00a3904c428354dd332b7 100644 (file)
@@ -2,7 +2,7 @@
   include/types/server.h
   This file defines everything related to servers.
 
-  Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -73,7 +73,9 @@ struct server {
        int state;                              /* server state (SRV_*) */
        int prev_state;                         /* server state before last change (SRV_*) */
        int  cklen;                             /* the len of the cookie, to speed up checks */
+       int rdr_len;                            /* the length of the redirection prefix */
        char *cookie;                           /* the id set in the cookie */
+       char *rdr_pfx;                          /* the redirection prefix */
 
        struct proxy *proxy;                    /* the proxy this server belongs to */
        int cur_sess, cur_sess_max;             /* number of currently active sessions (including syn_sent) */
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 a9d42a64cd0e23d8e3352daef600dd03f38c9de2..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)) {
@@ -1189,7 +1203,6 @@ int connect_server(struct session *s)
                struct sockaddr_in *remote = NULL;
                int ret, flags = 0;
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
                switch (s->srv->state & SRV_TPROXY_MASK) {
                case SRV_TPROXY_ADDR:
                        remote = (struct sockaddr_in *)&s->srv->tproxy_addr;
@@ -1204,7 +1217,6 @@ int connect_server(struct session *s)
                        remote = (struct sockaddr_in *)&s->cli_addr;
                        break;
                }
-#endif
                ret = tcpv4_bind_socket(fd, flags, &s->srv->source_addr, remote);
                if (ret) {
                        close(fd);
@@ -1228,7 +1240,6 @@ int connect_server(struct session *s)
                struct sockaddr_in *remote = NULL;
                int ret, flags = 0;
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
                switch (s->be->options & PR_O_TPXY_MASK) {
                case PR_O_TPXY_ADDR:
                        remote = (struct sockaddr_in *)&s->be->tproxy_addr;
@@ -1243,7 +1254,7 @@ int connect_server(struct session *s)
                        remote = (struct sockaddr_in *)&s->cli_addr;
                        break;
                }
-#endif
+
                ret = tcpv4_bind_socket(fd, flags, &s->be->source_addr, remote);
                if (ret) {
                        close(fd);
index 1815ed4871057d83589d351062f6b368cadae161..9329df3eb3c1a19900d9f293e27c5149920d253e 100644 (file)
@@ -1547,6 +1547,11 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                                newsrv->cklen = strlen(args[cur_arg + 1]);
                                cur_arg += 2;
                        }
+                       else if (!strcmp(args[cur_arg], "redir")) {
+                               newsrv->rdr_pfx = strdup(args[cur_arg + 1]);
+                               newsrv->rdr_len = strlen(args[cur_arg + 1]);
+                               cur_arg += 2;
+                       }
                        else if (!strcmp(args[cur_arg], "rise")) {
                                newsrv->rise = atol(args[cur_arg + 1]);
                                newsrv->health = newsrv->rise;
@@ -1691,7 +1696,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                                return -1;
                        }
                        else {
-                               Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'fastinter', 'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
+                               Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'redir', 'check', 'inter', 'fastinter', 'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
                                      file, linenum, newsrv->id);
                                return -1;
                        }
@@ -2904,6 +2909,19 @@ int readcfgfile(const char *file)
                if (curproxy->options & PR_O_LOGASAP)
                        curproxy->to_log &= ~LW_BYTES;
 
+               /*
+                * ensure that we're not cross-dressing a TCP server into HTTP.
+                */
+               newsrv = curproxy->srv;
+               while (newsrv != NULL) {
+                       if ((curproxy->mode != PR_MODE_HTTP) && (newsrv->rdr_len || newsrv->cklen)) {
+                               Alert("parsing %s, %s '%s' : server cannot have cookie or redirect prefix in non-HTTP mode.\n",
+                                     file, proxy_type_str(curproxy), curproxy->id, linenum);
+                               goto err;
+                       }
+                       newsrv = newsrv->next;
+               }
+
                /*
                 * If this server supports a maxconn parameter, it needs a dedicated
                 * tasks to fill the emptied slots when a connection leaves.
index 0fbe166789bc7d42d76ba12cd65ae88bac642e1d..124f40c53621484fc1b49d220d31f2f1339ebebb 100644 (file)
@@ -422,12 +422,10 @@ void process_chk(struct task *t, struct timeval *next)
                                        struct sockaddr_in *remote = NULL;
                                        int ret, flags = 0;
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
                                        if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) {
                                                remote = (struct sockaddr_in *)&s->tproxy_addr;
                                                flags  = 3;
                                        }
-#endif
                                        ret = tcpv4_bind_socket(fd, flags, &s->source_addr, remote);
                                        if (ret) {
                                                s->result |= SRV_CHK_ERROR;
@@ -447,12 +445,10 @@ void process_chk(struct task *t, struct timeval *next)
                                        struct sockaddr_in *remote = NULL;
                                        int ret, flags = 0;
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
                                        if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) {
                                                remote = (struct sockaddr_in *)&s->proxy->tproxy_addr;
                                                flags  = 3;
                                        }
-#endif
                                        ret = tcpv4_bind_socket(fd, flags, &s->proxy->source_addr, remote);
                                        if (ret) {
                                                s->result |= SRV_CHK_ERROR;
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 '/' ! */
diff --git a/tests/test-redir.cfg b/tests/test-redir.cfg
new file mode 100644 (file)
index 0000000..4aebafd
--- /dev/null
@@ -0,0 +1,35 @@
+# This is a test configuration.
+# It makes use of a farm built from 4 servers, 3 of which are remote and
+# referenced only via an HTTP redirect (302), and the 4th one is normal.
+# HTTP requests different from GET/HEAD should reach the servers directly
+# while GET/HEAD should get redirected for the 3 first ones.
+
+global
+       #log /dev/log local0
+       maxconn    1000
+        stats socket /tmp/sock1 mode 600
+        stats timeout 3000
+        stats maxconn 2000
+
+listen  sample1
+       #log global
+       #option httplog
+        mode       http
+        retries    1
+        option redispatch
+        contimeout 1000
+        clitimeout 5000
+        srvtimeout 5000
+        maxconn    40000
+        bind       :8080
+        #balance    source
+        balance    roundrobin
+       option     allbackups
+        server     rdr1 127.0.0.1:80 redir http://static1:80 weight 10 check inter 1000 fall 4
+        server     rdr2 127.0.0.2:80 redir http://static2:80 weight 20 check inter 1000 fall 4
+        server     rdr3 127.0.0.3:80 redir http://static3:80 weight 30 check inter 1000 fall 4
+        server     dir4 127.0.0.4:80 redir weight 30 check inter 1000 fall 4
+        option     httpclose
+       stats      uri /stats
+       stats      refresh 5
+