[MEDIUM] change server check result to a bit field

A server check currently returns either -1 or 1. This is not very
convenient to enhance the health-checks system. Let's use flags
instead.
This commit is contained in:
Willy Tarreau 2007-11-30 08:33:21 +01:00
parent 5eb1a9033a
commit c7dd71ae5b
2 changed files with 69 additions and 48 deletions

View File

@ -54,6 +54,11 @@
#define SRV_STATUS_FULL 3 /* the/all server(s) are saturated */ #define SRV_STATUS_FULL 3 /* the/all server(s) are saturated */
#define SRV_STATUS_QUEUED 4 /* the/all server(s) are saturated but the connection was queued */ #define SRV_STATUS_QUEUED 4 /* the/all server(s) are saturated but the connection was queued */
/* bits for s->result used for health-checks */
#define SRV_CHK_UNKNOWN 0x0000 /* initialized to this by default */
#define SRV_CHK_ERROR 0x0001 /* error encountered during the check; has precedence */
#define SRV_CHK_RUNNING 0x0002 /* server seen as running */
#define SRV_CHK_DISABLE 0x0004 /* server returned a "disable" code */
struct server { struct server {
struct server *next; struct server *next;
@ -81,7 +86,7 @@ struct server {
int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */ int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
int rise, fall; /* time in iterations */ int rise, fall; /* time in iterations */
int inter; /* time in milliseconds */ int inter; /* time in milliseconds */
int result; /* 0 = connect OK, -1 = connect KO */ int result; /* health-check result : SRV_CHK_* */
int curfd; /* file desc used for current test, or -1 if not in test */ int curfd; /* file desc used for current test, or -1 if not in test */
char *id; /* just for identification */ char *id; /* just for identification */

View File

@ -109,8 +109,7 @@ static void set_server_down(struct server *s)
/* /*
* This function is used only for server health-checks. It handles * This function is used only for server health-checks. It handles
* the connection acknowledgement. If the proxy requires HTTP health-checks, * the connection acknowledgement. If the proxy requires HTTP health-checks,
* it sends the request. In other cases, it returns 1 in s->result if the * it sends the request. In other cases, it fills s->result with SRV_CHK_*.
* socket is OK, or -1 if an error occured.
* The function itself returns 0 if it needs some polling before being called * The function itself returns 0 if it needs some polling before being called
* again, otherwise 1. * again, otherwise 1.
*/ */
@ -125,7 +124,7 @@ static int event_srv_chk_w(int fd)
/* here, we know that the connection is established */ /* here, we know that the connection is established */
if (s->result != -1) { if (!(s->result & SRV_CHK_ERROR)) {
/* we don't want to mark 'UP' a server on which we detected an error earlier */ /* we don't want to mark 'UP' a server on which we detected an error earlier */
if ((s->proxy->options & PR_O_HTTP_CHK) || if ((s->proxy->options & PR_O_HTTP_CHK) ||
(s->proxy->options & PR_O_SSL3_CHK) || (s->proxy->options & PR_O_SSL3_CHK) ||
@ -181,7 +180,7 @@ static int event_srv_chk_w(int fd)
goto out_error; goto out_error;
/* good TCP connection is enough */ /* good TCP connection is enough */
s->result = 1; s->result |= SRV_CHK_RUNNING;
goto out_wakeup; goto out_wakeup;
} }
} }
@ -197,7 +196,7 @@ static int event_srv_chk_w(int fd)
fdtab[fd].ev &= ~FD_POLL_WR; fdtab[fd].ev &= ~FD_POLL_WR;
return 0; return 0;
out_error: out_error:
s->result = -1; s->result |= SRV_CHK_ERROR;
fdtab[fd].state = FD_STERROR; fdtab[fd].state = FD_STERROR;
goto out_wakeup; goto out_wakeup;
} }
@ -205,31 +204,32 @@ static int event_srv_chk_w(int fd)
/* /*
* This function is used only for server health-checks. It handles the server's * This function is used only for server health-checks. It handles the server's
* reply to an HTTP request or SSL HELLO. It returns 1 in s->result if the * reply to an HTTP request or SSL HELLO. It sets s->result to SRV_CHK_RUNNING
* server replies HTTP 2xx or 3xx (valid responses), or if it returns at least * if an HTTP server replies HTTP 2xx or 3xx (valid responses), if an SMTP
* 5 bytes in response to SSL HELLO. The principle is that this is enough to * server returns 2xx, or if an SSL server returns at least 5 bytes in response
* distinguish between an SSL server and a pure TCP relay. All other cases will * to an SSL HELLO (the principle is that this is enough to distinguish between
* return -1. The function returns 0 if it needs to be called again after some * an SSL server and a pure TCP relay). All other cases will set s->result to
* polling, otherwise non-zero.. * SRV_CHK_ERROR. The function returns 0 if it needs to be called again after
* some polling, otherwise non-zero..
*/ */
static int event_srv_chk_r(int fd) static int event_srv_chk_r(int fd)
{ {
__label__ out_wakeup; __label__ out_wakeup;
int len, result; int len;
struct task *t = fdtab[fd].owner; struct task *t = fdtab[fd].owner;
struct server *s = t->context; struct server *s = t->context;
int skerr; int skerr;
socklen_t lskerr = sizeof(skerr); socklen_t lskerr = sizeof(skerr);
result = len = -1; len = -1;
if (unlikely(fdtab[fd].state == FD_STERROR || if (unlikely((s->result & SRV_CHK_ERROR) ||
(fdtab[fd].state == FD_STERROR) ||
(fdtab[fd].ev & FD_POLL_ERR) || (fdtab[fd].ev & FD_POLL_ERR) ||
(getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == -1) || (getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == -1) ||
(skerr != 0))) { (skerr != 0))) {
/* in case of TCP only, this tells us if the connection failed */ /* in case of TCP only, this tells us if the connection failed */
s->result = -1; s->result |= SRV_CHK_ERROR;
fdtab[fd].state = FD_STERROR;
goto out_wakeup; goto out_wakeup;
} }
@ -248,28 +248,44 @@ static int event_srv_chk_r(int fd)
return 0; return 0;
} }
if ((s->proxy->options & PR_O_HTTP_CHK) && (len >= sizeof("HTTP/1.0 000")) && /* Note: the response will only be accepted if read at once */
(memcmp(trash, "HTTP/1.", 7) == 0) && (trash[9] == '2' || trash[9] == '3')) { if (s->proxy->options & PR_O_HTTP_CHK) {
/* HTTP/1.X 2xx or 3xx */ /* Check if the server speaks HTTP 1.X */
result = 1; if ((len < strlen("HTTP/1.0 000\r")) ||
} (memcmp(trash, "HTTP/1.", 7) != 0)) {
else if ((s->proxy->options & PR_O_SSL3_CHK) && (len >= 5) && s->result |= SRV_CHK_ERROR;
(trash[0] == 0x15 || trash[0] == 0x16)) { goto out_wakeup;
/* SSLv3 alert or handshake */
result = 1;
}
else if ((s->proxy->options & PR_O_SMTP_CHK) && (len >= 3) &&
(trash[0] == '2')) /* 2xx (should be 250) */ {
result = 1;
} }
if (result == -1) /* check the reply : HTTP/1.X 2xx and 3xx are OK */
fdtab[fd].state = FD_STERROR; if (trash[9] == '2' || trash[9] == '3')
s->result |= SRV_CHK_RUNNING;
if (s->result != -1) else
s->result = result; s->result |= SRV_CHK_ERROR;
}
else if (s->proxy->options & PR_O_SSL3_CHK) {
/* Check for SSLv3 alert or handshake */
if ((len >= 5) && (trash[0] == 0x15 || trash[0] == 0x16))
s->result |= SRV_CHK_RUNNING;
else
s->result |= SRV_CHK_ERROR;
}
else if (s->proxy->options & PR_O_SMTP_CHK) {
/* Check for SMTP code 2xx (should be 250) */
if ((len >= 3) && (trash[0] == '2'))
s->result |= SRV_CHK_RUNNING;
else
s->result |= SRV_CHK_ERROR;
}
else {
/* other checks are valid if the connection succeeded anyway */
s->result |= SRV_CHK_RUNNING;
}
out_wakeup: out_wakeup:
if (s->result & SRV_CHK_ERROR)
fdtab[fd].state = FD_STERROR;
EV_FD_CLR(fd, DIR_RD); EV_FD_CLR(fd, DIR_RD);
task_wakeup(t); task_wakeup(t);
fdtab[fd].ev &= ~FD_POLL_RD; fdtab[fd].ev &= ~FD_POLL_RD;
@ -312,7 +328,7 @@ void process_chk(struct task *t, struct timeval *next)
} }
/* we'll initiate a new check */ /* we'll initiate a new check */
s->result = 0; /* no result yet */ s->result = SRV_CHK_UNKNOWN; /* no result yet */
if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) { if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
if ((fd < global.maxsock) && if ((fd < global.maxsock) &&
(fcntl(fd, F_SETFL, O_NONBLOCK) != -1) && (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
@ -343,7 +359,7 @@ void process_chk(struct task *t, struct timeval *next)
if (bind(fd, (struct sockaddr *)&s->source_addr, sizeof(s->source_addr)) == -1) { if (bind(fd, (struct sockaddr *)&s->source_addr, sizeof(s->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n", Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
s->proxy->id, s->id); s->proxy->id, s->id);
s->result = -1; s->result |= SRV_CHK_ERROR;
} }
#ifdef CONFIG_HAP_CTTPROXY #ifdef CONFIG_HAP_CTTPROXY
if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) { if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) {
@ -362,7 +378,7 @@ void process_chk(struct task *t, struct timeval *next)
setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) { setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n", Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n",
s->proxy->id, s->id); s->proxy->id, s->id);
s->result = -1; s->result |= SRV_CHK_ERROR;
} }
} }
#endif #endif
@ -372,7 +388,7 @@ void process_chk(struct task *t, struct timeval *next)
if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) { if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for %s '%s'. Aborting.\n", Alert("Cannot bind to source address before connect() for %s '%s'. Aborting.\n",
proxy_type_str(s->proxy), s->proxy->id); proxy_type_str(s->proxy), s->proxy->id);
s->result = -1; s->result |= SRV_CHK_ERROR;
} }
#ifdef CONFIG_HAP_CTTPROXY #ifdef CONFIG_HAP_CTTPROXY
if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) { if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) {
@ -391,13 +407,13 @@ void process_chk(struct task *t, struct timeval *next)
setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) { setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
Alert("Cannot bind to tproxy source address before connect() for %s '%s'. Aborting.\n", Alert("Cannot bind to tproxy source address before connect() for %s '%s'. Aborting.\n",
proxy_type_str(s->proxy), s->proxy->id); proxy_type_str(s->proxy), s->proxy->id);
s->result = -1; s->result |= SRV_CHK_ERROR;
} }
} }
#endif #endif
} }
if (!s->result) { if (s->result == SRV_CHK_UNKNOWN) {
if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) { if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
/* OK, connection in progress or established */ /* OK, connection in progress or established */
@ -425,14 +441,14 @@ void process_chk(struct task *t, struct timeval *next)
return; return;
} }
else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) { else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
s->result = -1; /* a real error */ s->result |= SRV_CHK_ERROR; /* a real error */
} }
} }
} }
close(fd); /* socket creation error */ close(fd); /* socket creation error */
} }
if (!s->result) { /* nothing done */ if (s->result == SRV_CHK_UNKNOWN) { /* nothing done */
//fprintf(stderr, "process_chk: 6\n"); //fprintf(stderr, "process_chk: 6\n");
while (tv_isle(&t->expire, &now)) while (tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter); tv_ms_add(&t->expire, &t->expire, s->inter);
@ -456,7 +472,7 @@ void process_chk(struct task *t, struct timeval *next)
else { else {
//fprintf(stderr, "process_chk: 8\n"); //fprintf(stderr, "process_chk: 8\n");
/* there was a test running */ /* there was a test running */
if (s->result > 0) { /* good server detected */ if ((s->result & (SRV_CHK_ERROR|SRV_CHK_RUNNING)) == SRV_CHK_RUNNING) { /* good server detected */
//fprintf(stderr, "process_chk: 9\n"); //fprintf(stderr, "process_chk: 9\n");
if (s->health < s->rise + s->fall - 1) { if (s->health < s->rise + s->fall - 1) {
@ -521,7 +537,7 @@ void process_chk(struct task *t, struct timeval *next)
tv_ms_add(&t->expire, &now, s->inter + rv); tv_ms_add(&t->expire, &now, s->inter + rv);
goto new_chk; goto new_chk;
} }
else if (s->result < 0 || tv_isle(&t->expire, &now)) { else if ((s->result & SRV_CHK_ERROR) || tv_isle(&t->expire, &now)) {
//fprintf(stderr, "process_chk: 10\n"); //fprintf(stderr, "process_chk: 10\n");
/* failure or timeout detected */ /* failure or timeout detected */
if (s->health > s->rise) { if (s->health > s->rise) {
@ -542,10 +558,10 @@ void process_chk(struct task *t, struct timeval *next)
tv_ms_add(&t->expire, &now, s->inter + rv); tv_ms_add(&t->expire, &now, s->inter + rv);
goto new_chk; goto new_chk;
} }
/* if result is 0 and there's no timeout, we have to wait again */ /* if result is unknown and there's no timeout, we have to wait again */
} }
//fprintf(stderr, "process_chk: 11\n"); //fprintf(stderr, "process_chk: 11\n");
s->result = 0; s->result = SRV_CHK_UNKNOWN;
task_queue(t); /* restore t to its place in the task list */ task_queue(t); /* restore t to its place in the task list */
*next = t->expire; *next = t->expire;
out: out: