[MEDIUM] implemented the 'monitor-uri' keyword.

It is used to test haproxy's status with an HTTP request to which
it will reply with HTTP/1.0 200 OK.
This commit is contained in:
Willy Tarreau 2006-07-09 08:22:27 +02:00
parent f3c692090e
commit 1c47f85292
6 changed files with 155 additions and 23 deletions

19
ROADMAP
View File

@ -35,6 +35,12 @@
srv->effective_maxconn =
max(srv->maxconn * px->nbsess / px->maxconn, srv->minconn)
1.2.15 :
+ monitor-uri : specify an URI for which we will always return 'HTTP/1.0 200'
and never forward nor log it.
+ option ssl-hello-chk : send SSLv3 client hello messages to check the servers
1.3 :
- remove unused STATTIME
@ -75,7 +81,6 @@
- clarify licence by adding a 'MODULE_LICENCE("GPL")' or something equivalent.
- handle half-closed connections better (cli/srv would not distinguish
DATA/SHUTR/SHUTW, it would be a session flag which would tell shutr/shutw).
Check how it got changed in httpterm.
@ -93,3 +98,15 @@
- verify if it would be worth implementing an epoll_ctl_batch() for Linux
- balance LC/WLC (patch available)
- option minservers XXX : activates some backup servers when active servers
are insufficient
- monitor minservers XXX : monitor-net and monitor-uri could report a failure
when the number of active servers is below this threshold.
- option smtp-chk : use SMTP health checks (avoid logs if possible)
- new keyword 'check' : check http xxx, check smtp xxx, check ssl-hello

View File

@ -494,6 +494,27 @@ Example :
monitor-net 192.168.1.252/31 # L4 load-balancers on .252 and .253
When the system executing the checks is located behind a proxy, the monitor-net
keyword cannot be used because haproxy will always see the proxy's address. To
overcome this limitation, version 1.2.15 brought the 'monitor-uri' keyword. It
defines an URI which will not be forwarded nor logged, but for which haproxy
will immediately send an "HTTP/1.0 200 OK" response. This makes it possible to
check the validity of the reverse-proxy->haproxy chain with one request. It can
be used in HTTPS checks in front of an stunnel -> haproxy combination for
instance. Obviously, this keyword is only valid in HTTP mode, otherwise there
is no notion of URI. Note that the method and HTTP versions are simply ignored.
Example :
---------
listen stunnel_backend :8080
mode http
balance roundrobin
server web1 192.168.1.10:80 check
server web2 192.168.1.11:80 check
monitor-uri /haproxy_test
2.3) Limiting the number of simultaneous connections
----------------------------------------------------
The 'maxconn' parameter allows a proxy to refuse connections above a certain

View File

@ -520,6 +520,28 @@ Exemple :
monitor-net 192.168.1.252/31 # L4 load-balancers on .252 and .253
Lorsque le système effectuant les tests est situé derrière un proxy, le mot-clé
'monitor-net' n'est pas utilisable du fait que haproxy verra toujours la même
adresse pour le proxy. Pour pallier à cette limitation, la version 1.2.15 a
apporté le mot-clé 'monitor-uri'. Il définit une URI qui ne sera ni retransmise
ni logée, mais pour laquelle haproxy retournera immédiatement une réponse
"HTTP/1.0 200 OK". Cela rend possibles les tests de validité d'une chaîne
reverse-proxy->haproxy en une requête HTTP. Cela peut être utilisé pour valider
une combinaision de stunnel+haproxy à l'aide de tests HTTPS par exemple. Bien
entendu, ce mot-clé n'est valide qu'en mode HTTP, sinon il n'y a pas de notion
d'URI. Noter que la méthode et la version HTTP sont simplement ignorées.
Exemple :
---------
listen stunnel_backend :8080
mode http
balance roundrobin
server web1 192.168.1.10:80 check
server web2 192.168.1.11:80 check
monitor-uri /haproxy_test
2.3) Limitation du nombre de connexions simultanées
---------------------------------------------------
Le paramètre "maxconn" permet de fixer la limite acceptable en nombre de

View File

