Browse Source

fix "client not connected" after SFTP reconnect (#1484)

* fix "client not connected" after SFTP reconnect

if the server closes the session and the client reconnects,
this currently leads to a broken state because the session
is re-created, but the SFTP subsession is not and still
references the old session.

This causes all operations to fail with "client not connected" or
even throwing the "An established connection was aborted by the server."
exception of the old session.

Always re-create the SFTP subsession to fix this.

fixes #1474

* Dispose old session on reconnect
mus65 1 year ago
parent
commit
666930974d

+ 10 - 0
src/Renci.SshNet/BaseClient.cs

@@ -240,6 +240,11 @@ namespace Renci.SshNet
             var session = Session;
             if (session is null || !session.IsConnected)
             {
+                if (session is not null)
+                {
+                    DisposeSession(session);
+                }
+
                 Session = CreateAndConnectSession();
             }
 
@@ -304,6 +309,11 @@ namespace Renci.SshNet
             var session = Session;
             if (session is null || !session.IsConnected)
             {
+                if (session is not null)
+                {
+                    DisposeSession(session);
+                }
+
                 Session = await CreateAndConnectSessionAsync(cancellationToken).ConfigureAwait(false);
             }
 

+ 2 - 9
src/Renci.SshNet/SftpClient.cs

@@ -2496,15 +2496,8 @@ namespace Renci.SshNet
         {
             base.OnConnected();
 
-            var sftpSession = _sftpSession;
-            if (sftpSession is null)
-            {
-                _sftpSession = CreateAndConnectToSftpSession();
-            }
-            else if (!sftpSession.IsOpen)
-            {
-                sftpSession.Connect();
-            }
+            _sftpSession?.Dispose();
+            _sftpSession = CreateAndConnectToSftpSession();
         }
 
         /// <summary>

+ 48 - 0
test/Renci.SshNet.IntegrationTests/ConnectivityTests.cs

@@ -326,6 +326,54 @@ namespace Renci.SshNet.IntegrationTests
             }
         }
 
+        [TestMethod]
+        public void SftpClient_HandleSftpSessionAbortByServer()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+                Assert.IsTrue(client.IsConnected);
+
+                _sshConnectionDisruptor.BreakConnections();
+                WaitForConnectionInterruption(client);
+                Assert.IsFalse(client.IsConnected);
+
+                client.Connect();
+                Assert.IsTrue(client.IsConnected);
+
+                foreach (var file in client.ListDirectory("."))
+                {
+                }
+
+                client.Disconnect();
+                Assert.IsFalse(client.IsConnected);
+            }
+        }
+
+        [TestMethod]
+        public async Task SftpClient_HandleSftpSessionAbortByServerAsync()
+        {
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                await client.ConnectAsync(CancellationToken.None);
+                Assert.IsTrue(client.IsConnected);
+
+                _sshConnectionDisruptor.BreakConnections();
+                WaitForConnectionInterruption(client);
+                Assert.IsFalse(client.IsConnected);
+
+                await client.ConnectAsync(CancellationToken.None);
+                Assert.IsTrue(client.IsConnected);
+
+                await foreach (var file in client.ListDirectoryAsync(".", CancellationToken.None))
+                {
+                }
+
+                client.Disconnect();
+                Assert.IsFalse(client.IsConnected);
+            }
+        }
+
         [TestMethod]
         public void Common_DetectSessionKilledOnServer()
         {