Blame SOURCES/0007-window_size-redid-window-handling-for-flow-control-r.patch

6fd6a0
From 8af6637d86b6a85e8889c286f7ff3d841fc5621c Mon Sep 17 00:00:00 2001
6fd6a0
From: Salvador Fandino <sfandino@yahoo.com>
6fd6a0
Date: Sat, 12 Oct 2013 02:51:46 +0200
6fd6a0
Subject: [PATCH 07/11] window_size: redid window handling for flow control reasons
6fd6a0
6fd6a0
Until now, the window size (channel->remote.window_size) was being
6fd6a0
updated just after receiving the packet from the transport layer.
6fd6a0
6fd6a0
That behaviour is wrong because the channel queue may grow uncontrolled
6fd6a0
when data arrives from the network faster that the upper layer consumes
6fd6a0
it.
6fd6a0
6fd6a0
This patch adds a new counter, read_avail, which keeps a count of the
6fd6a0
bytes available from the packet queue for reading. Also, now the window
6fd6a0
size is adjusted when the data is actually read by an upper layer.
6fd6a0
6fd6a0
That way, if the upper layer stops reading data, the window will
6fd6a0
eventually fill and the remote host will stop sending data. When the
6fd6a0
upper layers reads enough data, a window adjust packet is delivered and
6fd6a0
the transfer resumes.
6fd6a0
6fd6a0
The read_avail counter is used to detect the situation when the remote
6fd6a0
server tries to send data surpassing the window size. In that case, the
6fd6a0
extra data is discarded.
6fd6a0
6fd6a0
Signed-off-by: Salvador <sfandino@yahoo.com>
6fd6a0
6fd6a0
[upstream commit cdeef54967ed5b7d5bd8fa6da5851aa3d173faa0]
6fd6a0
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
6fd6a0
---
6fd6a0
 src/channel.c      |    8 +++++++-
6fd6a0
 src/libssh2_priv.h |    2 ++
6fd6a0
 src/packet.c       |   35 ++++++++++++++++++++++++++++-------
6fd6a0
 3 files changed, 37 insertions(+), 8 deletions(-)
6fd6a0
6fd6a0
diff --git a/src/channel.c b/src/channel.c
6fd6a0
index 74262d8..499d815 100644
6fd6a0
--- a/src/channel.c
6fd6a0
+++ b/src/channel.c
6fd6a0
@@ -1411,6 +1411,9 @@ _libssh2_channel_flush(LIBSSH2_CHANNEL *channel, int streamid)
6fd6a0
         channel->flush_state = libssh2_NB_state_created;
6fd6a0
     }
6fd6a0
 
6fd6a0
+    channel->read_avail -= channel->flush_flush_bytes;
6fd6a0
+    channel->remote.window_size -= channel->flush_flush_bytes;
6fd6a0
+
6fd6a0
     if (channel->flush_refund_bytes) {
6fd6a0
         int rc;
6fd6a0
 
6fd6a0
@@ -1868,11 +1871,14 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
6fd6a0
         /* if the transport layer said EAGAIN then we say so as well */
6fd6a0
         return _libssh2_error(session, rc, "would block");
6fd6a0
     }
6fd6a0
-    else
6fd6a0
+    else {
6fd6a0
+        channel->read_avail -= bytes_read;
6fd6a0
+        channel->remote.window_size -= bytes_read;
6fd6a0
         /* make sure we remain in the created state to focus on emptying the
6fd6a0
            data we already have in the packet brigade before we try to read
6fd6a0
            more off the network again */
6fd6a0
         channel->read_state = libssh2_NB_state_created;
6fd6a0
+    }
6fd6a0
 
