* released 1.1.11
* fixed multi-cookie handling in client request to allow clean deletion
  in insert+indirect mode. Now, only the server cookie is deleted and not
  all the header. Should now be compliant to RFC2109.
* added a "nocache" option to "cookie" to specify that we explicitly want
  to add a "cache-control" header when we add a cookie.
  It is also possible to add an "Expires: <old-date>" to keep compatibility
  with old/broken caches.
* some doc and examples cleanups
diff --git a/NOTES b/NOTES
index 13b19ba..78de2c1 100644
--- a/NOTES
+++ b/NOTES
@@ -12,10 +12,8 @@
   * full HTTP log with destination server ID, req and resp time.
   * source address of outgoing connections
 1.1.8 -> 1.1.9
-  - handle parametrable HTTP health-checks replies
-  - differentiate http headers and http uris
-  - support environment variables in config file
-  - support keep-alive
 1.1.9 -> 1.1.10
-  - automatically remove client cookie in insert+indirect mode
-
+  * automatically remove client cookie in insert+indirect mode
+1.1.10 -> 1.1.11
+  * support multi-cookie as described in RFC2109
+  * added "nocache" option to prevent caches from storing cookies.
diff --git a/TODO b/TODO
index d690e70..9723680 100644
--- a/TODO
+++ b/TODO
@@ -18,5 +18,12 @@
   - compter les matches
   - si match(n) & ([n].cpt > [n-1].cpt) & ([n].action == [n-1].action), swap(n,n-1)
   - régulièrement, diviser tous les compteurs (lors d'un dépassement par exemple)
-- filtrage sur l'adresse IP source
+- filtrage sur l'adresse IP source, et stocker le pointeur sur la dernière regex
+  matchée dans la "session" pour accélérer les regex.
 - gestion keep-alive
+
+- handle parametrable HTTP health-checks replies
+- differentiate http headers and http uris
+- support environment variables in config file
+- support keep-alive
+
diff --git a/doc/haproxy.txt b/doc/haproxy.txt
index 36c3c99..23c2a0e 100644
--- a/doc/haproxy.txt
+++ b/doc/haproxy.txt
@@ -1,9 +1,9 @@
 
          		     H A - P r o x y
          		     ---------------
-         		      version 1.1.10
+         		      version 1.1.11
 			      willy tarreau
-			       2002/05/10
+			       2002/06/11
 
 ================
 | Introduction |
@@ -417,7 +417,29 @@
 'balance'.
 
 
-2.8) Définition du nom du cookie
+2.8) Adresse de sortie
+----------------------
+Il est possible de forcer l'adresse utilisée pour établir les connexions
+vers les serveurs à l'aide du paramètre "source". Il est même possible de
+forcer le port, bien que cette fonctionnalité se limite à des usages très
+spécifiques. C'est particulièrement utile en cas d'adressage multiple, et
+plus généralement pour permettre aux serveurs de trouver le chemin de
+retour dans des contextes de routage difficiles. Si l'adresse est 0.0.0.0,
+elle sera choisie librement par le systeme. Si le port est 0, il
+sera choisi librement par le système.
+
+Exemples :
+----------
+    listen http_proxy 0.0.0.0:80
+   	# toutes les connexions prennent l'adresse 192.168.1.200
+	source 192.168.1.200:0
+
+    listen rlogin_proxy 0.0.0.0:513
+   	# utiliser l'adresse 192.168.1.200 et le port réservé 900
+	source 192.168.1.200:900
+
+
+2.9) Définition du nom du cookie
 --------------------------------
 En mode HTTP, il est possible de rechercher la valeur d'un cookie pour savoir
 vers quel serveur aiguiller la requête utilisateur. Le nom du cookie est donné
@@ -433,7 +455,8 @@
 vis-à-vis des applications relayées. Il est possible, notamment de supprimer ou
 réécrire un cookie retourné par un serveur accédé en direct, et d'insérer un
 cookie dans une réponse HTTP adressée à un serveur sélectionné en répartition
-de charge.
+de charge, et même de signaler aux proxies amont de ne pas cacher le cookie
+inséré.
 
 Exemples :
 ----------
