[MINOR] add the "ignore-persist" option to conditionally ignore persistence

This is used to disable persistence depending on some conditions (for
example using an ACL matching static files or a specific User-Agent).
You can see it as a complement to "force-persist".

In the configuration file, the force-persist/ignore-persist declaration
order define the rules priority.

Used with the "appsesion" keyword, it can also help reducing memory usage,
as the session won't be hashed the persistence is ignored.

diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index f5410fa..7890cb2 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -221,6 +221,13 @@
 	REDIRECT_TYPE_PREFIX,           /* prefix redirect */
 };
 
+/* Perist types (force-persist, ignore-persist) */
+enum {
+	PERSIST_TYPE_NONE = 0,          /* no persistence */
+	PERSIST_TYPE_FORCE,             /* force-persist */
+	PERSIST_TYPE_IGNORE,            /* ignore-persist */
+};
+
 /* Known HTTP methods */
 typedef enum {
 	HTTP_METH_NONE = 0,
diff --git a/include/types/proxy.h b/include/types/proxy.h
index fb34513..3ac80d8 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -176,7 +176,7 @@
 	struct list block_cond;                 /* early blocking conditions (chained) */
 	struct list redirect_rules;             /* content redirecting rules (chained) */
 	struct list switching_rules;            /* content switching rules (chained) */
-	struct list force_persist_rules;        /* 'force-persist' rules (chained) */
+	struct list persist_rules;		/* 'force-persist' and 'ignore-persist' rules (chained) */
 	struct list sticking_rules;             /* content sticking rules (chained) */
 	struct list storersp_rules;             /* content store response rules (chained) */
 	struct {                                /* TCP request processing */
@@ -307,9 +307,10 @@
 	} be;
 };
 
