diff --git a/src/mux_h2.c b/src/mux_h2.c index d9f0b565f..855bd50b9 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