From 473e0e54f5bf48a44c202b367a6da0f0aa0b2a62 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 18 Aug 2022 16:12:15 +0200 Subject: [PATCH] BUG/MINOR: mux-h2: send a CANCEL instead of ES on truncated writes If a POST upload is cancelled after having advertised a content-length, or a response body is truncated after a content-length, we're not allowed to send ES because in this case the total body length must exactly match the advertised value. Till now that's what we were doing, and that was causing the other side (possibly haproxy) to respond with an RST_STREAM PROTOCOL_ERROR due to "ES on DATA frame before content-length". We can behave a bit cleaner here. Let's detect that we haven't sent everything, and send an RST_STREAM(CANCEL) instead, which is designed exactly for this purpose. This patch could be backported to older versions but only a little bit of exposure to make sure it doesn't wake up a bad behavior somewhere. It relies on the following previous commit: "MINOR: mux-h2: make streams know if they need to send more data" --- src/mux_h2.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mux_h2.c b/src/mux_h2.c index 611fc2105..4970fa326 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -4604,9 +4604,11 @@ static void h2_do_shutw(struct h2s *h2s) TRACE_ENTER(H2_EV_STRM_SHUT, h2c->conn, h2s); - if (h2s->st != H2_SS_ERROR && (h2s->flags & H2_SF_HEADERS_SENT)) { - /* we can cleanly close using an empty data frame only after headers */ - + if (h2s->st != H2_SS_ERROR && + (h2s->flags & (H2_SF_HEADERS_SENT | H2_SF_MORE_HTX_DATA)) == H2_SF_HEADERS_SENT) { + /* we can cleanly close using an empty data frame only after headers + * and if no more data is expected to be sent. + */ if (!(h2s->flags & (H2_SF_ES_SENT|H2_SF_RST_SENT)) && h2_send_empty_data_es(h2s) <= 0) goto add_to_list; @@ -4627,6 +4629,13 @@ static void h2_do_shutw(struct h2s *h2s) h2c_error(h2c, H2_ERR_ENHANCE_YOUR_CALM); h2s_error(h2s, H2_ERR_ENHANCE_YOUR_CALM); } + else if (h2s->flags & H2_SF_MORE_HTX_DATA) { + /* some unsent data were pending (e.g. abort during an upload), + * let's send a CANCEL. + */ + TRACE_STATE("shutw before end of data, sending CANCEL", H2_EV_STRM_SHUT, h2c->conn, h2s); + h2s_error(h2s, H2_ERR_CANCEL); + } else { /* Nothing was never sent for this stream, so reset with * REFUSED_STREAM error to let the client retry the