Hi Ilya,

On Sat, Feb 01, 2014 at 11:33:50AM -0800, Ilya Grigorik wrote:
> Hi Eric.
> 
> 0001-MINOR-ssl-handshake-optimz-for-long-certificate-chai: works great!
> After applying this patch the full cert is sent in one RTT and without any
> extra pauses. [1]

Cool, I'm impressed to see that the SSL time has been divided by 3! Great
suggestion from you on this, thank you! I'll merge this one now.

> 0002-MINOR-ssl-Set-openssl-max_send_fragment-using-tune.s: I'm testing with
> / against openssl 1.0.1e, and it seems to work. Looking at the tcpdump, the
> packets look identical to previous runs without this patch. [2]
> 
> Any thoughts on dynamic sizing? ;)

OK I've implemented it and tested it with good success. I'm seeing several
small packets at the beginning, then large ones.

However in order to do this I am not using Emeric's 0002 patch, because we
certainly don't want to change the fragment size from the SSL stack upon
every ssl_write() in dynamic mode, so I'm back to the initial principle of
just moderating the buffer size. By using tune.ssl.maxrecord 2859, I'm
seeing a few series of two segments of 1448 and 1437 bytes respectively,
then larger ones up to 14-15kB that are coalesced by TSO on the NIC.

It seems to do what we want :-)

I'm attaching the patches if you're interested in trying it. However you'll
have to revert patch 0002.

Thanks for your tests and suggestions!
Willy

>From 64a67a3acbea1c7e82f0736502f3b8114dba6a07 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Sun, 2 Feb 2014 01:44:13 +0100
Subject: BUG/MINOR: raw_sock: correctly set the MSG_MORE flag

Due to a typo, the MSG_MORE flag used to replace MSG_NOSIGNAL and
MSG_DONTWAIT. Fortunately, sockets are always marked non-blocking,
so the loss of MSG_DONTWAIT is harmless, and the NOSIGNAL is covered
by the interception of the SIGPIPE. So no issue could have been
caused by this bug.
---
 src/raw_sock.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/raw_sock.c b/src/raw_sock.c
