From 91bfdd7e04e105559c00f0d711842ad91ca96b26 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Thu, 14 Dec 2017 12:00:14 +0100 Subject: [PATCH] BUG/MEDIUM: h2: fix stream limit enforcement Commit 4974561 ("BUG/MEDIUM: h2: enforce the per-connection stream limit") implemented a stream limit enforcement on the connection but it was not correctly done as it would count streams still known by the connection, which includes the lingering ones that are already marked close. We need to count only the non-closed ones, which this patch does. The effect is that some streams are rejected a bit before the limit. This fix needs to be backported to 1.8. --- src/mux_h2.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/mux_h2.c b/src/mux_h2.c index 126f141bc..7f5806493 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -629,6 +629,18 @@ static inline __maybe_unused int h2_get_frame_hdr(struct buffer *b, struct h2_fh return ret; } +/* marks stream as CLOSED for connection and decrement the number + * of active streams for this connection if the stream was not yet closed. + * Please use this exclusively before closing a stream to ensure stream count + * is well maintained. + */ +static inline void h2c_stream_close(struct h2c *h2c, struct h2s *h2s) +{ + if (h2s->st != H2_SS_CLOSED) + h2s->h2c->nb_streams--; + h2s->st = H2_SS_CLOSED; +} + /* creates a new stream on the h2c connection and returns it, or NULL in * case of memory allocation error. */ @@ -902,7 +914,7 @@ static int h2s_send_rst_stream(struct h2c *h2c, struct h2s *h2s) } h2s->flags |= H2_SF_RST_SENT; - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2c, h2s); return ret; } @@ -957,7 +969,7 @@ static int h2c_send_rst_stream(struct h2c *h2c, struct h2s *h2s) if (h2s->st > H2_SS_IDLE && h2s->st < H2_SS_CLOSED) { h2s->flags |= H2_SF_RST_SENT; - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2c, h2s); } return ret; @@ -1035,7 +1047,7 @@ static void h2_wake_some_streams(struct h2c *h2c, int last, uint32_t flags) if (!h2s->cs) { /* this stream was already orphaned */ - h2c->nb_streams--; + h2c_stream_close(h2c, h2s); eb32_delete(&h2s->by_id); pool_free(pool_head_h2s, h2s); continue; @@ -1053,7 +1065,7 @@ static void h2_wake_some_streams(struct h2c *h2c, int last, uint32_t flags) else if (flags & CS_FL_EOS && h2s->st == H2_SS_OPEN) h2s->st = H2_SS_HREM; else if (flags & CS_FL_EOS && h2s->st == H2_SS_HLOC) - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2c, h2s); } } @@ -1520,7 +1532,7 @@ static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s) return 1; h2s->errcode = h2_get_n32(h2c->dbuf, 0); - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2c, h2s); if (h2s->cs) { h2s->cs->flags |= CS_FL_EOS; @@ -1999,7 +2011,7 @@ static int h2_process_mux(struct h2c *h2c) h2s->cs->flags &= ~CS_FL_DATA_WR_ENA; else { /* just sent the last frame for this orphaned stream */ - h2c->nb_streams--; + h2c_stream_close(h2c, h2s); eb32_delete(&h2s->by_id); pool_free(pool_head_h2s, h2s); } @@ -2042,7 +2054,7 @@ static int h2_process_mux(struct h2c *h2c) h2s->cs->flags &= ~CS_FL_DATA_WR_ENA; else { /* just sent the last frame for this orphaned stream */ - h2c->nb_streams--; + h2c_stream_close(h2c, h2s); eb32_delete(&h2s->by_id); pool_free(pool_head_h2s, h2s); } @@ -2388,7 +2400,7 @@ static void h2_detach(struct conn_stream *cs) if (h2s->by_id.node.leaf_p) { /* h2s still attached to the h2c */ - h2c->nb_streams--; + h2c_stream_close(h2c, h2s); eb32_delete(&h2s->by_id); /* We don't want to close right now unless we're removing the @@ -2444,7 +2456,7 @@ static void h2_shutr(struct conn_stream *cs, enum cs_shr_mode mode) if (h2s->h2c->mbuf->o && !(cs->conn->flags & CO_FL_XPRT_WR_ENA)) conn_xprt_want_send(cs->conn); - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2s->h2c, h2s); } static void h2_shutw(struct conn_stream *cs, enum cs_shw_mode mode) @@ -2462,7 +2474,7 @@ static void h2_shutw(struct conn_stream *cs, enum cs_shw_mode mode) return; if (h2s->st == H2_SS_HREM) - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2s->h2c, h2s); else h2s->st = H2_SS_HLOC; } else { @@ -2480,7 +2492,7 @@ static void h2_shutw(struct conn_stream *cs, enum cs_shw_mode mode) h2c_send_goaway_error(h2s->h2c, h2s) <= 0) return; - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2s->h2c, h2s); } if (h2s->h2c->mbuf->o && !(cs->conn->flags & CO_FL_XPRT_WR_ENA)) @@ -2928,7 +2940,7 @@ static int h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf) if (h2s->st == H2_SS_OPEN) h2s->st = H2_SS_HLOC; else - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2c, h2s); } else if (h1m->status >= 100 && h1m->status < 200) { /* we'll let the caller check if it has more headers to send */ @@ -3170,7 +3182,7 @@ static int h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf) if (h2s->st == H2_SS_OPEN) h2s->st = H2_SS_HLOC; else - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2c, h2s); if (!(h1m->flags & H1_MF_CHNK)) h1m->state = HTTP_MSG_DONE; @@ -3235,7 +3247,7 @@ static int h2_snd_buf(struct conn_stream *cs, struct buffer *buf, int flags) if (h2s->st == H2_SS_ERROR || h2s->flags & H2_SF_RST_RCVD) { cs->flags |= CS_FL_ERROR; if (h2s_send_rst_stream(h2s->h2c, h2s) > 0) - h2s->st = H2_SS_CLOSED; + h2c_stream_close(h2s->h2c, h2s); } if (h2s->flags & H2_SF_BLK_SFCTL) {