@@ -455,6 +478,12 @@
 
 	cookie SERVERID insert
 
+Pour insérer un cookie, en s'assurant qu'un cache en amont ne le stockera pas,
+ajouter le mot clé 'nocache' après 'insert' :
+
+	cookie SERVERID insert nocache
+
+
 Remarques :
 -----------
 - Il est possible de combiner 'insert' avec 'indirect' ou 'rewrite' pour s'adapter
@@ -463,8 +492,13 @@
 - dans le cas où 'insert' et 'indirect' sont spécifiés, le cookie n'est jamais
   transmis au serveur vu qu'il n'en a pas connaissance et ne pourrait pas le
   comprendre.
+- il est particulièrement recommandé d'utiliser 'nocache' en mode insertion si
+  des caches peuvent se trouver entre les clients et l'instance du proxy. Dans
+  le cas contraire, un cache HTTP 1.0 pourrait cacher la réponse, incluant le
+  cookie de persistence inséré, donc provoquer des changements de serveurs pour
+  des clients partageant le même cache.
 
-2.9) Assignation d'un serveur à une valeur de cookie
+2.10) Assignation d'un serveur à une valeur de cookie
 ----------------------------------------------------
 En mode HTTP, il est possible d'associer des serveurs à des valeurs de
 cookie par le paramètre 'server'. La syntaxe est :
@@ -536,10 +570,11 @@
 	server web2 192.168.1.2:80 cookie server02 check inter 500 rise 1 fall 2
 
 # Insertion automatique de cookie dans la réponse du serveur, et suppression
-# automatique dans la requête.
+# automatique dans la requête, tout en indiquant aux caches de ne pas garder
+# ce cookie.
     listen web_appl 0.0.0.0:80
 	mode http
-	cookie SERVERID insert indirect
+	cookie SERVERID insert nocache indirect
 	balance roundrobin
 	server web1 192.168.1.1:80 cookie server01 check
 	server web2 192.168.1.2:80 cookie server02 check
@@ -667,7 +702,7 @@
    reqallow  <search>             autoriser une requête qui valide <search>
    reqiallow <search>             idem sans distinction majuscules/minuscules
    reqdeny   <search>             interdire une requête qui valide <search>
-   reqdeny   <search>             idem sans distinction majuscules/minuscules
+   reqideny  <search>             idem sans distinction majuscules/minuscules
 
    rspadd   <string>             pour ajouter un en-tête dans la réponse
    rsprep   <search> <replace>   pour modifier la réponse
@@ -733,14 +768,15 @@
 pratiquement transparente pour les applications. Le principe est simple :
   - attribuer une valeur d'un cookie à chaque serveur
   - effectuer une répartition interne
-  - insérer un cookie dans les réponses issues d'une répartition uniquement
-  - cacher ce cookie à l'application
+  - insérer un cookie dans les réponses issues d'une répartition uniquement,
+    et faire en sorte que des caches ne mémorisent pas ce cookie.
+  - cacher ce cookie à l'application lors des requêtes ultérieures.
 
 Exemple :
 -------
     listen application 0.0.0.0:80
 	mode http
-	cookie SERVERID insert indirect
+	cookie SERVERID insert nocache indirect
 	balance roundrobin
 	server 192.168.1.1:80 cookie server01 check
 	server 192.168.1.2:80 cookie server02 check
@@ -772,7 +808,6 @@
 fi
 
 echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range
-echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
 echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
 echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog
 echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets
