[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:
parent
7a58a72e85
commit
9c33612f53
|
@ -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) */
|
||||
|
|
|
@ -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)) {
|
||||
|
|
134
src/proto_http.c
134
src/proto_http.c
|
@ -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 '/' ! */
|
||||
|
|
Loading…
Reference in New Issue