5881ed4
From cae2385ba898f71038ed4dd00ddae02f85e588e7 Mon Sep 17 00:00:00 2001
5881ed4
From: Salvador <sfandino@yahoo.com>
5881ed4
Date: Tue, 15 Oct 2013 11:45:10 +0200
5881ed4
Subject: [PATCH 08/11] _libssh2_channel_read: fix data drop when out of window
5881ed4
5881ed4
After filling the read buffer with data from the read queue, when the
5881ed4
window size was too small, "libssh2_channel_receive_window_adjust" was
5881ed4
called to increase it. In non-blocking mode that function could return
5881ed4
EAGAIN and, in that case, the EAGAIN was propagated upwards and the data
5881ed4
already read on the buffer lost.
5881ed4
5881ed4
The function was also moving between the two read states
5881ed4
"libssh2_NB_state_idle" and "libssh2_NB_state_created" both of which
5881ed4
behave in the same way (excepting a debug statment).
5881ed4
5881ed4
This commit modifies "_libssh2_channel_read" so that the
5881ed4
"libssh2_channel_receive_window_adjust" call is performed first (when
5881ed4
required) and if everything goes well, then it reads the data from the
5881ed4
queued packets into the read buffer.
5881ed4
5881ed4
It also removes the useless "libssh2_NB_state_created" read state.
5881ed4
5881ed4
Some rotted comments have also been updated.
5881ed4
5881ed4
Signed-off-by: Salvador <sfandino@yahoo.com>
5881ed4
5881ed4
[upstream commit 27f9ac2549b7721cf9d857022c0e7a311830b367]
5881ed4
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
5881ed4
---
5881ed4
 src/channel.c |   75 +++++++++++++++++++--------------------------------------
5881ed4
 1 files changed, 25 insertions(+), 50 deletions(-)
5881ed4
5881ed4
diff --git a/src/channel.c b/src/channel.c
5881ed4
index 499d815..82f6980 100644
5881ed4
--- a/src/channel.c
5881ed4
+++ b/src/channel.c
5881ed4
@@ -1751,31 +1751,33 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
5881ed4
     LIBSSH2_PACKET *read_packet;
5881ed4
     LIBSSH2_PACKET *read_next;
5881ed4
 
5881ed4
-    if (channel->read_state == libssh2_NB_state_idle) {
5881ed4
-        _libssh2_debug(session, LIBSSH2_TRACE_CONN,
5881ed4
-                       "channel_read() wants %d bytes from channel %lu/%lu "
5881ed4
-                       "stream #%d",
5881ed4
-                       (int) buflen, channel->local.id, channel->remote.id,
5881ed4
-                       stream_id);
5881ed4
-        channel->read_state = libssh2_NB_state_created;
5881ed4
-    }
5881ed4
+    _libssh2_debug(session, LIBSSH2_TRACE_CONN,
5881ed4
+                   "channel_read() wants %d bytes from channel %lu/%lu "
5881ed4
+                   "stream #%d",
5881ed4
+                   (int) buflen, channel->local.id, channel->remote.id,
5881ed4
+                   stream_id);
5881ed4
 
5881ed4
-    /*
5881ed4
-     * =============================== NOTE ===============================
5881ed4
-     * I know this is very ugly and not a really good use of "goto", but
5881ed4
-     * this case statement would be even uglier to do it any other way
5881ed4
-     */
5881ed4
-    if (channel->read_state == libssh2_NB_state_jump1) {
5881ed4
-        goto channel_read_window_adjust;
5881ed4
-    }
5881ed4
+    /* expand the receiving window first if it has become too narrow */
5881ed4
+    if((channel->read_state == libssh2_NB_state_jump1) ||
5881ed4
+       (channel->remote.window_size < (LIBSSH2_CHANNEL_WINDOW_DEFAULT*30))) {
5881ed4
+
5881ed4
+        /* the actual window adjusting may not finish so we need to deal with
5881ed4
+           this special state here */
5881ed4
+        channel->read_state = libssh2_NB_state_jump1;
5881ed4
+        rc = _libssh2_channel_receive_window_adjust(channel,
5881ed4
+                                                    (LIBSSH2_CHANNEL_WINDOW_DEFAULT*60),
5881ed4
+                                                    0, NULL);
5881ed4
+        if (rc)
5881ed4
+            return rc;
5881ed4
 
5881ed4
-    rc = 1; /* set to >0 to let the while loop start */
5881ed4
+        channel->read_state = libssh2_NB_state_idle;
5881ed4
+    }
5881ed4
 