diff --git a/examples/cfg b/examples/examples.cfg
similarity index 100%
rename from examples/cfg
rename to examples/examples.cfg
diff --git a/examples/haproxy.cfg b/examples/haproxy.cfg
new file mode 100644
index 0000000..44fd8d7
--- /dev/null
+++ b/examples/haproxy.cfg
@@ -0,0 +1,63 @@
+global
+	log 127.0.0.1	local0
+	maxconn 4096
+	chroot /tmp
+	uid 11
+	gid 2
+	daemon
+	#debug
+	#quiet
+
+listen	appli1-rewrite 0.0.0.0:10001
+	log	global
+	mode	http
+	option	httplog
+	option	dontlognull
+	cookie	SERVERID rewrite
+	balance	roundrobin
+	server	app1_1 192.168.34.23:8080 cookie app1inst1 check inter 2000 rise 2 fall 5
+	server	app1_2 192.168.34.32:8080 cookie app1inst2 check inter 2000 rise 2 fall 5
+	server	app1_3 192.168.34.27:8080 cookie app1inst3 check inter 2000 rise 2 fall 5
+	server	app1_4 192.168.34.42:8080 cookie app1inst4 check inter 2000 rise 2 fall 5
+	retries	3
+	redispatch
+	maxconn	2000
+	contimeout	5000
+	clitimeout	50000
+	srvtimeout	50000
+	
+listen	appli2-insert 0.0.0.0:10002
+	log	global
+	mode	http
+	option	httplog
+	option	dontlognull
+	balance	roundrobin
+	cookie	SERVERID insert indirect nocache
+	server	inst1 192.168.114.56:80 cookie server01 check inter 2000 fall 3
+	server	inst2 192.168.114.56:81 cookie server02 check inter 2000 fall 3
+	retries	3
+	redispatch
+	maxconn	2000
+	contimeout	5000
+	clitimeout	50000
+	srvtimeout	50000
+
+	reqidel ^Connection:		# desactivation du keep-alive
+	reqadd  Connection:\ close
+	rspidel ^Connection:
+	rspadd  Connection:\ close
+	rspidel ^Set-cookie:\ IP=	# ne pas laisser sortir une adresse privee
+	
+listen	appli3-relais 0.0.0.0:10003
+	log	global
+       	mode	http
+	option	httplog
+	option	dontlognull
+	dispatch 192.168.135.17:80
+	retries	3
+	redispatch
+	maxconn	2000
+	contimeout	5000
+	clitimeout	50000
+	srvtimeout	50000
+
diff --git a/examples/rc.highsock b/examples/rc.highsock
index 9325ee0..d85935a 100644
--- a/examples/rc.highsock
+++ b/examples/rc.highsock
@@ -17,7 +17,7 @@
 fi
 
 echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range
-echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
+#echo 32768 > /proc/sys/net/ipv4/ip_queue_maxlen
 echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
 echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog
 echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets
diff --git a/haproxy.c b/haproxy.c
index 9ab37b1..e6721d7 100644
--- a/haproxy.c
+++ b/haproxy.c
@@ -17,6 +17,14 @@
  *
  * ChangeLog :
  *
+ * 2002/06/04 : 1.1.11
+ *   - fixed multi-cookie handling in client request to allow clean deletion
+ *     in insert+indirect mode. Now, only the server cookie is deleted and not
+ *     all the header. Should now be compliant to RFC2109.
+ *   - added a "nocache" option to "cookie" to specify that we explicitly want
+ *     to add a "cache-control" header when we add a cookie.
+ *     It is also possible to add an "Expires: <old-date>" to keep compatibility
+ *     with old/broken caches.
  * 2002/05/10 : 1.1.10
  *   - if a cookie is used in insert+indirect mode, it's desirable that the
  *     the servers don't see it. It was not possible to remove it correctly
@@ -145,8 +153,8 @@
 #include <linux/netfilter_ipv4.h>
 #endif
 
-#define HAPROXY_VERSION "1.1.9"
-#define HAPROXY_DATE	"2002/04/19"
+#define HAPROXY_VERSION "1.1.11"
+#define HAPROXY_DATE	"2002/06/04"
 
 /* this is for libc5 for example */
 #ifndef TCP_NODELAY
@@ -313,6 +321,7 @@
 #define	PR_O_FWDFOR	128	/* insert x-forwarded-for with client address */
 #define	PR_O_BIND_SRC	256	/* bind to a specific source address when connect()ing */
 #define PR_O_NULLNOLOG	512	/* a connect without request will not be logged */
+#define PR_O_COOK_NOC	1024	/* add a 'Cache-control' header with the cookie */
 
 
 /* various session flags */
@@ -2021,7 +2030,9 @@
     return delta;
 }
 
