From 14f4838be7d0fd9063531034ea866a6ba289db53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Tue, 23 May 2023 11:57:39 +0200 Subject: [PATCH 1/7] ListDirectoryAsync return IAsyncEnumerable --- build/build.proj | 4 ++++ build/nuget/SSH.NET.nuspec | 3 +++ src/Renci.SshNet/ISftpClient.cs | 10 ++++++---- src/Renci.SshNet/Renci.SshNet.csproj | 12 ++++++++---- src/Renci.SshNet/SftpClient.cs | 22 ++++++++++++---------- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/build/build.proj b/build/build.proj index d2911509c..97b406cf6 100644 --- a/build/build.proj +++ b/build/build.proj @@ -25,6 +25,10 @@ Renci.SshNet\bin\$(Configuration)\netstandard2.0 netstandard2.0 + + Renci.SshNet\bin\$(Configuration)\netstandard2.1 + netstandard2.1 + Renci.SshNet\bin\$(Configuration)\net6.0 net6.0 diff --git a/build/nuget/SSH.NET.nuspec b/build/nuget/SSH.NET.nuspec index 3fe48202b..3f4ea98a6 100644 --- a/build/nuget/SSH.NET.nuspec +++ b/build/nuget/SSH.NET.nuspec @@ -19,6 +19,9 @@ + + + diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs index 99e1e7b06..92286e055 100644 --- a/src/Renci.SshNet/ISftpClient.cs +++ b/src/Renci.SshNet/ISftpClient.cs @@ -698,21 +698,23 @@ public interface ISftpClient : IBaseClient, IDisposable /// The method was called after the client was disposed. IEnumerable ListDirectory(string path, Action listCallback = null); +#if FEATURE_ASYNC_ENUMERABLE /// - /// Asynchronously retrieves list of files in remote directory. + /// Asynchronously enumerates the files in remote directory. /// /// The path. /// The to observe. /// - /// A that represents the asynchronous list operation. - /// The task result contains an enumerable collection of for the files in the directory specified by . + /// An that represents the asynchronous enumeration operation. + /// The enumeration contains an async stream of for the files in the directory specified by . /// /// is null. /// Client is not connected. /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. /// A SSH error where is the message from the remote host. /// The method was called after the client was disposed. - Task> ListDirectoryAsync(string path, CancellationToken cancellationToken); + IAsyncEnumerable ListDirectoryAsync(string path, CancellationToken cancellationToken); +#endif //FEATURE_ASYNC_ENUMERABLE /// /// Opens a on the specified path with read/write access. diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 21b0128a8..cc9e79830 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -5,20 +5,24 @@ false Renci.SshNet ../Renci.SshNet.snk - 7.3 + latest true - net462;netstandard2.0;net6.0;net7.0 + net462;netstandard2.0;netstandard2.1;net6.0;net7.0 FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160 - + - + FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP + + + $(DefineConstants);FEATURE_ASYNC_ENUMERABLE + diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 937f4a1a0..709dfa745 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -11,6 +11,9 @@ using Renci.SshNet.Common; using Renci.SshNet.Sftp; using System.Threading.Tasks; +#if FEATURE_ASYNC_ENUMERABLE +using System.Runtime.CompilerServices; +#endif namespace Renci.SshNet { @@ -530,32 +533,33 @@ public IEnumerable ListDirectory(string path, Action listCallbac return InternalListDirectory(path, listCallback); } +#if FEATURE_ASYNC_ENUMERABLE /// - /// Asynchronously retrieves list of files in remote directory. + /// Asynchronously enumerates the files in remote directory. /// /// The path. /// The to observe. /// - /// A that represents the asynchronous list operation. - /// The task result contains an enumerable collection of for the files in the directory specified by . + /// An that represents the asynchronous enumeration operation. + /// The enumeration contains an async stream of for the files in the directory specified by . /// /// is null. /// Client is not connected. /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. /// A SSH error where is the message from the remote host. /// The method was called after the client was disposed. - public async Task> ListDirectoryAsync(string path, CancellationToken cancellationToken) + public async IAsyncEnumerable ListDirectoryAsync(string path, [EnumeratorCancellation] CancellationToken cancellationToken) { base.CheckDisposed(); if (path == null) - throw new ArgumentNullException("path"); + throw new ArgumentNullException(nameof(path)); if (_sftpSession == null) throw new SshConnectionException("Client not connected."); + cancellationToken.ThrowIfCancellationRequested(); var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false); - var result = new List(); var handle = await _sftpSession.RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false); try { @@ -573,18 +577,16 @@ public async Task> ListDirectoryAsync(string path, Cancel foreach (var file in files) { - result.Add(new SftpFile(_sftpSession, basePath + file.Key, file.Value)); + yield return new SftpFile(_sftpSession, basePath + file.Key, file.Value); } } - } finally { await _sftpSession.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false); } - - return result; } +#endif //FEATURE_ASYNC_ENUMERABLE /// /// Begins an asynchronous operation of retrieving list of files in remote directory. From 1b0732d731877e6cab979c21ffe9823eb2b8e1be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Tue, 23 May 2023 13:13:29 +0200 Subject: [PATCH 2/7] Fix documentation --- src/Renci.SshNet/ISftpClient.cs | 2 +- src/Renci.SshNet/SftpClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs index 92286e055..4ec6c29ca 100644 --- a/src/Renci.SshNet/ISftpClient.cs +++ b/src/Renci.SshNet/ISftpClient.cs @@ -39,7 +39,7 @@ public interface ISftpClient : IBaseClient, IDisposable /// SSH_FXP_DATA protocol fields. /// /// - /// The size of the each indivual SSH_FXP_DATA message is limited to the + /// The size of the each individual SSH_FXP_DATA message is limited to the /// local maximum packet size of the channel, which is set to 64 KB /// for SSH.NET. However, the peer can limit this even further. /// diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 709dfa745..8825d05a6 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -94,7 +94,7 @@ public TimeSpan OperationTimeout /// SSH_FXP_DATA protocol fields. /// /// - /// The size of the each indivual SSH_FXP_DATA message is limited to the + /// The size of the each individual SSH_FXP_DATA message is limited to the /// local maximum packet size of the channel, which is set to 64 KB /// for SSH.NET. However, the peer can limit this even further. /// From eafa871cfc30df6e5380934a74ac8c295ea669e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Wed, 24 May 2023 20:51:23 +0200 Subject: [PATCH 3/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5708d0af4..2c0dc015c 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Private keys can be encrypted using one of the following cipher methods: ## Framework Support **SSH.NET** supports the following target frameworks: * .NETFramework 4.6.2 (and higher) -* .NET Standard 2.0 +* .NET Standard 2.0 and 2.1 * .NET 6 (and higher) ## Usage From 582d6a3439f0af3c920206742ab281c5050cedd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Wed, 24 May 2023 20:58:38 +0200 Subject: [PATCH 4/7] Fix --- src/Renci.SshNet/ISftpClient.cs | 4 ++-- src/Renci.SshNet/SftpClient.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs index 074f3998b..21d05ae7c 100644 --- a/src/Renci.SshNet/ISftpClient.cs +++ b/src/Renci.SshNet/ISftpClient.cs @@ -706,8 +706,8 @@ public interface ISftpClient : IBaseClient, IDisposable /// The path. /// The to observe. /// - /// An that represents the asynchronous enumeration operation. - /// The enumeration contains an async stream of for the files in the directory specified by . + /// An of that represents the asynchronous enumeration operation. + /// The enumeration contains an async stream of for the files in the directory specified by . /// /// is null. /// Client is not connected. diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 8825d05a6..e85ff88ea 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -540,8 +540,8 @@ public IEnumerable ListDirectory(string path, Action listCallbac /// The path. /// The to observe. /// - /// An that represents the asynchronous enumeration operation. - /// The enumeration contains an async stream of for the files in the directory specified by . + /// An of that represents the asynchronous enumeration operation. + /// The enumeration contains an async stream of for the files in the directory specified by . /// /// is null. /// Client is not connected. From aaf8790dee72119208a942242e2ce83640bc0537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Wed, 24 May 2023 22:55:28 +0200 Subject: [PATCH 5/7] Add Sftp ListDirectoryAsync test --- .../Classes/SftpClientTest.ListDirectory.cs | 26 +++++++++++++++++++ .../Properties/Resources.resx | 6 ++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs index 69f90aefe..eca47ca09 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs @@ -4,6 +4,8 @@ using System; using System.Diagnostics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace Renci.SshNet.Tests.Classes { @@ -89,6 +91,30 @@ public void Test_Sftp_ListDirectory_Current() } } +#if NET6_0_OR_GREATER + [TestMethod] + [TestCategory("Sftp")] + [TestCategory("integration")] + public async Task Test_Sftp_ListDirectoryAsync_Current() + { + using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + { + sftp.Connect(); + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromMinutes(1)); + var count = 0; + await foreach(var file in sftp.ListDirectoryAsync(".", cts.Token)) + { + count++; + Debug.WriteLine(file.FullName); + } + + Assert.IsTrue(count > 0); + + sftp.Disconnect(); + } + } +#endif [TestMethod] [TestCategory("Sftp")] [TestCategory("integration")] diff --git a/src/Renci.SshNet.Tests/Properties/Resources.resx b/src/Renci.SshNet.Tests/Properties/Resources.resx index c392eb5c2..4867d67df 100644 --- a/src/Renci.SshNet.Tests/Properties/Resources.resx +++ b/src/Renci.SshNet.Tests/Properties/Resources.resx @@ -149,7 +149,7 @@ D8DHbFwAT2mUv1QxRXYJO1y4pENboEzT6LUqxJgE+ae/F/29g2RD9DhtwqKqWjhM -----END DSA PRIVATE KEY----- - 192.168.10.192 + localhost -----BEGIN DSA PRIVATE KEY----- @@ -166,7 +166,7 @@ tM7dZpB+reWl9L5e2L8= -----END DSA PRIVATE KEY----- - tester + ssh4ever 22 @@ -239,6 +239,6 @@ Vakr7Sa3K5niCyH5kxdyO1t29l1ksBqpDUrj+vViFuLkd3XIiui8IA== -----END RSA PRIVATE KEY----- - tester + sshnetadm \ No newline at end of file From a1b3dd6c2e49f0461b4624311396fae2433bbea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Wed, 24 May 2023 23:04:31 +0200 Subject: [PATCH 6/7] Revert --- src/Renci.SshNet.Tests/Properties/Resources.resx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Renci.SshNet.Tests/Properties/Resources.resx b/src/Renci.SshNet.Tests/Properties/Resources.resx index 4867d67df..c392eb5c2 100644 --- a/src/Renci.SshNet.Tests/Properties/Resources.resx +++ b/src/Renci.SshNet.Tests/Properties/Resources.resx @@ -149,7 +149,7 @@ D8DHbFwAT2mUv1QxRXYJO1y4pENboEzT6LUqxJgE+ae/F/29g2RD9DhtwqKqWjhM -----END DSA PRIVATE KEY----- - localhost + 192.168.10.192 -----BEGIN DSA PRIVATE KEY----- @@ -166,7 +166,7 @@ tM7dZpB+reWl9L5e2L8= -----END DSA PRIVATE KEY----- - ssh4ever + tester 22 @@ -239,6 +239,6 @@ Vakr7Sa3K5niCyH5kxdyO1t29l1ksBqpDUrj+vViFuLkd3XIiui8IA== -----END RSA PRIVATE KEY----- - sshnetadm + tester \ No newline at end of file From 17e7abe3b227a528faf7164af48e160e9c5fc2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Nag=C3=B3rski?= Date: Wed, 30 Aug 2023 07:46:43 +0200 Subject: [PATCH 7/7] Integration tests for ListDirectoryAsync with IAsyncEnumerable --- src/Renci.SshNet.IntegrationTests/SftpClientTests.cs | 10 +++++----- .../Classes/SftpClientTest.ListDirectory.cs | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs index dfb406475..9648587a1 100644 --- a/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs +++ b/src/Renci.SshNet.IntegrationTests/SftpClientTests.cs @@ -67,17 +67,17 @@ public async Task Create_directory_with_contents_and_list_it_async() Assert.IsTrue(_sftpClient.Exists(testFilePath)); // Check if ListDirectory works - var files = await _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None); - - _sftpClient.DeleteFile(testFilePath); - _sftpClient.DeleteDirectory(testDirectory); + var files = _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None); var builder = new StringBuilder(); - foreach (var file in files) + await foreach (var file in files) { builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}"); } + _sftpClient.DeleteFile(testFilePath); + _sftpClient.DeleteDirectory(testDirectory); + Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True /home/sshnet/sshnet-test/.. False True /home/sshnet/sshnet-test/test-file.txt True False diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs index eca47ca09..6a19ce5a3 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ListDirectory.cs @@ -4,8 +4,10 @@ using System; using System.Diagnostics; using System.Linq; +#if NET6_0_OR_GREATER using System.Threading; using System.Threading.Tasks; +#endif namespace Renci.SshNet.Tests.Classes { @@ -291,4 +293,4 @@ public void Test_Sftp_Call_EndListDirectory_Twice() } } } -} \ No newline at end of file +}