MINOR: ssl: add a new global option "tune.ssl.hard-maxrecord"

Low footprint client machines may not have enough memory to download a
complete 16KB TLS record at once. With the new option the maximum
record size can be defined on the server side.

Note: Before limiting the the record size on the server side, a client should
consider using the TLS Maximum Fragment Length Negotiation Extension defined
in RFC6066.

This patch fixes GitHub issue #1679.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 4f7fc4a..aa422f0 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1118,9 +1118,10 @@
    - tune.sndbuf.client
    - tune.sndbuf.server
    - tune.ssl.cachesize
+   - tune.ssl.force-private-cache
+   - tune.ssl.hard-maxrecord
    - tune.ssl.keylog
    - tune.ssl.lifetime
-   - tune.ssl.force-private-cache
    - tune.ssl.maxrecord
    - tune.ssl.default-dh-param
    - tune.ssl.ssl-ctx-cache-size
@@ -2903,6 +2904,12 @@
   this case, adding a first layer of hash-based load balancing before the SSL
   layer might limit the impact of the lack of session sharing.
 
+tune.ssl.hard-maxrecord <number>
+  Sets the maximum amount of bytes passed to SSL_write() at any time. Default
+  value 0 means there is no limit. In contrast to tune.ssl.maxrecord this
+  settings will not be adjusted dynamically. Smaller records may decrease
+  throughput, but may be required when dealing with low-footprint clients.
+
 tune.ssl.keylog { on | off }
   This option activates the logging of the TLS keys. It should be used with
   care as it will consume more memory per SSL session and could decrease
@@ -2950,18 +2957,19 @@
   being used for too long.
 
 tune.ssl.maxrecord <number>
-  Sets the maximum amount of bytes passed to SSL_write() at a time. Default
-  value 0 means there is no limit. Over SSL/TLS, the client can decipher the
-  data only once it has received a full record. With large records, it means
-  that clients might have to download up to 16kB of data before starting to
-  process them. Limiting the value can improve page load times on browsers
-  located over high latency or low bandwidth networks. It is suggested to find
-  optimal values which fit into 1 or 2 TCP segments (generally 1448 bytes over
-  Ethernet with TCP timestamps enabled, or 1460 when timestamps are disabled),
-  keeping in mind that SSL/TLS add some overhead. Typical values of 1419 and
-  2859 gave good results during tests. Use "strace -e trace=write" to find the
-  best value. HAProxy will automatically switch to this setting after an idle
-  stream has been detected (see tune.idletimer above).
+  Sets the maximum amount of bytes passed to SSL_write() at the beginning of
+  the data transfer. Default value 0 means there is no limit. Over SSL/TLS,
+  the client can decipher the data only once it has received a full record.
+  With large records, it means that clients might have to download up to 16kB
+  of data before starting to process them. Limiting the value can improve page
+  load times on browsers located over high latency or low bandwidth networks.
+  It is suggested to find optimal values which fit into 1 or 2 TCP segments
+  (generally 1448 bytes over Ethernet with TCP timestamps enabled, or 1460 when
+  timestamps are disabled), keeping in mind that SSL/TLS add some overhead.
+  Typical values of 1419 and 2859 gave good results during tests. Use
+  "strace -e trace=write" to find the best value. HAProxy will automatically
+  switch to this setting after an idle stream has been detected (see
+  tune.idletimer above). See also tune.ssl.hard-maxrecord.
 
 tune.ssl.default-dh-param <number>
   Sets the maximum size of the Diffie-Hellman parameters used for generating
diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h
index fab8dec..b404def 100644
--- a/include/haproxy/ssl_sock-t.h
+++ b/include/haproxy/ssl_sock-t.h
@@ -275,6 +275,7 @@
 	int private_cache; /* Force to use a private session cache even if nbproc > 1 */
 	unsigned int life_time;   /* SSL session lifetime in seconds */
 	unsigned int max_record; /* SSL max record size */
+	unsigned int hard_max_record; /* SSL max record size hard limit */
 	unsigned int default_dh_param; /* SSL maximum DH parameter size */
 	int ctx_cache; /* max number of entries in the ssl_ctx cache. */
 	int capture_buffer_size; /* Size of the capture buffer. */
diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c
index 831a3ca..7acd135 100644
--- a/src/cfgparse-ssl.c
+++ b/src/cfgparse-ssl.c
@@ -268,6 +268,8 @@
 		target = &global.tune.sslcachesize;
 	else if (strcmp(args[0], "tune.ssl.maxrecord") == 0)
 		target = (int *)&global_ssl.max_record;
+	else if (strcmp(args[0], "tune.ssl.hard-maxrecord") == 0)
+		target = (int *)&global_ssl.hard_max_record;
 	else if (strcmp(args[0], "tune.ssl.ssl-ctx-cache-size") == 0)
 		target = &global_ssl.ctx_cache;
 	else if (strcmp(args[0], "maxsslconn") == 0)
@@ -1942,6 +1944,7 @@
 	{ CFG_GLOBAL, "tune.ssl.force-private-cache",  ssl_parse_global_private_cache },
 	{ CFG_GLOBAL, "tune.ssl.lifetime", ssl_parse_global_lifetime },
 	{ CFG_GLOBAL, "tune.ssl.maxrecord", ssl_parse_global_int },
+	{ CFG_GLOBAL, "tune.ssl.hard-maxrecord", ssl_parse_global_int },
 	{ CFG_GLOBAL, "tune.ssl.ssl-ctx-cache-size", ssl_parse_global_int },
 	{ CFG_GLOBAL, "tune.ssl.capture-cipherlist-size", ssl_parse_global_capture_buffer },
 	{ CFG_GLOBAL, "tune.ssl.capture-buffer-size", ssl_parse_global_capture_buffer },
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 19e41fd..f88f241 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -122,6 +122,7 @@
 #ifdef DEFAULT_SSL_MAX_RECORD
 	.max_record = DEFAULT_SSL_MAX_RECORD,
 #endif
+	.hard_max_record = 0,
 	.default_dh_param = SSL_DEFAULT_DH_PARAM,
 	.ctx_cache = DEFAULT_SSL_CTX_CACHE,
 	.capture_buffer_size = 0,
@@ -6568,6 +6569,9 @@
 		if (try > count)
 			try = count;
 
+		if (global_ssl.hard_max_record && try > global_ssl.hard_max_record)
+			try = global_ssl.hard_max_record;
+
 		if (!(flags & CO_SFL_STREAMER) &&
 		    !(ctx->xprt_st & SSL_SOCK_SEND_UNLIMITED) &&
 		    global_ssl.max_record && try > global_ssl.max_record) {