[MINOR] cookie: add support for the "preserve" option

This option makes haproxy preserve any persistence cookie emitted by
the server, which allows the server to change it or to unset it, for
instance, after a logout request.
(cherry picked from commit 52e6d75374c7900c1fe691c5633b4ae029cae8d5)
diff --git a/doc/configuration.txt b/doc/configuration.txt
index d05ad9e..ae4827a 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1675,7 +1675,7 @@
 
 
 cookie <name> [ rewrite | insert | prefix ] [ indirect ] [ nocache ]
-              [ postonly ] [ domain <domain> ]*
+              [ postonly ] [ preserve ] [ domain <domain> ]*
               [ maxidle <idle> ] [ maxlife <life> ]
   Enable cookie-based persistence in a backend.
   May be used in sections :   defaults | frontend | listen | backend
@@ -1705,16 +1705,18 @@
 
     insert    This keyword indicates that the persistence cookie will have to
               be inserted by haproxy in server responses if the client did not
+
               already have a cookie that would have permitted it to access this
-              server. If the server emits a cookie with the same name, it will
-              be remove before processing. For this reason, this mode can be
-              used to upgrade existing configurations running in the "rewrite"
-              mode. The cookie will only be a session cookie and will not be
-              stored on the client's disk. By default, unless the "indirect"
-              option is added, the server will see the cookies emitted by the
-              client. Due to caching effects, it is generally wise to add the
-              "nocache" or "postonly" keywords (see below). The "insert"
-              keyword is not compatible with "rewrite" and "prefix".
+              server. When used without the "preserve" option, if the server
+              emits a cookie with the same name, it will be remove before
+              processing.  For this reason, this mode can be used to upgrade
+              existing configurations running in the "rewrite" mode. The cookie
+              will only be a session cookie and will not be stored on the
+              client's disk. By default, unless the "indirect" option is added,
+              the server will see the cookies emitted by the client. Due to
+              caching effects, it is generally wise to add the "nocache" or
+              "postonly" keywords (see below). The "insert" keyword is not
+              compatible with "rewrite" and "prefix".
 
     prefix    This keyword indicates that instead of relying on a dedicated
               cookie for the persistence, an existing one will be completed.
@@ -1731,10 +1733,10 @@
     indirect  When this option is specified, no cookie will be emitted to a
               client which already has a valid one for the server which has
               processed the request. If the server sets such a cookie itself,
-              it will be removed. In "insert" mode, this will additionally
-              remove cookies from requests transmitted to the server, making
-              the persistence mechanism totally transparent from an application
-              point of view.
+              it will be removed, unless the "preserve" option is also set. In
+              "insert" mode, this will additionally remove cookies from the
+              requests transmitted to the server, making the persistence
+              mechanism totally transparent from an application point of view.
 
     nocache   This option is recommended in conjunction with the insert mode
               when there is a cache between the client and HAProxy, as it
@@ -1756,6 +1758,17 @@
               persistence cookie in the cache.
               See also the "insert" and "nocache" options.
 
+    preserve  This option may only be used with "insert" and/or "indirect". It
+              allows the server to emit the persistence cookie itself. In this
+              case, if a cookie is found in the response, haproxy will leave it
+              untouched. This is useful in order to end persistence after a
+              logout request for instance. For this, the server just has to
+              emit a cookie with an invalid value (eg: empty) or with a date in
+              the past. By combining this mechanism with the "disable-on-404"
+              check option, it is possible to perform a completely graceful
+              shutdown because users will definitely leave the server after
+              they logout.
+
     domain    This option allows to specify the domain at which a cookie is
               inserted. It requires exactly one parameter: a valid domain
               name. If the domain begins with a dot, the browser is allowed to
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 246a9be..f4fd4a9 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -148,6 +148,7 @@
 #define PR_O2_EXP_RSTR  0x02000000      /* http-check expect rstring */
 #define PR_O2_EXP_TYPE  0x03800000      /* mask for http-check expect type */
 #define PR_O2_EXP_INV   0x04000000      /* http-check expect !<rule> */
+#define PR_O2_COOK_PSV  0x08000000      /* cookie ... preserve */
 /* end of proxy->options2 */
 
 /* bits for sticking rules */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index da6ef81..a7cdea4 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1661,6 +1661,7 @@
 		}
 
 		curproxy->options &= ~PR_O_COOK_ANY;
+		curproxy->options2 &= ~PR_O2_COOK_PSV;
 		curproxy->cookie_maxidle = curproxy->cookie_maxlife = 0;
 		free(curproxy->cookie_domain); curproxy->cookie_domain = NULL;
 		free(curproxy->cookie_name);
@@ -1684,6 +1685,9 @@
 			else if (!strcmp(args[cur_arg], "postonly")) {
 				curproxy->options |= PR_O_COOK_POST;
 			}
+			else if (!strcmp(args[cur_arg], "preserve")) {
+				curproxy->options2 |= PR_O2_COOK_PSV;
+			}
 			else if (!strcmp(args[cur_arg], "prefix")) {
 				curproxy->options |= PR_O_COOK_PFX;
 			}
@@ -1790,6 +1794,12 @@
 			      file, linenum);
 			err_code |= ERR_ALERT | ERR_FATAL;
 		}
+
+		if ((curproxy->options2 & PR_O2_COOK_PSV) && !(curproxy->options & (PR_O_COOK_INS|PR_O_COOK_IND))) {
+			Alert("parsing [%s:%d] : cookie 'preserve' requires at least 'insert' or 'indirect'.\n",
+			      file, linenum);
+			err_code |= ERR_ALERT | ERR_FATAL;
+		}
 	}/* end else if (!strcmp(args[0], "cookie"))  */
 	else if (!strcmp(args[0], "persist")) {  /* persist */
 		if (*(args[1]) == 0) {
diff --git a/src/proto_http.c b/src/proto_http.c
index 3a2be18..a5e6e16 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -5098,6 +5098,7 @@
 		 * 6: add server cookie in the response if needed
 		 */
 		if ((t->srv) && (t->be->options & PR_O_COOK_INS) &&
+		    !((txn->flags & TX_SCK_FOUND) && (t->be->options2 & PR_O2_COOK_PSV)) &&
 		    (!(t->flags & SN_DIRECT) ||
 		     ((t->be->cookie_maxidle || txn->cookie_last_date) &&
 		      (!txn->cookie_last_date || (txn->cookie_last_date - date.tv_sec) < 0)) ||
@@ -6780,7 +6781,12 @@
 				 * We'll delete it too if the "indirect" option is set and we're in
 				 * a direct access.
 				 */
-				if (((t->srv) && (t->be->options & PR_O_COOK_INS)) ||
+				if (t->be->options2 & PR_O2_COOK_PSV) {
+					/* The "preserve" flag was set, we don't want to touch the
+					 * server's cookie.
+					 */
+				}
+				else if (((t->srv) && (t->be->options & PR_O_COOK_INS)) ||
 				    ((t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_IND))) {
 					/* this cookie must be deleted */
 					if (*prev == ':' && next == hdr_end) {