-/* same except that the string len is given */
+/* same except that the string len is given, which allows str to be NULL if
+ * len is 0.
+ */
 int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) {
     int delta;
 
@@ -2034,7 +2045,8 @@
     memmove(end + delta, end, b->data + b->l - end);
 
     /* now, copy str over pos */
-    memcpy(pos, str,len);
+    if (len)
+	memcpy(pos, str, len);
 
     /* we only move data after the displaced zone */
     if (b->r  > pos) b->r  += delta;
@@ -2247,43 +2259,59 @@
 		*ptr = term; /* restore the string terminator */
 	    }
 	    
-	    /* now look for cookies */
-	    if (!delete_header && (req->r >= req->h + 8) && (t->proxy->cookie_name != NULL)
+	    /* Now look for cookies. Conforming to RFC2109, we have to support
+	     * attributes whose name begin with a '$', and associate them with
+	     * the right cookie, if we want to delete this cookie.
+	     * So there are 3 cases for each cookie read :
+	     * 1) it's a special attribute, beginning with a '$' : ignore it.
+	     * 2) it's a server id cookie that we *MAY* want to delete : save
+	     *    some pointers on it (last semi-colon, beginning of cookie...)
+	     * 3) it's an application cookie : we *MAY* have to delete a previous
+	     *    "special" cookie.
+	     * At the end of loop, if a "special" cookie remains, we may have to
+	     * remove it. If no application cookie persists in the header, we
+	     * *MUST* delete it
+	     */
+	    if (!delete_header && (t->proxy->cookie_name != NULL)
+		&& !(t->flags & SN_CLDENY) && (ptr >= req->h + 8)
 		&& (strncmp(req->h, "Cookie: ", 8) == 0)) {
 		char *p1, *p2, *p3, *p4;
-		
+		char *del_colon, *del_cookie, *colon;
+		int app_cookies;
+
 		p1 = req->h + 8; /* first char after 'Cookie: ' */
+		colon = p1;
+		/* del_cookie == NULL => nothing to be deleted */
+		del_colon = del_cookie = NULL;
+		app_cookies = 0;
 		
 		while (p1 < ptr) {
-		    while (p1 < ptr && (isspace((int)*p1) || *p1 == ';'))
+		    /* skip spaces and colons, but keep an eye on these ones */
+		    while (p1 < ptr) {
+			if (*p1 == ';' || *p1 == ',')
+			    colon = p1;
+			else if (!isspace((int)*p1))
+			    break;
 			p1++;
+		    }
 		    
 		    if (p1 == ptr)
 			break;
-		    else if (*p1 == ';') { /* next cookie */
-			++p1;
-			continue;
-		    }
 		    
 		    /* p1 is at the beginning of the cookie name */
 		    p2 = p1;
-		    
-		    while (p2 < ptr && *p2 != '=' && *p2 != ';')
+		    while (p2 < ptr && *p2 != '=')
 			p2++;
 		    
 		    if (p2 == ptr)
 			break;
-		    else if (*p2 == ';') { /* next cookie */
-			p1=++p2;
-			continue;
-		    }
 
 		    p3 = p2 + 1; /* skips the '=' sign */
 		    if (p3 == ptr)
 			break;
 		    
-		    p4=p3;
-		    while (p4 < ptr && !isspace((int)*p4) && *p4 != ';')
+		    p4 = p3;
+		    while (p4 < ptr && !isspace((int)*p4) && *p4 != ';' && *p4 != ',')
 			p4++;
 		    
 		    /* here, we have the cookie name between p1 and p2,
@@ -2291,8 +2319,11 @@
 		     * we can process it.
 		     */
 		    
-		    if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
-			(strncmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
+		    if (*p1 == '$') {
+			/* skip this one */
+		    }
+		    else if ((p2 - p1 == strlen(t->proxy->cookie_name)) &&
+			(memcmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) {
 			/* Cool... it's the right one */
 			struct server *srv = t->proxy->srv;
 
@@ -2304,31 +2335,64 @@
 			if (srv) { /* we found the server */
 			    t->flags |= SN_DIRECT;
 			    t->srv = srv;
-			    /* if this cookie was set in insert+indirect mode, then it's better that the
-			     * server never sees it.
-			     */
-			    if ((t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND))
-				delete_header = 1;
 			}
-
-			break;
+			/* if this cookie was set in insert+indirect mode, then it's better that the
+			 * server never sees it.
+			 */
+			if (del_cookie == NULL &&
+			    (t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) {
+				del_cookie = p1;
+				del_colon = colon;
+			}
 		    }
 		    else {
-			// fprintf(stderr,"Ignoring unknown cookie : ");
-			// write(2, p1, p2-p1);
-			// fprintf(stderr," = ");
-			// write(2, p3, p4-p3);
-			// fprintf(stderr,"\n");
+			/* now we know that we must keep this cookie since it's
+			 * not ours. But if we wanted to delete our cookie
+			 * earlier, we cannot remove the complete header, but we
+			 * can remove the previous block itself.
+			 */
+			app_cookies++;
+
+			if (del_cookie != NULL) {
+			    buffer_replace2(req, del_cookie, p1, NULL, 0);
+			    p4  -= (p1 - del_cookie);
+			    ptr -= (p1 - del_cookie);
+			    del_cookie = del_colon = NULL;
+			}
 		    }
+
 		    /* we'll have to look for another cookie ... */
 		    p1 = p4;
 		} /* while (p1 < ptr) */
-	    } /* end of cookie processing */
+
+		/* There's no more cookie on this line.
+		 * We may have marked the last one(s) for deletion.
+		 * We must do this now in two ways :
+		 *  - if there is no app cookie, we simply delete the header ;
+		 *  - if there are app cookies, we must delete the end of the
+		 *    string properly, including the colon/semi-colon before
+		 *    the cookie name.
+		 */
+		if (del_cookie != NULL) {
+		    if (app_cookies) {
+			buffer_replace2(req, del_colon, ptr, NULL, 0);
+			/* WARNING! <ptr> becomes invalid for now. If some code
+			 * below needs to rely on it before the end of the global
+			 * header loop, we need to correct it with this code :
+			 * ptr = del_colon;
+			 */
+		    }
+		    else
+			delete_header = 1;
+		}
+	    } /* end of cookie processing on this header */
 
 	    /* let's look if we have to delete this header */
 	    if (delete_header && !(t->flags & SN_CLDENY)) {
-		buffer_replace2(req, req->h, req->lr, "", 0);
+		buffer_replace2(req, req->h, req->lr, NULL, 0);
 	    }
+	    /* WARNING: ptr is not valid anymore, since the header may have been deleted or truncated ! */
+
 	    req->h = req->lr;
 	} /* while (req->lr < req->r) */
 
@@ -2634,6 +2698,9 @@
 		     */
 		    len = sprintf(newhdr, "Set-Cookie: %s=%s; path=/\r\n",
 				  t->proxy->cookie_name, t->srv->cookie);
+		    if (t->proxy->options & PR_O_COOK_NOC)
+			len += sprintf(newhdr + len, "Cache-control: no-cache=\"set-cookie\"\r\n");
+
 		    buffer_replace2(rep, rep->h, rep->h, newhdr, len);
 		}
 
@@ -2731,8 +2798,9 @@
 	    }
 	    
 	    /* check for server cookies */
-	    if (!delete_header && (t->proxy->options & PR_O_COOK_ANY) && (rep->r >= rep->h + 12) &&
-		(t->proxy->cookie_name != NULL)	&& (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) {
+	    if (!delete_header && (t->proxy->options & PR_O_COOK_ANY)
+		&& (t->proxy->cookie_name != NULL) && (ptr >= rep->h + 12)
+		&& (strncmp(rep->h, "Set-Cookie: ", 12) == 0)) {
 		char *p1, *p2, *p3, *p4;
 		
 		p1 = rep->h + 12; /* first char after 'Set-Cookie: ' */
@@ -3709,6 +3777,9 @@
 	    else if (!strcmp(args[cur_arg], "insert")) {
 		curproxy->options |= PR_O_COOK_INS;
 	    }
+	    else if (!strcmp(args[cur_arg], "nocache")) {
+		curproxy->options |= PR_O_COOK_NOC;
+	    }
 	    else {
 		Alert("parsing [%s:%d] : <cookie> supports 'rewrite', 'insert' and 'indirect' options.\n",
 		      file, linenum);