Revert "[BUILD] backend.c and checks.c did not build without tproxy !"
This reverts commit 3c3c0122f84d72eae1c4ef4b1826bfdbef7d95e6.
This commit was buggy as it also removed previous tproxy changes !
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 0e40988..900b9a7 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3844,6 +3844,24 @@
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
diff --git a/include/types/server.h b/include/types/server.h
index cfc4d7d..c93236c 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -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 @@
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) */
diff --git a/include/types/session.h b/include/types/session.h
index 46569a5..9437198 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -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) */
diff --git a/src/backend.c b/src/backend.c
index a9d42a6..20e3a04 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1059,6 +1059,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
remote = (struct sockaddr_in *)&s->cli_addr;
break;
}
-#endif
+
ret = tcpv4_bind_socket(fd, flags, &s->be->source_addr, remote);
if (ret) {
close(fd);
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 1815ed4..9329df3 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1547,6 +1547,11 @@
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 @@
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;
}
@@ -2905,6 +2910,19 @@
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.
*/
diff --git a/src/checks.c b/src/checks.c
index 0fbe166..124f40c 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -422,12 +422,10 @@
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 @@
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;
diff --git a/src/proto_http.c b/src/proto_http.c
index 16b2f07..429f571 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -592,6 +592,54 @@
}
+/* 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 @@
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 @@
/* 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
index 0000000..4aebafd
--- /dev/null
+++ b/tests/test-redir.cfg
@@ -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
+