Bläddra i källkod

Added ChangeDirectoryAsync to SftpClient (#1504)

* Add ChangeDirectoryAsync

* Added async tests for ChangeDirectoryAsync
Ryan Esteves 1 år sedan
förälder
incheckning
737c3e5f9d

+ 14 - 0
src/Renci.SshNet/ISftpClient.cs

@@ -358,6 +358,20 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
         void ChangeDirectory(string path);
 
+        /// <summary>
+        /// Asynchronously requests to change the current working directory to the specified path.
+        /// </summary>
+        /// <param name="path">The new working directory.</param>
+        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+        /// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
+        /// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);
+
         /// <summary>
         /// Changes permissions of file(s) to specified mode.
         /// </summary>

+ 8 - 0
src/Renci.SshNet/Sftp/ISftpSession.cs

@@ -34,6 +34,14 @@ namespace Renci.SshNet.Sftp
         /// <param name="path">The new working directory.</param>
         void ChangeDirectory(string path);
 
+        /// <summary>
+        /// Asynchronously requests to change the current working directory to the specified path.
+        /// </summary>
+        /// <param name="path">The new working directory.</param>
+        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+        /// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
+        Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);
+
         /// <summary>
         /// Resolves a given path into an absolute path on the server.
         /// </summary>

+ 18 - 0
src/Renci.SshNet/Sftp/SftpSession.cs

@@ -82,6 +82,24 @@ namespace Renci.SshNet.Sftp
             WorkingDirectory = fullPath;
         }
 
+        /// <summary>
+        /// Asynchronously requests to change the current working directory to the specified path.
+        /// </summary>
+        /// <param name="path">The new working directory.</param>
+        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+        /// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
+        public async Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+
+            var fullPath = await GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
+            var handle = await RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false);
+
+            await RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);
+
+            WorkingDirectory = fullPath;
+        }
+
         internal void SendMessage(SftpMessage sftpMessage)
         {
             var data = sftpMessage.GetBytes();

+ 27 - 0
src/Renci.SshNet/SftpClient.cs

@@ -305,6 +305,33 @@ namespace Renci.SshNet
             _sftpSession.ChangeDirectory(path);
         }
 
+        /// <summary>
+        /// Asynchronously requests to change the current working directory to the specified path.
+        /// </summary>
+        /// <param name="path">The new working directory.</param>
+        /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+        /// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
+        /// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
+        {
+            CheckDisposed();
+            ThrowHelper.ThrowIfNull(path);
+
+            if (_sftpSession is null)
+            {
+                throw new SshConnectionException("Client not connected.");
+            }
+
+            cancellationToken.ThrowIfCancellationRequested();
+
+            return _sftpSession.ChangeDirectoryAsync(path, cancellationToken);
+        }
+
         /// <summary>
         /// Changes permissions of file(s) to specified mode.
         /// </summary>

+ 72 - 0
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ChangeDirectory.cs

@@ -19,6 +19,18 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             }
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public async Task Test_Sftp_ChangeDirectory_Root_Dont_ExistsAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+                await sftp.ChangeDirectoryAsync("/asdasd", CancellationToken.None).ConfigureAwait(false);
+            }
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
@@ -31,6 +43,18 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             }
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public async Task Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_ExistsAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+                await sftp.ChangeDirectoryAsync("/asdasd/", CancellationToken.None).ConfigureAwait(false);
+            }
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
@@ -43,6 +67,18 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             }
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public async Task Test_Sftp_ChangeDirectory_Subfolder_Dont_ExistsAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+                await sftp.ChangeDirectoryAsync("/asdasd/sssddds", CancellationToken.None).ConfigureAwait(false);
+            }
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         [ExpectedException(typeof(SftpPathNotFoundException))]
@@ -55,6 +91,18 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             }
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [ExpectedException(typeof(SftpPathNotFoundException))]
+        public async Task Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_ExistsAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+                await sftp.ChangeDirectoryAsync("/asdasd/sssddds/", CancellationToken.None).ConfigureAwait(false);
+            }
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         public void Test_Sftp_ChangeDirectory_Which_Exists()
@@ -67,6 +115,18 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             }
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_ChangeDirectory_Which_ExistsAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+                await sftp.ChangeDirectoryAsync("/usr", CancellationToken.None).ConfigureAwait(false);
+                Assert.AreEqual("/usr", sftp.WorkingDirectory);
+            }
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
@@ -78,5 +138,17 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
                 Assert.AreEqual("/usr", sftp.WorkingDirectory);
             }
         }
+
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_ChangeDirectory_Which_Exists_With_SlashAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+                await sftp.ChangeDirectoryAsync("/usr/", CancellationToken.None).ConfigureAwait(false);
+                Assert.AreEqual("/usr", sftp.WorkingDirectory);
+            }
+        }
     }
 }

+ 85 - 0
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs

