MINOR: mux-h2: make the H2 MAX_FRAME_SIZE setting configurable

This creates a new tunable "tune.h2.max-frame-size" to adjust the
advertised max frame size. When not set it still defaults to the buffer
size. It is convenient to advertise sizes lower than the buffer size,
for example when using very large buffers.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index d2a49cf..d1ce793 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1580,6 +1580,15 @@
   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
+  receive to its peers. The default value is the largest between 16384 and the
+  buffer size (tune.bufsize). In any case, haproxy will not announce support
+  for frame sizes larger than buffers. The main purpose of this setting is to
+  allow to limit the maximum frame size setting when using large buffers. Too
+  large frame sizes might have performance impact or cause some peers to
+  misbehave. It is highly recommended not to change this value.
+
 tune.http.cookielen <number>
   Sets the maximum length of captured cookies. This is the maximum value that
   the "capture cookie xxx len yyy" will be allowed to take, and any upper value
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 725074e..3855cb2 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -226,6 +226,7 @@
 static int h2_settings_header_table_size      =  4096; /* initial value */
 static int h2_settings_initial_window_size    = 65535; /* initial value */
 static unsigned int h2_settings_max_concurrent_streams = 100;
+static int h2_settings_max_frame_size         = 0;     /* unset */
 
 /* a dmumy closed stream */
 static const struct h2s *h2_closed_stream = &(const struct h2s){
@@ -1003,6 +1004,7 @@
 	struct buffer *res;
 	char buf_data[100]; // enough for 15 settings
 	struct buffer buf;
+	int mfs;
 	int ret;
 
 	if (h2c_mux_busy(h2c, NULL)) {
@@ -1048,14 +1050,21 @@
 		chunk_memcat(&buf, str, 6);
 	}
 
-	if (global.tune.bufsize != 16384) {
+	mfs = h2_settings_max_frame_size;
+	if (mfs > global.tune.bufsize)
+		mfs = global.tune.bufsize;
+
+	if (!mfs)
+		mfs = global.tune.bufsize;
+
+	if (mfs != 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);
+		write_n32(str + 2, mfs);
 		chunk_memcat(&buf, str, 6);
 	}
 
@@ -5479,6 +5488,22 @@
 	return 0;
 }
 
+/* config parser for global "tune.h2.max-frame-size" */
+static int h2_parse_max_frame_size(char **args, int section_type, struct proxy *curpx,
+                                   struct proxy *defpx, const char *file, int line,
+                                   char **err)
+{
+	if (too_many_args(1, args, err, NULL))
+		return -1;
+
+	h2_settings_max_frame_size = atoi(args[1]);
+	if (h2_settings_max_frame_size < 16384 || h2_settings_max_frame_size > 16777215) {
+		memprintf(err, "'%s' expects a numeric value between 16384 and 16777215.", args[0]);
+		return -1;
+	}
+	return 0;
+}
+
 
 /****************************************/
 /* MUX initialization and instanciation */
@@ -5521,6 +5546,7 @@
 	{ 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 },
+	{ CFG_GLOBAL, "tune.h2.max-frame-size",         h2_parse_max_frame_size         },
 	{ 0, NULL, NULL }
 }};