between clients. It doesn't affect resource usage.
See also: tune.h2.initial-window-size.
+tune.h2.be.max-concurrent-streams <number>
+ Sets the HTTP/2 maximum number of concurrent streams per outgoing connection
+ (i.e. the number of outstanding requests on a single connection to a server).
+ When not set, the default set by tune.h2.max-concurrent-streams applies. A
+ smaller value than the default 100 may improve a site's responsiveness at the
+ expense of maintaining more established connections to the servers. When the
+ "http-reuse" setting is set to "always", it is recommended to reduce this
+ value so as not to mix too many different clients over the same connection,
+ because if a client is slower than others, a mechanism known as "head of
+ line blocking" tends to cause cascade effect on download speed for all
+ clients sharing a connection (keep tune.h2.be.initial-window-size low in this
+ case). It is highly recommended not to increase this value; some might find
+ it optimal to run at low values (1..5 typically).
+
tune.h2.fe.initial-window-size <number>
Sets the HTTP/2 initial window size for incoming connections, which is the
number of bytes the client can upload before waiting for an acknowledgment
clients to experience a lack of responsiveness if pages are accessed in
parallel to large uploads. See also: tune.h2.initial-window-size.
+tune.h2.fe.max-concurrent-streams <number>
+ Sets the HTTP/2 maximum number of concurrent streams per incoming connection
+ (i.e. the number of outstanding requests on a single connection from a
+ client). When not set, the default set by tune.h2.max-concurrent-streams
+ applies. A larger value than the default 100 may sometimes slightly improve
+ the page load time for complex sites with lots of small objects over high
+ latency networks but can also result in using more memory by allowing a
+ client to allocate more resources at once. The default value of 100 is
+ generally good and it is recommended not to change this value.
+
tune.h2.header-table-size <number>
Sets the HTTP/2 dynamic header table size. It defaults to 4096 bytes and
cannot be larger than 65536 bytes. A larger value may help certain clients
tune.h2.be.initial-window-size.
tune.h2.max-concurrent-streams <number>
- Sets the HTTP/2 maximum number of concurrent streams per connection (ie the
- number of outstanding requests on a single connection). The default value is
- 100. A larger one may slightly improve page load time for complex sites when
- visited over high latency networks, but increases the amount of resources a
- single client may allocate. A value of zero disables the limit so a single
- client may create as many streams as allocatable by HAProxy. It is highly
- recommended not to change this value.
+ Sets the default HTTP/2 maximum number of concurrent streams per connection
+ (i.e. the number of outstanding requests on a single connection). Ths value
+ is used for incoming connections when tune.h2.fe.max-concurrent-streams is
+ not set, and for outgoing connections when tune.h2.be.max-concurrent-streams
+ is not set. The default value is 100. The impact varies depending on the side
+ so please see the two settings above for more details. It is recommended not
+ to use this setting and to switch to the per-side ones instead. A value of
+ zero disables the limit so a single client may create as many streams as
+ allocatable by HAProxy. It is highly recommended not to change this value.
tune.h2.max-frame-size <number>
Sets the HTTP/2 maximum frame size that HAProxy announces it is willing to
static int h2_settings_initial_window_size = 65536; /* default initial value */
static int h2_be_settings_initial_window_size = 0; /* backend's default initial value */
static int h2_fe_settings_initial_window_size = 0; /* frontend's default initial value */
-static unsigned int h2_settings_max_concurrent_streams = 100;
+static unsigned int h2_settings_max_concurrent_streams = 100; /* default value */
+static unsigned int h2_be_settings_max_concurrent_streams = 0; /* backend value */
+static unsigned int h2_fe_settings_max_concurrent_streams = 0; /* frontend value */
static int h2_settings_max_frame_size = 0; /* unset */
/* a dummy closed endpoint */
return !h2c->nb_sc;
}
+/* returns the number of max concurrent streams permitted on a connection,
+ * depending on its side (frontend or backend), falling back to the default
+ * h2_settings_max_concurrent_streams. It may even be zero.
+ */
+static inline int h2c_max_concurrent_streams(const struct h2c *h2c)
+{
+ int ret;
+
+ ret = (h2c->flags & H2_CF_IS_BACK) ?
+ h2_be_settings_max_concurrent_streams :
+ h2_fe_settings_max_concurrent_streams;
+
+ ret = ret ? ret : h2_settings_max_concurrent_streams;
+ return ret;
+}
+
+
/* update h2c timeout if needed */
static void h2c_update_timeout(struct h2c *h2c)
{
/* returns true if the front connection has too many stream connectors attached */
static inline int h2_frt_has_too_many_sc(const struct h2c *h2c)
{
- return h2c->nb_sc > h2_settings_max_concurrent_streams;
+ return h2c->nb_sc > h2c_max_concurrent_streams(h2c);
}
/* Tries to grab a buffer and to re-enable processing on mux <target>. The h2c
/* Initialise the context. */
h2c->st0 = H2_CS_PREFACE;
h2c->conn = conn;
- h2c->streams_limit = h2_settings_max_concurrent_streams;
+ h2c->streams_limit = h2c_max_concurrent_streams(h2c);
h2c->max_id = -1;
h2c->errcode = H2_ERR_NO_ERROR;
h2c->rcvd_c = 0;
TRACE_ENTER(H2_EV_H2S_NEW, h2c->conn);
- if (h2c->nb_streams >= h2_settings_max_concurrent_streams) {
+ if (h2c->nb_streams >= h2c_max_concurrent_streams(h2c)) {
TRACE_ERROR("HEADERS frame causing MAX_CONCURRENT_STREAMS to be exceeded", H2_EV_H2S_NEW|H2_EV_RX_FRAME|H2_EV_RX_HDR, h2c->conn);
goto out;
}
struct buffer buf;
int iws;
int mfs;
+ int mcs;
int ret = 0;
TRACE_ENTER(H2_EV_TX_FRAME|H2_EV_TX_SETTINGS, h2c->conn);
chunk_memcat(&buf, str, 6);
}
- if (h2_settings_max_concurrent_streams != 0) {
+ mcs = h2c_max_concurrent_streams(h2c);
+ if (mcs != 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);
+ write_n32(str + 2, mcs);
chunk_memcat(&buf, str, 6);
}
case H2_SETTINGS_MAX_CONCURRENT_STREAMS:
if (h2c->flags & H2_CF_IS_BACK) {
/* the limit is only for the backend; for the frontend it is our limit */
- if ((unsigned int)arg > h2_settings_max_concurrent_streams)
- arg = h2_settings_max_concurrent_streams;
+ if ((unsigned int)arg > h2c_max_concurrent_streams(h2c))
+ arg = h2c_max_concurrent_streams(h2c);
h2c->streams_limit = arg;
}
break;
return 0;
}
-/* config parser for global "tune.h2.max-concurrent-streams" */
+/* config parser for global "tune.h2.{be.,fe.,}max-concurrent-streams" */
static int h2_parse_max_concurrent_streams(char **args, int section_type, struct proxy *curpx,
const struct proxy *defpx, const char *file, int line,
char **err)
{
+ uint *vptr;
+
if (too_many_args(1, args, err, NULL))
return -1;
- h2_settings_max_concurrent_streams = atoi(args[1]);
- if ((int)h2_settings_max_concurrent_streams < 0) {
+ /* backend/frontend/default */
+ vptr = (args[0][8] == 'b') ? &h2_be_settings_max_concurrent_streams :
+ (args[0][8] == 'f') ? &h2_fe_settings_max_concurrent_streams :
+ &h2_settings_max_concurrent_streams;
+
+ *vptr = atoi(args[1]);
+ if ((int)*vptr < 0) {
memprintf(err, "'%s' expects a positive numeric value.", args[0]);
return -1;
}
/* config keyword parsers */
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "tune.h2.be.initial-window-size", h2_parse_initial_window_size },
+ { CFG_GLOBAL, "tune.h2.be.max-concurrent-streams", h2_parse_max_concurrent_streams },
{ CFG_GLOBAL, "tune.h2.fe.initial-window-size", h2_parse_initial_window_size },
+ { CFG_GLOBAL, "tune.h2.fe.max-concurrent-streams", h2_parse_max_concurrent_streams },
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },
{ CFG_GLOBAL, "tune.h2.initial-window-size", h2_parse_initial_window_size },
{ CFG_GLOBAL, "tune.h2.max-concurrent-streams", h2_parse_max_concurrent_streams },