@@ -229,6 +229,75 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             RemoveAllFiles();
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        public async Task Test_Sftp_Change_DirectoryAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
+
+                sftp.CreateDirectory("test1");
+
+                await sftp.ChangeDirectoryAsync("test1", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
+
+                sftp.CreateDirectory("test1_1");
+                sftp.CreateDirectory("test1_2");
+                sftp.CreateDirectory("test1_3");
+
+                var files = sftp.ListDirectory(".");
+
+                Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}", sftp.WorkingDirectory)));
+
+                await sftp.ChangeDirectoryAsync("test1_1", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
+
+                await sftp.ChangeDirectoryAsync("../test1_2", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2");
+
+                await sftp.ChangeDirectoryAsync("..", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
+
+                await sftp.ChangeDirectoryAsync("..", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
+
+                files = sftp.ListDirectory("test1/test1_1");
+
+                Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}/test1/test1_1", sftp.WorkingDirectory)));
+
+                await sftp.ChangeDirectoryAsync("test1/test1_1", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
+
+                await sftp.ChangeDirectoryAsync("/home/sshnet/test1/test1_1", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
+
+                await sftp.ChangeDirectoryAsync("/home/sshnet/test1/test1_1/../test1_2", CancellationToken.None).ConfigureAwait(false);
+
+                Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2");
+
+                await sftp.ChangeDirectoryAsync("../../", CancellationToken.None).ConfigureAwait(false);
+
+                sftp.DeleteDirectory("test1/test1_1");
+                sftp.DeleteDirectory("test1/test1_2");
+                sftp.DeleteDirectory("test1/test1_3");
+                sftp.DeleteDirectory("test1");
+
+                sftp.Disconnect();
+            }
+
+            RemoveAllFiles();
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         [Description("Test passing null to ChangeDirectory.")]
@@ -245,6 +314,22 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
             }
         }
 
+        [TestMethod]
+        [TestCategory("Sftp")]
+        [Description("Test passing null to ChangeDirectory.")]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public async Task Test_Sftp_ChangeDirectory_NullAsync()
+        {
+            using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
+            {
+                await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+                await sftp.ChangeDirectoryAsync(null, CancellationToken.None).ConfigureAwait(false);
+
+                sftp.Disconnect();
+            }
+        }
+
         [TestMethod]
         [TestCategory("Sftp")]
         [Description("Test calling EndListDirectory method more then once.")]

+ 89 - 1
test/Renci.SshNet.IntegrationTests/SftpTests.cs

@@ -995,7 +995,6 @@ namespace Renci.SshNet.IntegrationTests
                     {
                         client.DeleteFile(remoteFile);
                     }
-
                 }
             }
         }
@@ -3993,6 +3992,36 @@ namespace Renci.SshNet.IntegrationTests
             }
         }
 
+        [TestMethod]
+        public async Task Sftp_ChangeDirectory_DirectoryDoesNotExistAsync()
+        {
+            const string remoteDirectory = "/home/sshnet/test123";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                await client.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    await command.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
+                }
+            }
+
+            using (var client = new SftpClient(_connectionInfoFactory.Create()))
+            {
+                client.Connect();
+
+                try
+                {
+                    await client.ChangeDirectoryAsync(remoteDirectory, CancellationToken.None).ConfigureAwait(false);
+                    Assert.Fail();
+                }
+                catch (SftpPathNotFoundException)
+                {
+                }
+            }
+        }
+
         [TestMethod]
         public void Sftp_ChangeDirectory_DirectoryExists()
         {
@@ -4052,6 +4081,65 @@ namespace Renci.SshNet.IntegrationTests
             }
         }
 
+        [TestMethod]
+        public async Task Sftp_ChangeDirectory_DirectoryExistsAsync()
+        {
+            const string remoteDirectory = "/home/sshnet/test123";
+
+            using (var client = new SshClient(_connectionInfoFactory.Create()))
+            {
+                await client.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+                using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    await command.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
+                }
+
+                using (var command = client.CreateCommand("mkdir -p " + _remotePathTransformation.Transform(remoteDirectory)))
+                {
+                    await command.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
+                }
+            }
+
+            try
+            {
+                using (var client = new SftpClient(_connectionInfoFactory.Create()))
+                {
+                    await client.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+                    await client.ChangeDirectoryAsync(remoteDirectory, CancellationToken.None).ConfigureAwait(false);
+
+                    Assert.AreEqual(remoteDirectory, client.WorkingDirectory);
+
+                    using (var uploadStream = CreateMemoryStream(100))
+                    {
+                        uploadStream.Position = 0;
+
+                        client.UploadFile(uploadStream, "gert.txt");
+
+                        uploadStream.Position = 0;
+
+                        using (var downloadStream = client.OpenRead(remoteDirectory + "/gert.txt"))
+                        {
+                            Assert.AreEqual(CreateHash(uploadStream), CreateHash(downloadStream));
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                using (var client = new SshClient(_connectionInfoFactory.Create()))
+                {
+                    await client.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
+
+                    using (var command = client.CreateCommand("rm -Rf " + _remotePathTransformation.Transform(remoteDirectory)))
+                    {
+                        await command.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
+                    }
+                }
+            }
+        }
+
         [TestMethod]
         public void Sftp_DownloadFile_MemoryStream()
         {