@ -83,6 +83,8 @@ struct proxy {
int capture_namelen; /* length of the cookie name to match */
int capture_len; /* length of the string to be captured */
struct uri_auth *uri_auth; /* if non-NULL, the (list of) per-URI authentications */
char *monitor_uri; /* a special URI to which we respond with HTTP/200 OK */
int monitor_uri_len; /* length of the string above. 0 if unused */
int clitimeout; /* client I/O timeout (in milliseconds) */
int srvtimeout; /* server I/O timeout (in milliseconds) */
int contimeout; /* connect timeout (in milliseconds) */

View File

@ -521,6 +521,11 @@ int cfg_parse_listen(char *file, int linenum, char **args)
curproxy->source_addr = defproxy.source_addr;
curproxy->mon_net = defproxy.mon_net;
curproxy->mon_mask = defproxy.mon_mask;
if (defproxy.monitor_uri)
curproxy->monitor_uri = strdup(defproxy.monitor_uri);
curproxy->monitor_uri_len = defproxy.monitor_uri_len;
return 0;
}
else if (!strcmp(args[0], "defaults")) { /* use this one to assign default values */
@ -535,6 +540,7 @@ int cfg_parse_listen(char *file, int linenum, char **args)
if (defproxy.errmsg.msg502) free(defproxy.errmsg.msg502);
if (defproxy.errmsg.msg503) free(defproxy.errmsg.msg503);
if (defproxy.errmsg.msg504) free(defproxy.errmsg.msg504);
if (defproxy.monitor_uri) free(defproxy.monitor_uri);
/* we cannot free uri_auth because it might already be used */
init_default_instance();
curproxy = &defproxy;
@ -572,6 +578,24 @@ int cfg_parse_listen(char *file, int linenum, char **args)
curproxy->mon_net.s_addr &= curproxy->mon_mask.s_addr;
return 0;
}
else if (!strcmp(args[0], "monitor-uri")) { /* set the URI to intercept */
if (!*args[1]) {
Alert("parsing [%s:%d] : '%s' expects an URI.\n",
file, linenum, args[0]);
return -1;
}
if (curproxy->monitor_uri != NULL)
free(curproxy->monitor_uri);
curproxy->monitor_uri_len = strlen(args[1]) + 2; /* include leading and trailing spaces */
curproxy->monitor_uri = (char *)calloc(1, curproxy->monitor_uri_len + 1);
memcpy(curproxy->monitor_uri + 1, args[1], curproxy->monitor_uri_len - 2);
curproxy->monitor_uri[curproxy->monitor_uri_len-1] = curproxy->monitor_uri[0] = ' ';
curproxy->monitor_uri[curproxy->monitor_uri_len] = '\0';
return 0;
}
else if (!strcmp(args[0], "mode")) { /* sets the proxy mode */
if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
@ -1903,6 +1927,10 @@ int readcfgfile(char *file)
Warning("parsing %s : client regular expressions will be ignored for listener %s.\n",
file, curproxy->id);
}
if (curproxy->monitor_uri != NULL) {
Warning("parsing %s : monitor-uri will be ignored for listener %s.\n",
file, curproxy->id);
}
}
else if (curproxy->mode == PR_MODE_HTTP) { /* HTTP PROXY */
if ((curproxy->cookie_name != NULL) && ((newsrv = curproxy->srv) == NULL)) {

View File

@ -50,6 +50,15 @@
#include <proto/task.h>
/* This is used by remote monitoring */
const char *HTTP_200 =
"HTTP/1.0 200 OK\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"<html><body><h1>200 OK</h1>\nHAProxy: service ready.\n</body></html>\n";
/* Warning: this one is an sprintf() fmt string, with <realm> as its only argument */
const char *HTTP_401_fmt =
"HTTP/1.0 401 Unauthorized\r\n"
@ -180,7 +189,9 @@ int process_session(struct task *t)
s->logs.bytes = s->rep->total;
/* let's do a final log if we need it */
if (s->logs.logwait && (!(s->proxy->options & PR_O_NULLNOLOG) || s->req->total))
if (s->logs.logwait &&
!(s->flags & SN_MONITOR) &&
(!(s->proxy->options & PR_O_NULLNOLOG) || s->req->total))
sess_log(s);
/* the task MUST not be in the run queue anymore */
@ -282,6 +293,37 @@ int process_cli(struct session *t)
* whatever we want.
*/
/* check if the URI matches the monitor_uri. To speed-up the
* test, we include the leading and trailing spaces in the
* comparison.
*/
if ((t->proxy->monitor_uri_len != 0) &&
(t->req_line.len >= t->proxy->monitor_uri_len)) {
char *p = t->req_line.str;
int idx = 0;
/* skip the method so that we accept any method */
while (idx < t->req_line.len && p[idx] != ' ')
idx++;
p += idx;
if (t->req_line.len - idx >= t->proxy->monitor_uri_len &&
!memcmp(p, t->proxy->monitor_uri, t->proxy->monitor_uri_len)) {
/*
* We have found the monitor URI
*/
t->flags |= SN_MONITOR;
t->logs.status = 200;
client_retnclose(t, strlen(HTTP_200), HTTP_200);
if (!(t->flags & SN_ERR_MASK))
t->flags |= SN_ERR_PRXCOND;
if (!(t->flags & SN_FINST_MASK))
t->flags |= SN_FINST_R;
return 1;
}
}
if (t->proxy->uri_auth != NULL
&& t->req_line.len >= t->proxy->uri_auth->uri_len + 4) { /* +4 for "GET /" */
if (!memcmp(t->req_line.str + 4,