From: Willy Tarreau Date: Tue, 17 Oct 2017 06:10:17 +0000 (+0200) Subject: MINOR: h2: implement h2_send_rst_stream() to send RST_STREAM frames X-Git-Tag: v1.8-rc1~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=27a84c90ce57da78dff382d99d7829e90aee524a;p=thirdparty%2Fhaproxy.git MINOR: h2: implement h2_send_rst_stream() to send RST_STREAM frames This one sends an RST_STREAM for a given stream, using the current demux stream ID. It's also used to send RST_STREAM for streams which have lost their CS part (ie were aborted). --- diff --git a/src/mux_h2.c b/src/mux_h2.c index 855bd50b9e..72a4b507e8 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -753,6 +753,64 @@ static int h2c_send_goaway_error(struct h2c *h2c, struct h2s *h2s) return ret; } +/* try to send an RST_STREAM frame on the connection for the current demuxed + * stream to report an error, with h2s->errcode as the error code. Returns > 0 + * on success or zero if nothing was done. It uses h2c->dsi as the stream ID + * and h2s->errcode for the error code. In case of lack of room to write the + * message, it subscribes the requester (either or ) to future + * notifications. It's worth mentionning that an RST may even be sent for a + * closed stream with error 0 in this case. + */ +static int h2c_send_rst_stream(struct h2c *h2c, struct h2s *h2s) +{ + struct buffer *res; + char str[13]; + int ret; + + if (h2c_mux_busy(h2c, h2s)) { + if (h2s) + h2s->flags |= H2_SF_BLK_MBUSY; + else + h2c->flags |= H2_CF_DEM_MBUSY; + return 0; + } + + res = h2_get_mbuf(h2c); + if (!res) { + h2c->flags |= H2_CF_MUX_MALLOC; + if (h2s) + h2s->flags |= H2_SF_BLK_MROOM; + else + h2c->flags |= H2_CF_DEM_MROOM; + return 0; + } + + /* len: 4, type: 3, flags: none */ + memcpy(str, "\x00\x00\x04\x03\x00", 5); + write_n32(str + 5, h2c->dsi); + write_n32(str + 9, (h2s->st > H2_SS_IDLE && h2s->st < H2_SS_RESET) ? + h2s->errcode : H2_ERR_STREAM_CLOSED); + ret = bo_istput(res, ist2(str, 13)); + if (unlikely(ret <= 0)) { + if (!ret) { + h2c->flags |= H2_CF_MUX_MFULL; + if (h2s) + h2s->flags |= H2_SF_BLK_MROOM; + else + h2c->flags |= H2_CF_DEM_MROOM; + return 0; + } + else { + h2c_error(h2c, H2_ERR_INTERNAL_ERROR); + return 0; + } + } + + if (h2s) + h2s->flags |= H2_SF_RST_SENT; + return ret; +} + /* Increase all streams' outgoing window size by the difference passed in * argument. This is needed upon receipt of the settings frame if the initial * window size is different. The difference may be negative and the resulting @@ -1158,6 +1216,15 @@ static void h2_process_demux(struct h2c *h2c) ret = h2c->dfl == 0; } + /* RST are sent similarly to frame acks */ + if (h2s->st == H2_SS_ERROR) { + if (h2c->st0 == H2_CS_FRAME_P) + h2c->st0 = H2_CS_FRAME_A; + + if (h2c->st0 == H2_CS_FRAME_A) + ret = h2c_send_rst_stream(h2c, h2s); + } + /* error or missing data condition met above ? */ if (ret <= 0) break; @@ -1203,6 +1270,8 @@ static int h2_process_mux(struct h2c *h2c) if (h2s->cs) { h2s->cs->data_cb->send(h2s->cs); h2s->cs->data_cb->wake(h2s->cs); + } else { + h2c_send_rst_stream(h2c, h2s); } /* depending on callee's blocking reasons, we may queue in send @@ -1239,6 +1308,8 @@ static int h2_process_mux(struct h2c *h2c) if (h2s->cs) { h2s->cs->data_cb->send(h2s->cs); h2s->cs->data_cb->wake(h2s->cs); + } else { + h2c_send_rst_stream(h2c, h2s); } /* depending on callee's blocking reasons, we may queue in fctl * list or completely dequeue.