index fda7de1..4b125ce 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -373,7 +373,7 @@ static int raw_sock_from_buf(struct connection *conn, 
struct buffer *buf, int fl
 
                send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
                if (try < buf->o)
-                       send_flag = MSG_MORE;
+                       send_flag |= MSG_MORE;
 
                ret = send(conn->t.sock.fd, bo_ptr(buf), try, send_flag | 
flags);
 
-- 
1.7.12.2.21.g234cd45.dirty

>From 92e526d5101dbe73f29ba6610a2a7092452277d1 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Sun, 2 Feb 2014 01:51:17 +0100
Subject: MEDIUM: connection: don't use real send() flags in snd_buf()

This prevents us from passing other useful info and requires the
upper levels to know these flags. Let's use a new flags category
instead : CO_SFL_*. For now, only MSG_MORE has been remapped.
---
 include/types/connection.h | 4 ++++
 src/checks.c               | 4 ++--
 src/raw_sock.c             | 8 ++++----
 src/ssl_sock.c             | 4 ++--
 src/stream_interface.c     | 4 ++--
 5 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/include/types/connection.h b/include/types/connection.h
index eb9307f..f8e526a 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -177,6 +177,10 @@ enum {
        CO_SRC_BIND        = 0x0008,    /* bind to a specific source address 
when connecting */
 };
 
+/* flags that can be passed to xprt->snd_buf() */
+enum {
+       CO_SFL_MSG_MORE    = 0x0001,    /* More data to come afterwards */
+};
 
 /* xprt_ops describes transport-layer operations for a connection. They
  * generally run over a socket-based control layer, but not always. Some
diff --git a/src/checks.c b/src/checks.c
index 70b5a2e..a5273e7 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -962,7 +962,7 @@ static void event_srv_chk_w(struct connection *conn)
        }
 
        if (check->bo->o) {
-               conn->xprt->snd_buf(conn, check->bo, MSG_DONTWAIT | 
MSG_NOSIGNAL);
+               conn->xprt->snd_buf(conn, check->bo, 0);
                if (conn->flags & CO_FL_ERROR) {
                        chk_report_conn_err(conn, errno, 0);
                        __conn_data_stop_both(conn);
@@ -2006,7 +2006,7 @@ static void tcpcheck_main(struct connection *conn)
                     check->current_step->action != TCPCHK_ACT_SEND ||
                     check->current_step->string_len >= 
buffer_total_space(check->bo))) {
 
-                       if (conn->xprt->snd_buf(conn, check->bo, MSG_DONTWAIT | 
MSG_NOSIGNAL) <= 0) {
+                       if (conn->xprt->snd_buf(conn, check->bo, 0) <= 0) {
                                if (conn->flags & CO_FL_ERROR) {
                                        chk_report_conn_err(conn, errno, 0);
                                        __conn_data_stop_both(conn);
diff --git a/src/raw_sock.c b/src/raw_sock.c
index 4b125ce..3708b19 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -341,8 +341,8 @@ static int raw_sock_to_buf(struct connection *conn, struct 
buffer *buf, int coun
 
 
 /* Send all pending bytes from buffer <buf> to connection <conn>'s socket.
- * <flags> may contain MSG_MORE to make the system hold on without sending
- * data too fast.
+ * <flags> may contain some CO_SFL_* flags to hint the system about other
+ * pending data for example.
  * Only one call to send() is performed, unless the buffer wraps, in which case
  * a second call may be performed. The connection's flags are updated with
  * whatever special event is detected (error, empty). The caller is responsible
@@ -372,10 +372,10 @@ static int raw_sock_from_buf(struct connection *conn, 
struct buffer *buf, int fl
                        try = buf->data + try - buf->p;
 
                send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
-               if (try < buf->o)
+               if (try < buf->o || flags & CO_SFL_MSG_MORE)
                        send_flag |= MSG_MORE;
 
-               ret = send(conn->t.sock.fd, bo_ptr(buf), try, send_flag | 
flags);
+               ret = send(conn->t.sock.fd, bo_ptr(buf), try, send_flag);
 
                if (ret > 0) {
                        buf->o -= ret;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 45a6dd0..c35a180 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1482,8 +1482,8 @@ static int ssl_sock_to_buf(struct connection *conn, 
struct buffer *buf, int coun
 
 
 /* Send all pending bytes from buffer <buf> to connection <conn>'s socket.
- * <flags> may contain MSG_MORE to make the system hold on without sending
- * data too fast, but this flag is ignored at the moment.
+ * <flags> may contain some CO_SFL_* flags to hint the system about other
+ * pending data for example, but this flag is ignored at the moment.
  * Only one call to send() is performed, unless the buffer wraps, in which case
  * a second call may be performed. The connection's flags are updated with
  * whatever special event is detected (error, empty). The caller is responsible
diff --git a/src/stream_interface.c b/src/stream_interface.c
index c4d2715..967f645 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -693,13 +693,13 @@ static void si_conn_send(struct connection *conn)
                 * The test is arranged so that the most common case does only 2
                 * tests.
                 */
-               unsigned int send_flag = MSG_DONTWAIT | MSG_NOSIGNAL;
+               unsigned int send_flag = 0;
 
                if ((!(chn->flags & (CF_NEVER_WAIT|CF_SEND_DONTWAIT)) &&
                     ((chn->to_forward && chn->to_forward != 
CHN_INFINITE_FORWARD) ||
                      (chn->flags & CF_EXPECT_MORE))) ||
                    ((chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW))
-                       send_flag |= MSG_MORE;
+                       send_flag |= CO_SFL_MSG_MORE;
 
                ret = conn->xprt->snd_buf(conn, chn->buf, send_flag);
                if (ret > 0) {
-- 
1.7.12.2.21.g234cd45.dirty

>From f64e6058654f87b762bce1bc46b326cd2dbcdde0 Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w...@1wt.eu>
Date: Sun, 2 Feb 2014 02:00:24 +0100
Subject: OPTIM: ssl: implement dynamic record size adjustment

By having the stream interface pass the CF_STREAMER flag to the
snd_buf() primitive, we're able to tell the send layer whether
we're sending large chunks or small ones.

We use this information in SSL to adjust the max record dynamically.
This results in small chunks respecting tune.ssl.maxrecord at the
beginning of a transfer or for small transfers, with an automatic
switch to full records if the exchanges last long. This allows the
receiver to parse HTML contents on the fly without having to retrieve
16kB of data, which is even more important with small initcwnd since
the receiver does not need to wait for round trips to start fetching
new objects. However, sending large files still produces large chunks.

For example, with tune.ssl.maxrecord = 2859, we see 5 write(2885)
sent in two segments each and 6 write(16421).

This idea was first proposed on the haproxy mailing list by Ilya Grigorik.
---
 include/types/connection.h | 1 +
 src/ssl_sock.c             | 3 ++-
 src/stream_interface.c     | 3 +++
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/include/types/connection.h b/include/types/connection.h
index f8e526a..5341a86 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -180,6 +180,7 @@ enum {
 /* flags that can be passed to xprt->snd_buf() */
 enum {
        CO_SFL_MSG_MORE    = 0x0001,    /* More data to come afterwards */
+       CO_SFL_STREAMER    = 0x0002,    /* Producer is continuously streaming 
data */
 };
 
 /* xprt_ops describes transport-layer operations for a connection. They
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index c35a180..82ad719 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1511,7 +1511,8 @@ static int ssl_sock_from_buf(struct connection *conn, 
struct buffer *buf, int fl
        while (buf->o) {
                try = bo_contig_data(buf);
 
-               if (global.tune.ssl_max_record && try > 
global.tune.ssl_max_record)
+               if (!(flags & CO_SFL_STREAMER) &&
+                   global.tune.ssl_max_record && try > 
global.tune.ssl_max_record)
                        try = global.tune.ssl_max_record;
 
                ret = SSL_write(conn->xprt_ctx, bo_ptr(buf), try);
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 967f645..b3364a3 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -701,6 +701,9 @@ static void si_conn_send(struct connection *conn)
                    ((chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW))
                        send_flag |= CO_SFL_MSG_MORE;
 
+               if (chn->flags & CF_STREAMER)
+                       send_flag |= CO_SFL_STREAMER;
+
                ret = conn->xprt->snd_buf(conn, chn->buf, send_flag);
                if (ret > 0) {
                        chn->flags |= CF_WRITE_PARTIAL;
-- 
1.7.12.2.21.g234cd45.dirty

Reply via email to