From: Willy Tarreau Date: Thu, 21 Feb 2019 17:16:35 +0000 (+0100) Subject: BUG/MEDIUM: mux-h2/htx: send an empty DATA frame on empty HTX trailers X-Git-Tag: v2.0-dev1~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=67b8caefc95ccd885cb82c6bd031fe385a0538b1;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: mux-h2/htx: send an empty DATA frame on empty HTX trailers When chunked-encoding is used in HTX mode, a trailers HTX block is always made due to the way trailers are currently implemented (verbatim copy of the H1 representation). Because of this it's not possible to know when processing data that we've reached the end of the stream, and it's up to the function encoding the trailers (h2s_htx_make_trailers) to put the end of stream. But when there are no trailers and only an empty HTX block, this one cannot produce a HEADERS frame, thus it cannot send the END_STREAM flag either, leaving the other end with an incomplete message, waiting for either more data or some trailers. This is particularly visible with POST requests where the server continues to wait. What this patch does is transform the HEADERS frame into an empty DATA frame when meeting an empty trailers block. It is possible to do this because we've not sent any trailers so the other end is still waiting for DATA frames. The check is made after attempting to encode the list of headers, so as to minimize the specific code paths. Thanks to Dragan Dosen for reporting the issue with a reproducer. This fix must be backported to 1.9. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index 3855cb2804..5ae2297a04 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -4933,9 +4933,6 @@ static size_t h2s_htx_make_trailers(struct h2s *h2s, struct htx *htx) if (!blk_end) goto end; // end not found yet - if (!hdr) - goto done; - chunk_reset(&outbuf); while (1) { @@ -4980,6 +4977,16 @@ static size_t h2s_htx_make_trailers(struct h2s *h2s, struct htx *htx) } } + if (!hdr) { + /* here we have a problem, we've received an empty trailers + * block followed by an EOM. Because of this we can't send a + * HEADERS frame, so we have to cheat and instead send an empty + * DATA frame conveying the ES flag. + */ + outbuf.area[3] = H2_FT_DATA; + outbuf.area[4] = H2_F_DATA_END_STREAM; + } + /* update the frame's size */ h2_set_frame_size(outbuf.area, outbuf.data - 9);