]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: h2: Only report early HTX EOM for tunneled streams
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 1 Aug 2024 14:01:50 +0000 (16:01 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 2 Aug 2024 06:42:28 +0000 (08:42 +0200)
For regular H2 messages, the HTX EOM flag is synonymous the end of input. So
SE_FL_EOI flag must also be set on the stream-endpoint descriptor. However,
there is an exception. For tunneled streams, the end of message is reported
on the HTX message just after the headers. But in that case, no end of input
is reported on the SE.

But here, there is a bug. The "early" EOM is also report on the HTX messages
when there is no payload (for instance a content-length set to 0). If there
is no ES flag on the H2 HEADERS frame, it is an unexpected case. Because for
the applicative stream and most probably for the opposite endpoint, the
message is considered as finihsed. It is switched in its DONE state (or the
equivalent on the endpoint). But, if an extra H2 frame with the ES flag is
received, a TRAILERS frame or an emtpy DATA frame, an extra EOT HTX block is
pushed to carry the HTX EOM flag. So an extra HTX block is emitted for a
regular HTX message. It is totally invalid, it must never happen.

Because it is an undefined behavior, it is difficult to predict the result.
But it definitly prevent the applicative stream to properly handle aborts
and errors because data remain blocked in the channel buffer. Indeed, the
end of the message was seen, so no more data are forwarded.

It seems to be an issue for 2.8 and upper. Harder to evaluate for older
versions.

This patch must be backported as far as 2.4.

src/h2.c

index c2b41a8485dadfa82053f975725e93e13b4a816d..a9e8b65b1933f2a37ab311a94e875f9325274d89 100644 (file)
--- a/src/h2.c
+++ b/src/h2.c
@@ -456,7 +456,8 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms
            (*msgf & H2_MSGF_BODY_TUNNEL)) {
                /* Request without body or tunnel requested */
                sl_flags |= HTX_SL_F_BODYLESS;
-               htx->flags |= HTX_FL_EOM;
+               if (*msgf & H2_MSGF_BODY_TUNNEL)
+                   htx->flags |= HTX_FL_EOM;
        }
 
        if (*msgf & H2_MSGF_EXT_CONNECT) {
@@ -730,7 +731,8 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m
            (*msgf & H2_MSGF_BODY_TUNNEL)) {
                /* Response without body or tunnel successfully established */
                sl_flags |= HTX_SL_F_BODYLESS;
-               htx->flags |= HTX_FL_EOM;
+               if (*msgf & H2_MSGF_BODY_TUNNEL)
+                   htx->flags |= HTX_FL_EOM;
        }
 
        /* update the start line with last detected header info */