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"
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 611fc21..4970fa3 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -4604,9 +4604,11 @@
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 @@
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