6fd6a0
     if(channel->remote.window_size < (LIBSSH2_CHANNEL_WINDOW_DEFAULT*30)) {
6fd6a0
         /* the window is getting too narrow, expand it! */
6fd6a0
diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
6fd6a0
index 4ec9f73..fcf4370 100644
6fd6a0
--- a/src/libssh2_priv.h
6fd6a0
+++ b/src/libssh2_priv.h
6fd6a0
@@ -357,6 +357,8 @@ struct _LIBSSH2_CHANNEL
6fd6a0
     libssh2_channel_data local, remote;
6fd6a0
     /* Amount of bytes to be refunded to receive window (but not yet sent) */
6fd6a0
     uint32_t adjust_queue;
6fd6a0
+    /* Data immediately available for reading */
6fd6a0
+    uint32_t read_avail;
6fd6a0
 
6fd6a0
     LIBSSH2_SESSION *session;
6fd6a0
 
6fd6a0
diff --git a/src/packet.c b/src/packet.c
6fd6a0
index bfbd56a..d2e758c 100644
6fd6a0
--- a/src/packet.c
6fd6a0
+++ b/src/packet.c
6fd6a0
@@ -653,6 +653,18 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
6fd6a0
                 _libssh2_debug(session, LIBSSH2_TRACE_CONN,
6fd6a0
                                "Ignoring extended data and refunding %d bytes",
6fd6a0
                                (int) (datalen - 13));
6fd6a0
+                if (channelp->read_avail + datalen - data_head >=
6fd6a0
+                    channelp->remote.window_size)
6fd6a0
+                    datalen = channelp->remote.window_size -
6fd6a0
+                        channelp->read_avail + data_head;
6fd6a0
+
6fd6a0
+                channelp->remote.window_size -= datalen - data_head;
6fd6a0
+                _libssh2_debug(session, LIBSSH2_TRACE_CONN,
6fd6a0
+                               "shrinking window size by %lu bytes to %lu, read_avail %lu",
6fd6a0
+                               datalen - data_head,
6fd6a0
+                               channelp->remote.window_size,
6fd6a0
+                               channelp->read_avail);
6fd6a0
+
6fd6a0
                 session->packAdd_channelp = channelp;
6fd6a0
 
6fd6a0
                 /* Adjust the window based on the block we just freed */
6fd6a0
@@ -684,7 +696,7 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
6fd6a0
                                " to receive, truncating");
6fd6a0
                 datalen = channelp->remote.packet_size + data_head;
6fd6a0
             }
6fd6a0
-            if (channelp->remote.window_size <= 0) {
6fd6a0
+            if (channelp->remote.window_size <= channelp->read_avail) {
6fd6a0
                 /*
6fd6a0
                  * Spec says we MAY ignore bytes sent beyond
6fd6a0
                  * window_size
6fd6a0
@@ -700,17 +712,26 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
6fd6a0
             /* Reset EOF status */
6fd6a0
             channelp->remote.eof = 0;
6fd6a0
 
6fd6a0
-            if ((datalen - data_head) > channelp->remote.window_size) {
6fd6a0
+            if (channelp->read_avail + datalen - data_head >
6fd6a0
+                channelp->remote.window_size) {
6fd6a0
                 _libssh2_error(session,
6fd6a0
                                LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED,
6fd6a0
                                "Remote sent more data than current "
6fd6a0
                                "window allows, truncating");
6fd6a0
-                datalen = channelp->remote.window_size + data_head;
6fd6a0
-                channelp->remote.window_size = 0;
6fd6a0
+                datalen = channelp->remote.window_size -
6fd6a0
+                    channelp->read_avail + data_head;
6fd6a0
             }
6fd6a0
-            else
6fd6a0
-                /* Now that we've received it, shrink our window */
6fd6a0
-                channelp->remote.window_size -= datalen - data_head;
6fd6a0
+
6fd6a0
+            /* Update the read_avail counter. The window size will be
6fd6a0
+             * updated once the data is actually read from the queue
6fd6a0
+             * from an upper layer */
6fd6a0
+            channelp->read_avail += datalen - data_head;
6fd6a0
+
6fd6a0
+            _libssh2_debug(session, LIBSSH2_TRACE_CONN,
6fd6a0
+                           "increasing read_avail by %lu bytes to %lu/%lu",
6fd6a0
+                           (long)(datalen - data_head),
6fd6a0
+                           (long)channelp->read_avail,
6fd6a0
+                           (long)channelp->remote.window_size);
6fd6a0
 
6fd6a0
             break;
6fd6a0
 
6fd6a0
-- 
6fd6a0
1.7.1
6fd6a0