-struct force_persist_rule {
+struct persist_rule {
 	struct list list;			/* list linked to from the proxy */
 	struct acl_cond *cond;			/* acl condition to meet */
+	int type;
 };
 
 struct sticking_rule {
diff --git a/include/types/session.h b/include/types/session.h
index 94bb5d5..daf2619 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -81,6 +81,8 @@
 #define	SN_FINST_SHIFT	16		/* bit shift */
 /* unused:              0x00080000 */
 
+#define SN_IGNORE_PRST	0x00100000	/* ignore persistence */
+
 /* WARNING: if new fields are added, they must be initialized in event_accept()
  * and freed in session_free() !
  */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index f25cc76..ebba4df 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -903,7 +903,7 @@
 	LIST_INIT(&p->redirect_rules);
 	LIST_INIT(&p->mon_fail_cond);
 	LIST_INIT(&p->switching_rules);
-	LIST_INIT(&p->force_persist_rules);
+	LIST_INIT(&p->persist_rules);
 	LIST_INIT(&p->sticking_rules);
 	LIST_INIT(&p->storersp_rules);
 	LIST_INIT(&p->tcp_req.inspect_rules);
@@ -2178,8 +2178,9 @@
 		LIST_INIT(&rule->list);
 		LIST_ADDQ(&curproxy->switching_rules, &rule->list);
 	}
-	else if (!strcmp(args[0], "force-persist")) {
-		struct force_persist_rule *rule;
+	else if ((!strcmp(args[0], "force-persist")) ||
+		 (!strcmp(args[0], "ignore-persist"))) {
+		struct persist_rule *rule;
 
 		if (curproxy == &defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@@ -2198,18 +2199,23 @@
 		}
 
 		if ((cond = build_acl_cond(file, linenum, curproxy, (const char **)args + 1)) == NULL) {
-			Alert("parsing [%s:%d] : error detected while parsing a 'force-persist' rule.\n",
-			      file, linenum);
+			Alert("parsing [%s:%d] : error detected while parsing a '%s' rule.\n",
+			      file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
 		err_code |= warnif_cond_requires_resp(cond, file, linenum);
 
-		rule = (struct force_persist_rule *)calloc(1, sizeof(*rule));
+		rule = (struct persist_rule *)calloc(1, sizeof(*rule));
 		rule->cond = cond;
+		if (!strcmp(args[0], "force-persist")) {
+			rule->type = PERSIST_TYPE_FORCE;
+		} else {
+			rule->type = PERSIST_TYPE_IGNORE;
+		}
 		LIST_INIT(&rule->list);
-		LIST_ADDQ(&curproxy->force_persist_rules, &rule->list);
+		LIST_ADDQ(&curproxy->persist_rules, &rule->list);
 	}
 	else if (!strcmp(args[0], "stick-table")) {
 		int myidx = 1;
diff --git a/src/proto_http.c b/src/proto_http.c
index ce8448b..0f6f77b 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3282,8 +3282,8 @@
 	 * so let's do the same now.
 	 */
 
-	/* It needs to look into the URI */
-	if ((txn->sessid == NULL) && s->be->appsession_name) {
+	/* It needs to look into the URI unless persistence must be ignored */
+	if ((txn->sessid == NULL) && s->be->appsession_name && !(s->flags & SN_IGNORE_PRST)) {
 		get_srv_from_appsession(s, msg->sol + msg->sl.rq.u, msg->sl.rq.u_l);
 	}
 
@@ -3730,7 +3730,7 @@
 	s->req->cons->flags     = SI_FL_NONE;
 	s->req->flags &= ~(BF_SHUTW|BF_SHUTW_NOW|BF_AUTO_CONNECT|BF_WRITE_ERROR|BF_STREAMER|BF_STREAMER_FAST);
 	s->rep->flags &= ~(BF_SHUTR|BF_SHUTR_NOW|BF_READ_ATTACHED|BF_READ_ERROR|BF_READ_NOEXP|BF_STREAMER|BF_STREAMER_FAST|BF_WRITE_PARTIAL);
-	s->flags &= ~(SN_DIRECT|SN_ASSIGNED|SN_ADDR_SET|SN_BE_ASSIGNED|SN_FORCE_PRST);
+	s->flags &= ~(SN_DIRECT|SN_ASSIGNED|SN_ADDR_SET|SN_BE_ASSIGNED|SN_FORCE_PRST|SN_IGNORE_PRST);
 	s->flags &= ~(SN_CURR_SESS|SN_REDIRECTABLE);
 	s->txn.meth = 0;
 	http_reset_txn(s);
@@ -4856,7 +4856,8 @@
 		 * 6: add server cookie in the response if needed
 		 */
 		if ((t->srv) && !(t->flags & SN_DIRECT) && (t->be->options & PR_O_COOK_INS) &&
-		    (!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST))) {
+		    (!(t->be->options & PR_O_COOK_POST) || (txn->meth == HTTP_METH_POST)) &&
+		    !(t->flags & SN_IGNORE_PRST)) {
 			int len;
 
 			/* the server is known, it's not the one the client requested, we have to
@@ -5493,6 +5494,7 @@
 
 		if (asession->serverid != NULL) {
 			struct server *srv = t->be->srv;
+
 			while (srv) {
 				if (strcmp(srv->id, asession->serverid) == 0) {
 					if ((srv->state & SRV_RUNNING) ||
@@ -5686,8 +5688,9 @@
 					 * However, to prevent clients from sticking to cookie-less backup server
 					 * when they have incidentely learned an empty cookie, we simply ignore
 					 * empty cookies and mark them as invalid.
+					 * The same behaviour is applied when persistence must be ignored.
 					 */
-					if (delim == p3)
+					if ((delim == p3) || (t->flags & SN_IGNORE_PRST))
 						srv = NULL;
 
 					while (srv) {
@@ -5765,7 +5768,8 @@
 					}
 				}
 
-				if (t->be->appsession_name != NULL) {
+				/* Look for the appsession cookie unless persistence must be ignored */
+				if (!(t->flags & SN_IGNORE_PRST) && (t->be->appsession_name != NULL)) {
 					int cmp_len, value_len;
 					char *value_begin;
 
@@ -6161,7 +6165,7 @@
 			}
 
 			/* now check if we need to process it for persistence */
-			if ((p2 - p1 == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
+			if (!(t->flags & SN_IGNORE_PRST) && (p2 - p1 == t->be->cookie_len) && (t->be->cookie_name != NULL) &&
 			    (memcmp(p1, t->be->cookie_name, p2 - p1) == 0)) {
 				/* Cool... it's the right one */
 				txn->flags |= TX_SCK_SEEN;
@@ -6208,8 +6212,8 @@
 					txn->flags |= TX_SCK_INSERTED | TX_SCK_DELETED;
 				}
 			}
-			/* next, let's see if the cookie is our appcookie */
-			else if (t->be->appsession_name != NULL) {
+			/* next, let's see if the cookie is our appcookie, unless persistence must be ignored */
+			else if (!(t->flags & SN_IGNORE_PRST) && (t->be->appsession_name != NULL)) {
 				int cmp_len, value_len;
 				char *value_begin;
 
diff --git a/src/session.c b/src/session.c
index 34ffb11..3f08a6a 100644
--- a/src/session.c
+++ b/src/session.c
@@ -551,7 +551,7 @@
  */
 int process_switching_rules(struct session *s, struct buffer *req, int an_bit)
 {
-	struct force_persist_rule *prst_rule;
+	struct persist_rule *prst_rule;
 
 	req->analysers &= ~an_bit;
 	req->analyse_exp = TICK_ETERNITY;
@@ -598,10 +598,10 @@
 	if (s->fe == s->be)
 		s->req->analysers &= ~AN_REQ_HTTP_PROCESS_BE;
 
-	/* as soon as we know the backend, we must check if we have a matching forced
+	/* as soon as we know the backend, we must check if we have a matching forced or ignored
 	 * persistence rule, and report that in the session.
 	 */
-	list_for_each_entry(prst_rule, &s->be->force_persist_rules, list) {
+	list_for_each_entry(prst_rule, &s->be->persist_rules, list) {
 		int ret = 1;
 
 		if (prst_rule->cond) {
@@ -613,7 +613,11 @@
 
 		if (ret) {
 			/* no rule, or the rule matches */
-			s->flags |= SN_FORCE_PRST;
+			if (prst_rule->type == PERSIST_TYPE_FORCE) {
+				s->flags |= SN_FORCE_PRST;
+			} else {
+				s->flags |= SN_IGNORE_PRST;
+			}
 			break;
 		}
 	}