MEDIUM: streams: Add a new http action, disable-l7-retry.

Add a new action for http-request, disable-l7-retry, that can be used to
disable any attempt at retry requests (see retry-on) if it fails for any
reason other than a connection failure.
This is useful for example to make sure POST requests aren't retried.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 31aac44..f88dad8 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -4297,6 +4297,11 @@
   those that can be overridden by the "errorfile" directive.
   No further "http-request" rules are evaluated.
 
+http-request disable-l7-retry [ { if | unless } <condition> ]
+  This disables any attempt to retry the request if it fails for any other
+  reason than a connection failure. This can be useful for example to make
+  sure POST requests aren't retried on failure.
+
 http-request do-resolve(<var>,<resolvers>,[ipv4,ipv6]) <expr> :
 
   This action performs a DNS resolution of the output of <expr> and stores
diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h
index 6b30de5..e11a6c9 100644
--- a/include/types/stream_interface.h
+++ b/include/types/stream_interface.h
@@ -84,6 +84,7 @@
 	SI_FL_RXBLK_ANY  = 0x001F0000,  /* any of the RXBLK flags above */
 	SI_FL_RX_WAIT_EP = 0x00200000,  /* stream-int waits for more data from the end point */
 	SI_FL_L7_RETRY   = 0x01000000,  /* The stream interface may attempt L7 retries */
+	SI_FL_D_L7_RETRY = 0x02000000,  /* Disable L7 retries on this stream interface, even if configured to do it */
 };
 
 /* A stream interface has 3 parts :
diff --git a/src/http_act.c b/src/http_act.c
index c8618ea..c1b94dd 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -217,6 +217,38 @@
 	return ACT_RET_PRS_OK;
 }
 
+/* This function executes the "disable-l7-retry" HTTP action.
+ * It disables L7 retries (all retry except for a connection failure). This
+ * can be useful for example to avoid retrying on POST requests.
+ * It just removes the L7 retry flag on the stream_interface, and always
+ * return ACT_RET_CONT;
+ */
+static enum act_return http_req_disable_l7_retry(struct act_rule *rule, struct proxy *px,
+                                          struct session *sess, struct stream *s, int flags)
+{
+	struct stream_interface *si = &s->si[1];
+
+	/* In theory, the SI_FL_L7_RETRY flags isn't set at this point, but
+	 * let's be future-proof and remove it anyway.
+	 */
+	si->flags &= ~SI_FL_L7_RETRY;
+	si->flags |= SI_FL_D_L7_RETRY;
+	return ACT_RET_CONT;
+}
+
+/* parse the "disable-l7-retry" action:
+ * This action takes no argument and returns ACT_RET_PRS_OK on success,
+ * ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_req_disable_l7_retry(const char **args,
+							  int *orig_args, struct proxy *px,
+							  struct act_rule *rule, char **err)
+{
+	rule->action = ACT_CUSTOM;
+	rule->action_ptr = http_req_disable_l7_retry;
+	return ACT_RET_PRS_OK;
+}
+
 /* This function executes the "capture" action. It executes a fetch expression,
  * turns the result into a string and puts it in a capture slot. It always
  * returns 1. If an error occurs the action is cancelled, but the rule
@@ -575,6 +607,7 @@
 	.kw = {
 		{ "capture",    parse_http_req_capture },
 		{ "reject",     parse_http_action_reject },
+		{ "disable-l7-retry", parse_http_req_disable_l7_retry },
 		{ "set-method", parse_set_req_line },
 		{ "set-path",   parse_set_req_line },
 		{ "set-query",  parse_set_req_line },
diff --git a/src/stream.c b/src/stream.c
index 58e1cc1..62c5598 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -2341,7 +2341,8 @@
 				 */
 				si_b->state = SI_ST_REQ; /* new connection requested */
 				si_b->conn_retries = s->be->conn_retries;
-				if (s->be->retry_type &~ PR_RE_CONN_FAILED)
+				if ((s->be->retry_type &~ PR_RE_CONN_FAILED) &&
+				    !(si_b->flags & SI_FL_D_L7_RETRY))
 					si_b->flags |= SI_FL_L7_RETRY;
 			}
 		}