BUG/MINOR: http-ana/filters: Wait end of the http_end callback for all filters

Filters may define the "http_end" callback, called at the end of the analysis of
any HTTP messages. It is called at the end of the payload forwarding and it can
interrupt the stream processing. So we must be sure to not remove the XFER_BODY
analyzers while there is still at least filter in progress on this callback.

Unfortunatly, once the request and the response are borh in the DONE or the
TUNNEL mode, we consider the XFER_BODY analyzer has finished its processing on
both sides. So it is possible to prematurely interrupt the execution of the
filters "http_end" callback.

To fix this bug, we switch a message in the ENDING state. It is then switched in
DONE/TUNNEL mode only after the execution of the filters "http_end" callback.

This patch must be backported (and adapted) to 2.1, 2.0 and 1.9. The legacy HTTP
mode shoud probaly be fixed too.

(cherry picked from commit 1a3e0279c6079174288e2e3fbbf09e530ff221c5)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 8ece0801d813d6f821dabde13f7a74759dd95ee4)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/proto_htx.c b/src/proto_htx.c
index c75bc27..1ad4aa8 100644
--- a/src/proto_htx.c
+++ b/src/proto_htx.c
@@ -1204,11 +1204,8 @@
 
 	if (req->to_forward) {
 		if (req->to_forward == CHN_INFINITE_FORWARD) {
-			if (req->flags & CF_EOI) {
-				msg->msg_state = HTTP_MSG_DONE;
-				req->to_forward = 0;
-				goto done;
-			}
+			if (req->flags & CF_EOI)
+				msg->msg_state = HTTP_MSG_ENDING;
 		}
 		else {
 			/* We can't process the buffer's contents yet */
@@ -1217,8 +1214,14 @@
 		}
 	}
 
-	if (msg->msg_state >= HTTP_MSG_DONE)
-		goto done;
+	if (msg->msg_state >= HTTP_MSG_ENDING)
+		goto ending;
+
+	if (txn->meth == HTTP_METH_CONNECT) {
+		msg->msg_state = HTTP_MSG_ENDING;
+		goto ending;
+	}
+
 	/* Forward input data. We get it by removing all outgoing data not
 	 * forwarded yet from HTX data size. If there are some data filters, we
 	 * let them decide the amount of data to forward.
@@ -1235,11 +1238,8 @@
 			channel_htx_forward_forever(req, htx);
 	}
 
-	if (txn->meth == HTTP_METH_CONNECT) {
-		msg->msg_state = HTTP_MSG_TUNNEL;
-		goto done;
-	}
-
+	if (htx->data != co_data(req))
+		goto missing_data_or_waiting;
 
 	/* Check if the end-of-message is reached and if so, switch the message
 	 * in HTTP_MSG_ENDING state. Then if all data was marked to be
@@ -1249,16 +1249,11 @@
 		goto missing_data_or_waiting;
 
 	msg->msg_state = HTTP_MSG_ENDING;
-	if (htx->data != co_data(req))
-		goto missing_data_or_waiting;
-	msg->msg_state = HTTP_MSG_DONE;
-	req->to_forward = 0;
 
-  done:
-	/* other states, DONE...TUNNEL */
-	/* we don't want to forward closes on DONE except in tunnel mode. */
-	if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
-		channel_dont_close(req);
+  ending:
+	/* other states, ENDING...TUNNEL */
+	if (msg->msg_state >= HTTP_MSG_DONE)
+		goto done;
 
 	if (HAS_REQ_DATA_FILTERS(s)) {
 		ret = flt_http_end(s, msg);
@@ -1269,6 +1264,18 @@
 		}
 	}
 
+	if (txn->meth == HTTP_METH_CONNECT)
+		msg->msg_state = HTTP_MSG_TUNNEL;
+	else {
+		msg->msg_state = HTTP_MSG_DONE;
+		req->to_forward = 0;
+	}
+
+  done:
+	/* we don't want to forward closes on DONE except in tunnel mode. */
+	if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
+		channel_dont_close(req);
+
 	htx_end_request(s);
 	if (!(req->analysers & an_bit)) {
 		htx_end_response(s);
@@ -2205,11 +2212,8 @@
 
 	if (res->to_forward) {
 		if (res->to_forward == CHN_INFINITE_FORWARD) {
-			if (res->flags & CF_EOI) {
-				msg->msg_state = HTTP_MSG_DONE;
-				res->to_forward = 0;
-				goto done;
-			}
+			if (res->flags & CF_EOI)
+				msg->msg_state = HTTP_MSG_ENDING;
 		}
 		else {
 			/* We can't process the buffer's contents yet */
@@ -2218,8 +2222,14 @@
 		}
 	}
 
-	if (msg->msg_state >= HTTP_MSG_DONE)
-		goto done;
+	if (msg->msg_state >= HTTP_MSG_ENDING)
+		goto ending;
+
+	if ((txn->meth == HTTP_METH_CONNECT && txn->status == 200) || txn->status == 101 ||
+	    (!(msg->flags & HTTP_MSGF_XFER_LEN) && !HAS_RSP_DATA_FILTERS(s))) {
+		msg->msg_state = HTTP_MSG_ENDING;
+		goto ending;
+	}
 
 	/* Forward input data. We get it by removing all outgoing data not
 	 * forwarded yet from HTX data size. If there are some data filters, we
@@ -2237,10 +2247,12 @@
 			channel_htx_forward_forever(res, htx);
 	}
 
-	if ((txn->meth == HTTP_METH_CONNECT && txn->status == 200) || txn->status == 101 ||
-	    (!(msg->flags & HTTP_MSGF_XFER_LEN) && (res->flags & CF_SHUTR || !HAS_RSP_DATA_FILTERS(s)))) {
-		msg->msg_state = HTTP_MSG_TUNNEL;
-		goto done;
+	if (htx->data != co_data(res))
+		goto missing_data_or_waiting;
+
+	if (!(msg->flags & HTTP_MSGF_XFER_LEN) && res->flags & CF_SHUTR) {
+		msg->msg_state = HTTP_MSG_ENDING;
+		goto ending;
 	}
 
 	/* Check if the end-of-message is reached and if so, switch the message
@@ -2251,14 +2263,11 @@
 		goto missing_data_or_waiting;
 
 	msg->msg_state = HTTP_MSG_ENDING;
-	if (htx->data != co_data(res))
-		goto missing_data_or_waiting;
-	msg->msg_state = HTTP_MSG_DONE;
-	res->to_forward = 0;
 
-  done:
-	/* other states, DONE...TUNNEL */
-	channel_dont_close(res);
+  ending:
+	/* other states, ENDING...TUNNEL */
+	if (msg->msg_state >= HTTP_MSG_DONE)
+		goto done;
 
 	if (HAS_RSP_DATA_FILTERS(s)) {
 		ret = flt_http_end(s, msg);
@@ -2269,6 +2278,20 @@
 		}
 	}
 
+	if ((txn->meth == HTTP_METH_CONNECT && txn->status == 200) || txn->status == 101 ||
+	    !(msg->flags & HTTP_MSGF_XFER_LEN)) {
+		msg->msg_state = HTTP_MSG_TUNNEL;
+		goto ending;
+	}
+	else {
+		msg->msg_state = HTTP_MSG_DONE;
+		res->to_forward = 0;
+	}
+
+  done:
+
+	channel_dont_close(res);
+
 	htx_end_response(s);
 	if (!(res->analysers & an_bit)) {
 		htx_end_request(s);