[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

@ -67,42 +67,44 @@ struct proxy {
int state; /* proxy state */
struct sockaddr_in dispatch_addr; /* the default address to connect to */
struct server *srv; /* known servers */
int srv_act, srv_bck; /* # of running servers */
int tot_wact, tot_wbck; /* total weights of active and backup servers */
int srv_act, srv_bck; /* # of running servers */
int tot_wact, tot_wbck; /* total weights of active and backup servers */
struct server **srv_map; /* the server map used to apply weights */
int srv_map_sz; /* the size of the effective server map */
int srv_rr_idx; /* next server to be elected in round robin mode */
int srv_map_sz; /* the size of the effective server map */
int srv_rr_idx; /* next server to be elected in round robin mode */
char *cookie_name; /* name of the cookie to look for */
int cookie_len; /* strlen(cookie_name), computed only once */
char *appsession_name; /* name of the cookie to look for */
char *appsession_name; /* name of the cookie to look for */
int appsession_name_len; /* strlen(appsession_name), computed only once */
int appsession_len; /* length of the appsession cookie value to be used */
int appsession_len; /* length of the appsession cookie value to be used */
int appsession_timeout;
CHTbl htbl_proxy; /* Per Proxy hashtable */
char *capture_name; /* beginning of the name of the cookie to capture */
int capture_namelen; /* length of the cookie name to match */
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 */
int clitimeout; /* client I/O timeout (in milliseconds) */
int srvtimeout; /* server I/O timeout (in milliseconds) */
int contimeout; /* connect timeout (in milliseconds) */
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) */
char *id; /* proxy id */
struct list pendconns; /* pending connections with no server assigned yet */
int nbpend, nbpend_max; /* number of pending connections with no server assigned yet */
int totpend; /* total number of pending connections on this instance (for stats) */
struct list pendconns; /* pending connections with no server assigned yet */
int nbpend, nbpend_max; /* number of pending connections with no server assigned yet */
int totpend; /* total number of pending connections on this instance (for stats) */
unsigned int nbconn, nbconn_max; /* # of active sessions */
unsigned int cum_conn; /* cumulated number of processed sessions */
unsigned int maxconn; /* max # of active sessions */
unsigned int cum_conn; /* cumulated number of processed sessions */
unsigned int maxconn; /* max # of active sessions */
unsigned failed_conns, failed_resp; /* failed connect() and responses */
unsigned failed_secu; /* blocked responses because of security concerns */
unsigned failed_secu; /* blocked responses because of security concerns */
int conn_retries; /* maximum number of connect retries */
int options; /* PR_O_REDISP, PR_O_TRANSP, ... */
int options; /* PR_O_REDISP, PR_O_TRANSP, ... */
int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
struct proxy *next;
struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
signed char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
int loglev1, loglev2; /* log level for each server, 7 by default */
struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
signed char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
int loglev1, loglev2; /* log level for each server, 7 by default */
int to_log; /* things to be logged (LW_*) */
struct timeval stop_time; /* date to stop listening, when stopping != 0 */
int nb_reqadd, nb_rspadd;

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,