]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: mux-quic: do not close STREAM with empty FIN if no data sent
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 18 Oct 2024 15:46:06 +0000 (17:46 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 21 Oct 2024 09:24:38 +0000 (11:24 +0200)
A stream may be shut without any HTX EOM reported to report a proper
closure. This is the case for QCS instances flagged with
QC_SF_UNKNOWN_PL_LENGTH. Shut is performed with an empty FIN emission
instead of a RESET_STREAM. This has been implemented since the following
patch :

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

However, in case of HTTP/3, an empty FIN should only be done after a
full message is emitted, which requires at least a HEADERS frame. If an
empty FIN is emitted without it, client may interpret this as invalid
and close the connection. To prevent this, fallback to a RESET_STREAM
emission if no data were emitted on the stream.

This was reproduced using ngtcp2-client with 10% loss (-r 0.1) on a
remote host, with httpterm request "/?s=100k&C=1&b=0&P=400". An error
ERR_H3_FRAME_UNEXPECTED is returned by ngtcp2-client when the bug
occurs.

Note that this change is incomplete. The message validity depends solely
on the application protocol in use. As such, a new app_ops callback
should be implemented to ensure the stream is closed accordingly.
However, this first patch ensures that at least HTTP/3 case is valid
while keeping a minimal backport process.

This should be backported up to 2.8.

src/mux_quic.c

index ea8c38b5f7679cb1717a8e45be4920b8acb16d6f..302dc155b27db63acd6bd609d41da09908fa4912 100644 (file)
@@ -3359,8 +3359,12 @@ static void qmux_strm_shut(struct stconn *sc, unsigned int mode, struct se_abort
        if (!qcs_is_close_local(qcs) &&
            !(qcs->flags & (QC_SF_FIN_STREAM|QC_SF_TO_RESET))) {
 
-               if (qcs->flags & QC_SF_UNKNOWN_PL_LENGTH) {
-                       /* Close stream with a FIN STREAM frame. */
+               /* Close stream with FIN if length unknown and some data are
+                * ready to be/already transmitted.
+                * TODO select closure method on app proto layer
+                */
+               if (qcs->flags & QC_SF_UNKNOWN_PL_LENGTH &&
+                   qcs->tx.fc.off_soft) {
                        if (!(qcc->flags & (QC_CF_ERR_CONN|QC_CF_ERRL))) {
                                TRACE_STATE("set FIN STREAM",
                                            QMUX_EV_STRM_SHUT, qcc->conn, qcs);
@@ -3485,7 +3489,7 @@ static const struct mux_ops qmux_ops = {
        .subscribe   = qmux_strm_subscribe,
        .unsubscribe = qmux_strm_unsubscribe,
        .wake        = qmux_wake,
-       .shut       = qmux_strm_shut,
+       .shut        = qmux_strm_shut,
        .ctl         = qmux_ctl,
        .sctl        = qmux_sctl,
        .show_sd     = qmux_strm_show_sd,