From: Willy Tarreau Date: Mon, 25 Sep 2017 14:25:39 +0000 (+0200) Subject: MINOR: h2: send a real SETTINGS frame based on the configuration X-Git-Tag: v1.8-rc1~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=be5b715fb2c5ff92448f9481d18cfee000cef6c9;p=thirdparty%2Fhaproxy.git MINOR: h2: send a real SETTINGS frame based on the configuration An initial settings frame is emitted upon receipt of the connection preface, which takes care of configured values. These settings are only emitted when they differ from the protocol's default value : - header_table_size (defaults to 4096) - initial_window_size (defaults to 65535) - max_concurrent_streams (defaults to unlimited) - max_frame_size (defaults to 16384) The max frame size is a copy of tune.bufsize. Clients will most often reject values lower than 16384 and currently there's no trivial way to check if H2 is going to be used at boot time. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index c8afadc276..d2d8574fa3 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -581,6 +581,87 @@ static struct h2s *h2c_stream_new(struct h2c *h2c, int id) return h2s; } +/* try to send a settings frame on the connection. Returns > 0 on success, 0 if + * it couldn't do anything. It may return an error in h2c. See RFC7540#11.3 for + * the various settings codes. + */ +static int h2c_snd_settings(struct h2c *h2c) +{ + struct buffer *res; + char buf_data[100]; // enough for 15 settings + struct chunk buf; + int ret; + + if (h2c_mux_busy(h2c, NULL)) { + h2c->flags |= H2_CF_DEM_MBUSY; + return 0; + } + + res = h2_get_mbuf(h2c); + if (!res) { + h2c->flags |= H2_CF_MUX_MALLOC; + h2c->flags |= H2_CF_DEM_MROOM; + return 0; + } + + chunk_init(&buf, buf_data, sizeof(buf_data)); + chunk_memcpy(&buf, + "\x00\x00\x00" /* length : 0 for now */ + "\x04\x00" /* type : 4 (settings), flags : 0 */ + "\x00\x00\x00\x00", /* stream ID : 0 */ + 9); + + if (h2_settings_header_table_size != 4096) { + char str[6] = "\x00\x01"; /* header_table_size */ + + write_n32(str + 2, h2_settings_header_table_size); + chunk_memcat(&buf, str, 6); + } + + if (h2_settings_initial_window_size != 65535) { + char str[6] = "\x00\x04"; /* initial_window_size */ + + write_n32(str + 2, h2_settings_initial_window_size); + chunk_memcat(&buf, str, 6); + } + + if (h2_settings_max_concurrent_streams != 0) { + char str[6] = "\x00\x03"; /* max_concurrent_streams */ + + /* Note: 0 means "unlimited" for haproxy's config but not for + * the protocol, so never send this value! + */ + write_n32(str + 2, h2_settings_max_concurrent_streams); + chunk_memcat(&buf, str, 6); + } + + if (global.tune.bufsize != 16384) { + char str[6] = "\x00\x05"; /* max_frame_size */ + + /* note: similarly we could also emit MAX_HEADER_LIST_SIZE to + * match bufsize - rewrite size, but at the moment it seems + * that clients don't take care of it. + */ + write_n32(str + 2, global.tune.bufsize); + chunk_memcat(&buf, str, 6); + } + + h2_set_frame_size(buf.str, buf.len - 9); + ret = bo_istput(res, ist2(buf.str, buf.len)); + if (unlikely(ret <= 0)) { + if (!ret) { + h2c->flags |= H2_CF_MUX_MFULL; + h2c->flags |= H2_CF_DEM_MROOM; + return 0; + } + else { + h2c_error(h2c, H2_ERR_INTERNAL_ERROR); + return 0; + } + } + return ret; +} + /* Try to receive a connection preface, then upon success try to send our * preface which is a SETTINGS frame. Returns > 0 on success or zero on * missing data. It may return an error in h2c. @@ -588,6 +669,7 @@ static struct h2s *h2c_stream_new(struct h2c *h2c, int id) static int h2c_frt_recv_preface(struct h2c *h2c) { int ret1; + int ret2; ret1 = b_isteq(h2c->dbuf, 0, h2c->dbuf->i, ist(H2_CONN_PREFACE)); @@ -597,9 +679,11 @@ static int h2c_frt_recv_preface(struct h2c *h2c) return 0; } - bi_del(h2c->dbuf, ret1); + ret2 = h2c_snd_settings(h2c); + if (ret2 > 0) + bi_del(h2c->dbuf, ret1); - return ret1; + return ret2; } /* try to send a GOAWAY frame on the connection to report an error or a graceful