[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.
diff --git a/include/types/server.h b/include/types/server.h
index 4258502..47c152c 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -54,6 +54,11 @@
#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 */
+/* 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 *next;
@@ -81,7 +86,7 @@
int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
int rise, fall; /* time in iterations */
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 */
char *id; /* just for identification */
diff --git a/src/checks.c b/src/checks.c
index 5fe98ab..e02b891 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -109,8 +109,7 @@
/*
* This function is used only for server health-checks. It handles
* 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
- * socket is OK, or -1 if an error occured.
+ * it sends the request. In other cases, it fills s->result with SRV_CHK_*.
* The function itself returns 0 if it needs some polling before being called
* again, otherwise 1.
*/
@@ -125,7 +124,7 @@
/* 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 */
if ((s->proxy->options & PR_O_HTTP_CHK) ||
(s->proxy->options & PR_O_SSL3_CHK) ||
@@ -181,7 +180,7 @@
goto out_error;
/* good TCP connection is enough */
- s->result = 1;
+ s->result |= SRV_CHK_RUNNING;
goto out_wakeup;
}
}
@@ -197,7 +196,7 @@
fdtab[fd].ev &= ~FD_POLL_WR;
return 0;
out_error:
- s->result = -1;
+ s->result |= SRV_CHK_ERROR;
fdtab[fd].state = FD_STERROR;
goto out_wakeup;
}
@@ -205,31 +204,32 @@
/*
* 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
- * server replies HTTP 2xx or 3xx (valid responses), or if it returns at least
- * 5 bytes in response to SSL HELLO. The principle is that this is enough to
- * distinguish between an SSL server and a pure TCP relay. All other cases will
- * return -1. The function returns 0 if it needs to be called again after some
- * polling, otherwise non-zero..
+ * reply to an HTTP request or SSL HELLO. It sets s->result to SRV_CHK_RUNNING
+ * if an HTTP server replies HTTP 2xx or 3xx (valid responses), if an SMTP
+ * server returns 2xx, or if an SSL server returns at least 5 bytes in response
+ * to an SSL HELLO (the principle is that this is enough to distinguish between
+ * an SSL server and a pure TCP relay). All other cases will set s->result to
+ * 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)
{
__label__ out_wakeup;
- int len, result;
+ int len;
struct task *t = fdtab[fd].owner;
struct server *s = t->context;
int 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) ||
(getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == -1) ||
(skerr != 0))) {
/* in case of TCP only, this tells us if the connection failed */
- s->result = -1;
- fdtab[fd].state = FD_STERROR;
+ s->result |= SRV_CHK_ERROR;
goto out_wakeup;
}
@@ -248,28 +248,44 @@
return 0;
}
+ /* Note: the response will only be accepted if read at once */
+ if (s->proxy->options & PR_O_HTTP_CHK) {
+ /* Check if the server speaks HTTP 1.X */
+ if ((len < strlen("HTTP/1.0 000\r")) ||
+ (memcmp(trash, "HTTP/1.", 7) != 0)) {
+ s->result |= SRV_CHK_ERROR;
+ goto out_wakeup;
+ }
+
- if ((s->proxy->options & PR_O_HTTP_CHK) && (len >= sizeof("HTTP/1.0 000")) &&
- (memcmp(trash, "HTTP/1.", 7) == 0) && (trash[9] == '2' || trash[9] == '3')) {
- /* HTTP/1.X 2xx or 3xx */
- result = 1;
+ /* check the reply : HTTP/1.X 2xx and 3xx are OK */
+ if (trash[9] == '2' || trash[9] == '3')
+ s->result |= SRV_CHK_RUNNING;
+ else
+ 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_SSL3_CHK) && (len >= 5) &&
- (trash[0] == 0x15 || trash[0] == 0x16)) {
- /* SSLv3 alert or handshake */
- result = 1;
+ 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 if ((s->proxy->options & PR_O_SMTP_CHK) && (len >= 3) &&
- (trash[0] == '2')) /* 2xx (should be 250) */ {
- result = 1;
+ else {
+ /* other checks are valid if the connection succeeded anyway */
+ s->result |= SRV_CHK_RUNNING;
}
- if (result == -1)
+ out_wakeup:
+ if (s->result & SRV_CHK_ERROR)
fdtab[fd].state = FD_STERROR;
- if (s->result != -1)
- s->result = result;
-
- out_wakeup:
EV_FD_CLR(fd, DIR_RD);
task_wakeup(t);
fdtab[fd].ev &= ~FD_POLL_RD;
@@ -312,7 +328,7 @@
}
/* 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 < global.maxsock) &&
(fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
@@ -343,7 +359,7 @@
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",
s->proxy->id, s->id);
- s->result = -1;
+ s->result |= SRV_CHK_ERROR;
}
#ifdef CONFIG_HAP_CTTPROXY
if ((s->state & SRV_TPROXY_MASK) == SRV_TPROXY_ADDR) {
@@ -362,7 +378,7 @@
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",
s->proxy->id, s->id);
- s->result = -1;
+ s->result |= SRV_CHK_ERROR;
}
}
#endif
@@ -372,7 +388,7 @@
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",
proxy_type_str(s->proxy), s->proxy->id);
- s->result = -1;
+ s->result |= SRV_CHK_ERROR;
}
#ifdef CONFIG_HAP_CTTPROXY
if ((s->proxy->options & PR_O_TPXY_MASK) == PR_O_TPXY_ADDR) {
@@ -391,13 +407,13 @@
setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
Alert("Cannot bind to tproxy source address before connect() for %s '%s'. Aborting.\n",
proxy_type_str(s->proxy), s->proxy->id);
- s->result = -1;
+ s->result |= SRV_CHK_ERROR;
}
}
#endif
}
- if (!s->result) {
+ if (s->result == SRV_CHK_UNKNOWN) {
if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
/* OK, connection in progress or established */
@@ -425,14 +441,14 @@
return;
}
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 */
}
- if (!s->result) { /* nothing done */
+ if (s->result == SRV_CHK_UNKNOWN) { /* nothing done */
//fprintf(stderr, "process_chk: 6\n");
while (tv_isle(&t->expire, &now))
tv_ms_add(&t->expire, &t->expire, s->inter);
@@ -456,7 +472,7 @@
else {
//fprintf(stderr, "process_chk: 8\n");
/* 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");
if (s->health < s->rise + s->fall - 1) {
@@ -521,7 +537,7 @@
tv_ms_add(&t->expire, &now, s->inter + rv);
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");
/* failure or timeout detected */
if (s->health > s->rise) {
@@ -542,10 +558,10 @@
tv_ms_add(&t->expire, &now, s->inter + rv);
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");
- s->result = 0;
+ s->result = SRV_CHK_UNKNOWN;
task_queue(t); /* restore t to its place in the task list */
*next = t->expire;
out: