[MEDIUM] completely implement the server redirection method

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.
This commit is contained in:
Willy Tarreau 2008-02-13 00:45:24 +01:00
parent 7a58a72e85
commit 9c33612f53
3 changed files with 116 additions and 34 deletions

View 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) */

View 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)) {

View 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 '/' ! */