[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.
diff --git a/ROADMAP b/ROADMAP
index e529eb1..f5c638f 100644
--- a/ROADMAP
+++ b/ROADMAP
@@ -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
+
diff --git a/doc/haproxy-en.txt b/doc/haproxy-en.txt
index 14554f2..ea71807 100644
--- a/doc/haproxy-en.txt
+++ b/doc/haproxy-en.txt
@@ -494,6 +494,27 @@
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
diff --git a/doc/haproxy-fr.txt b/doc/haproxy-fr.txt
index dd02891..157028a 100644
--- a/doc/haproxy-fr.txt
+++ b/doc/haproxy-fr.txt
@@ -520,6 +520,28 @@
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
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 50c16ec..7a262da 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -67,42 +67,44 @@
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;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 5e4aa51..836dc9a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -521,6 +521,11 @@
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 @@
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 @@
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 @@
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)) {
diff --git a/src/proto_http.c b/src/proto_http.c
index e1a7d17..d4e9764 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -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 @@
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 */
@@ -281,6 +292,37 @@
* and that unwanted requests have been filtered out. We can do
* 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 /" */