5881ed4
-    /* Process all pending incoming packets in all states in order to "even
5881ed4
-       out" the network readings. Tests prove that this way produces faster
5881ed4
-       transfers. */
5881ed4
-    while (rc > 0)
5881ed4
+    /* Process all pending incoming packets. Tests prove that this way
5881ed4
+       produces faster transfers. */
5881ed4
+    do {
5881ed4
         rc = _libssh2_transport_read(session);
5881ed4
+    } while (rc > 0);
5881ed4
 
5881ed4
     if ((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN))
5881ed4
         return _libssh2_error(session, rc, "transport read");
5881ed4
@@ -1857,8 +1859,6 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
5881ed4
     }
5881ed4
 
5881ed4
     if (!bytes_read) {
5881ed4
-        channel->read_state = libssh2_NB_state_idle;
5881ed4
-
5881ed4
         /* If the channel is already at EOF or even closed, we need to signal
5881ed4
            that back. We may have gotten that info while draining the incoming
5881ed4
            transport layer until EAGAIN so we must not be fooled by that
5881ed4
@@ -1871,34 +1871,9 @@ ssize_t _libssh2_channel_read(LIBSSH2_CHANNEL *channel, int stream_id,
5881ed4
         /* if the transport layer said EAGAIN then we say so as well */
5881ed4
         return _libssh2_error(session, rc, "would block");
5881ed4
     }
5881ed4
-    else {
5881ed4
-        channel->read_avail -= bytes_read;
5881ed4
-        channel->remote.window_size -= bytes_read;
5881ed4
-        /* make sure we remain in the created state to focus on emptying the
5881ed4
-           data we already have in the packet brigade before we try to read
5881ed4
-           more off the network again */
5881ed4
-        channel->read_state = libssh2_NB_state_created;
5881ed4
-    }
5881ed4
-
5881ed4
-    if(channel->remote.window_size < (LIBSSH2_CHANNEL_WINDOW_DEFAULT*30)) {
5881ed4
-        /* the window is getting too narrow, expand it! */
5881ed4
-
5881ed4
-      channel_read_window_adjust:
5881ed4
-        channel->read_state = libssh2_NB_state_jump1;
5881ed4
-        /* the actual window adjusting may not finish so we need to deal with
5881ed4
-           this special state here */
5881ed4
-        rc = _libssh2_channel_receive_window_adjust(channel,
5881ed4
-                                                    (LIBSSH2_CHANNEL_WINDOW_DEFAULT*60), 0, NULL);
5881ed4
-        if (rc)
5881ed4
-            return rc;
5881ed4
 
5881ed4
-        _libssh2_debug(session, LIBSSH2_TRACE_CONN,
5881ed4
-                       "channel_read() filled %d adjusted %d",
5881ed4
-                       bytes_read, buflen);
5881ed4
-        /* continue in 'created' state to drain the already read packages
5881ed4
-           first before starting to empty the socket further */
5881ed4
-        channel->read_state = libssh2_NB_state_created;
5881ed4
-    }
5881ed4
+    channel->read_avail -= bytes_read;
5881ed4
+    channel->remote.window_size -= bytes_read;
5881ed4
 
5881ed4
     return bytes_read;
5881ed4
 }
5881ed4
-- 
5881ed4
1.7.1
5881ed4