BUG/MEDIUM: mux-quic: do not emit RESET_STREAM for unknown length

Some HTX responses may not always contain a EOM block. For example this
is the case if content-length header is missing from the HTTP server
response. Stream termination is thus signaled to QUIC mux via shutw
callback. However, this is interpreted inconditionnally as an early
close by the mux with a RESET_STREAM emission. Most of the times, QUIC
clients report this as an error.

To fix this, check if htx.extra is set to HTX_UNKOWN_PAYLOAD_LENGTH for
a qcs instance. If true, shutw will never be used to emit a
RESET_STREAM. Instead, the stream will be closed properly with a FIN
STREAM frame. If all data were already transfered, an empty STREAM frame
is sent.

This fix may help with the github issue #2004 where chrome browser stop
to use QUIC after receiving RESET_STREAM frames.

This issue was reported by Vladimir Zakharychev. Thanks to him for his
help and testing. It was also reproduced locally using httpterm with the
query string "/?s=1k&b=0&C=1".

This should be backported up to 2.7.
diff --git a/src/mux_quic.c b/src/mux_quic.c
index 74e0fc8..e6ac7ff 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -2567,8 +2567,10 @@
 	}
 
 	ret = qcs_http_snd_buf(qcs, buf, count, &fin);
-	if (fin)
+	if (fin) {
+		TRACE_STATE("reached stream fin", QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
 		qcs->flags |= QC_SF_FIN_STREAM;
+	}
 
 	if (ret || fin) {
 		qcc_send_stream(qcs, 0);
@@ -2668,21 +2670,30 @@
 static void qc_shutw(struct stconn *sc, enum co_shw_mode mode)
 {
 	struct qcs *qcs = __sc_mux_strm(sc);
-
-	TRACE_ENTER(QMUX_EV_STRM_SHUT, qcs->qcc->conn, qcs);
+	struct qcc *qcc = qcs->qcc;
 
-	/* If QC_SF_FIN_STREAM is not set and stream is not closed locally, it
-	 * means that upper layer reported an early closure. A RESET_STREAM is
-	 * necessary if not already scheduled.
-	 */
+	TRACE_ENTER(QMUX_EV_STRM_SHUT, qcc->conn, qcs);
 
+	/* Early closure reported if QC_SF_FIN_STREAM not yet set. */
 	if (!qcs_is_close_local(qcs) &&
 	    !(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_RESET))) {
-		qcc_reset_stream(qcs, 0);
-		se_fl_set_error(qcs->sd);
+
+		if (qcs->flags & QC_SF_UNKNOWN_PL_LENGTH) {
+			/* Close stream with a FIN STREAM frame. */
+			TRACE_STATE("set FIN STREAM", QMUX_EV_STRM_SHUT, qcc->conn, qcs);
+			qcs->flags |= QC_SF_FIN_STREAM;
+			qcc_send_stream(qcs, 0);
+		}
+		else {
+			/* RESET_STREAM necessary. */
+			qcc_reset_stream(qcs, 0);
+			se_fl_set_error(qcs->sd);
+		}
+
+		tasklet_wakeup(qcc->wait_event.tasklet);
 	}
 
-	TRACE_LEAVE(QMUX_EV_STRM_SHUT, qcs->qcc->conn, qcs);
+	TRACE_LEAVE(QMUX_EV_STRM_SHUT, qcc->conn, qcs);
 }
 
 /* for debugging with CLI's "show sess" command. May emit multiple lines, each