BUG/MINOR: http-act: Properly generate 103 responses when several rules are used

When several "early-hint" rules are used, we try, as far as possible, to
merge links into the same 103-early-hints response. However, it only works
if there is no ACLs. If a "early-hint" rule is not executed an invalid
response is generated. the EOH block or the start-line may be missing,
depending on the rule order.

To fix the bug, we use the transaction status code. It is unused at this
stage. Thus, it is set to 103 when a 103-early-hints response is in
progress. And it is reset when the response is forwarded. In addition, the
response is forwarded if the next rule is an "early-hint" rule with an
ACL. This way, the response is always valid.

This patch must be backported as far as 2.2.

(cherry picked from commit 4c3d3d2a685bb4f33c7dae44a6f56fcce1689dcf)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 2d9ac87c94b0469af40b2c2126db52797b4ba5bc)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 6f67262e33dcade272a27ecd81d57d8770dbaafd)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/http_act.c b/src/http_act.c
index 8574a7d..f88c856 100644
--- a/src/http_act.c
+++ b/src/http_act.c
@@ -1456,7 +1456,7 @@
 static enum act_return http_action_early_hint(struct act_rule *rule, struct proxy *px,
 					      struct session *sess, struct stream *s, int flags)
 {
-	struct act_rule *prev_rule, *next_rule;
+	struct act_rule *next_rule;
 	struct channel *res = &s->res;
 	struct htx *htx = htx_from_buf(&res->buf);
 	struct buffer *value = alloc_trash_chunk();
@@ -1471,13 +1471,10 @@
 		goto error;
 	}
 
-	/* get previous and next rules */
-	prev_rule = LIST_PREV(&rule->list, typeof(rule), list);
-	next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
-
-	/* if no previous rule or previous rule is not early-hint, start a new response. Otherwise,
-	 * continue to add link to a previously started response */
-	if (&prev_rule->list == s->current_rule_list || prev_rule->action_ptr != http_action_early_hint) {
+	/* if there is no pending 103 response, start a new response. Otherwise,
+	 * continue to add link to a previously started response
+         */
+	if (s->txn->status != 103) {
 		struct htx_sl *sl;
 		unsigned int flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|
 				      HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
@@ -1487,6 +1484,7 @@
 		if (!sl)
 			goto error;
 		sl->info.res.status = 103;
+		s->txn->status = 103;
 	}
 
 	/* Add the HTTP Early Hint HTTP 103 response heade */
@@ -1494,18 +1492,16 @@
 	if (!htx_add_header(htx, rule->arg.http.str, ist2(b_head(value), b_data(value))))
 		goto error;
 
-	/* if it is the last rule or the next one is not an early-hint, terminate the current
-	 * response. */
-	if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint) {
-		if (!htx_add_endof(htx, HTX_BLK_EOH)) {
-			/* If an error occurred during an Early-hint rule,
-			 * remove the incomplete HTTP 103 response from the
-			 * buffer */
+	/* if it is the last rule or the next one is not an early-hint or an
+	 * conditional early-hint, terminate the current response.
+	 */
+	next_rule = LIST_NEXT(&rule->list, typeof(rule), list);
+	if (&next_rule->list == s->current_rule_list || next_rule->action_ptr != http_action_early_hint || next_rule->cond) {
+		if (!htx_add_endof(htx, HTX_BLK_EOH))
 			goto error;
-		}
-
 		if (!http_forward_proxy_resp(s, 0))
 			goto error;
+		s->txn->status = 0;
 	}
 
   leave:
@@ -1517,6 +1513,7 @@
 	 * HTTP 103 response from the buffer */
 	channel_htx_truncate(res, htx);
 	ret = ACT_RET_ERR;
+	s->txn->status = 0;
 	goto leave;
 }