[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 /" */