From: Willy Tarreau Date: Thu, 27 Jul 2017 15:18:30 +0000 (+0200) Subject: MEDIUM: h2: honor WINDOW_UPDATE frames X-Git-Tag: v1.8-rc1~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=26f95954fe4302a6ae558542ef9a73e9102b696c;p=thirdparty%2Fhaproxy.git MEDIUM: h2: honor WINDOW_UPDATE frames Now they really increase the window size of connections and streams. If a stream was not queued but requested to send, it means it was flow-controlled so it's added again into the connection's send list. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index d9f0b565f7..855bd50b9e 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -952,6 +952,91 @@ static int h2c_ack_ping(struct h2c *h2c) return ret; } +/* processes a WINDOW_UPDATE frame whose payload is for bytes. + * Returns > 0 on success or zero on missing data. It may return an error in + * h2c or h2s. Described in RFC7540#6.9. + */ +static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s) +{ + int32_t inc; + int error; + + if (h2c->dfl != 4) { + error = H2_ERR_FRAME_SIZE_ERROR; + goto conn_err; + } + + /* process full frame only */ + if (h2c->dbuf->i < h2c->dfl) + return 0; + + inc = h2_get_n32(h2c->dbuf, 0); + + if (h2c->dsi != 0) { + /* stream window update */ + if (h2s->st == H2_SS_IDLE) { + error = H2_ERR_PROTOCOL_ERROR; + goto conn_err; + } + + /* it's not an error to receive WU on a closed stream */ + if (h2s->st == H2_SS_CLOSED) + return 1; + + if (!inc) { + error = H2_ERR_PROTOCOL_ERROR; + goto strm_err; + } + + if (h2s->mws >= 0 && h2s->mws + inc < 0) { + error = H2_ERR_FLOW_CONTROL_ERROR; + goto strm_err; + } + + h2s->mws += inc; + if (h2s->mws > 0 && (h2s->flags & H2_SF_BLK_SFCTL)) { + h2s->flags &= ~H2_SF_BLK_SFCTL; + if (h2s->cs && LIST_ISEMPTY(&h2s->list) && + (h2s->cs->flags & CS_FL_DATA_WR_ENA)) { + /* This stream wanted to send but could not due to its + * own flow control. We can put it back into the send + * list now, it will be handled upon next send() call. + */ + LIST_ADDQ(&h2c->send_list, &h2s->list); + } + } + } + else { + /* connection window update */ + if (!inc) { + error = H2_ERR_PROTOCOL_ERROR; + goto conn_err; + } + + if (h2c->mws >= 0 && h2c->mws + inc < 0) { + error = H2_ERR_FLOW_CONTROL_ERROR; + goto conn_err; + } + + h2c->mws += inc; + } + + return 1; + + conn_err: + h2c_error(h2c, error); + return 0; + + strm_err: + if (h2s) { + h2s_error(h2s, error); + h2c->st0 = H2_CS_FRAME_A; + } + else + h2c_error(h2c, error); + return 0; +} + /* process Rx frames to be demultiplexed */ static void h2_process_demux(struct h2c *h2c) { @@ -1056,6 +1141,11 @@ static void h2_process_demux(struct h2c *h2c) ret = h2c_ack_ping(h2c); break; + case H2_FT_WINDOW_UPDATE: + if (h2c->st0 == H2_CS_FRAME_P) + ret = h2c_handle_window_update(h2c, h2s); + break; + /* FIXME: implement all supported frame types here */ default: /* drop frames that we ignore. They may be larger than