Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ListDirectoryAsync return IAsyncEnumerable #1126

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build/build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
<OutputDirectory>Renci.SshNet\bin\$(Configuration)\netstandard2.0</OutputDirectory>
<Moniker>netstandard2.0</Moniker>
</TargetFrameworkModern>
<TargetFrameworkModern Include=".NETStandard 2.1">
WojciechNagorski marked this conversation as resolved.
Show resolved Hide resolved
<OutputDirectory>Renci.SshNet\bin\$(Configuration)\netstandard2.1</OutputDirectory>
<Moniker>netstandard2.1</Moniker>
</TargetFrameworkModern>
<TargetFrameworkModern Include=".NET 6.0">
<OutputDirectory>Renci.SshNet\bin\$(Configuration)\net6.0</OutputDirectory>
<Moniker>net6.0</Moniker>
Expand Down
3 changes: 3 additions & 0 deletions build/nuget/SSH.NET.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<group targetFramework="net462" />
<group targetFramework="netstandard2.0">
<dependency id="SshNet.Security.Cryptography" version="[1.3.0]" />
</group>
<group targetFramework="netstandard2.1">
<dependency id="SshNet.Security.Cryptography" version="[1.3.0]" />
</group>
<group targetFramework="net6.0">
<dependency id="SshNet.Security.Cryptography" version="[1.3.0]" />
Expand Down
12 changes: 7 additions & 5 deletions src/Renci.SshNet/ISftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public interface ISftpClient : IBaseClient, IDisposable
/// SSH_FXP_DATA protocol fields.
/// </para>
/// <para>
/// 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 <c>64 KB</c>
/// for SSH.NET. However, the peer can limit this even further.
/// </para>
Expand Down Expand Up @@ -698,21 +698,23 @@ public interface ISftpClient : IBaseClient, IDisposable
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null);

#if FEATURE_ASYNC_ENUMERABLE
/// <summary>
/// Asynchronously retrieves list of files in remote directory.
/// Asynchronously enumerates the files in remote directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
/// <returns>
/// A <see cref="Task{IEnumerable}"/> that represents the asynchronous list operation.
/// The task result contains an enumerable collection of <see cref="SftpFile"/> for the files in the directory specified by <paramref name="path" />.
/// An <see cref="IAsyncEnumerable{SftpFile}"/> that represents the asynchronous enumeration operation.
WojciechNagorski marked this conversation as resolved.
Show resolved Hide resolved
/// The enumeration contains an async stream of <see cref="SftpFile"/> for the files in the directory specified by <paramref name="path" />.
WojciechNagorski marked this conversation as resolved.
Show resolved Hide resolved
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</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<IEnumerable<ISftpFile>> ListDirectoryAsync(string path, CancellationToken cancellationToken);
IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, CancellationToken cancellationToken);
#endif //FEATURE_ASYNC_ENUMERABLE

/// <summary>
/// Opens a <see cref="SftpFileStream"/> on the specified path with read/write access.
Expand Down
12 changes: 8 additions & 4 deletions src/Renci.SshNet/Renci.SshNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AssemblyName>Renci.SshNet</AssemblyName>
<AssemblyOriginatorKeyFile>../Renci.SshNet.snk</AssemblyOriginatorKeyFile>
<LangVersion>7.3</LangVersion>
<LangVersion>latest</LangVersion>
WojciechNagorski marked this conversation as resolved.
Show resolved Hide resolved
<SignAssembly>true</SignAssembly>
<TargetFrameworks>net462;netstandard2.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'net462' ">
<DefineConstants>FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160</DefineConstants>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
<PackageReference Include="SshNet.Security.Cryptography" Version="[1.3.0]" />
</ItemGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
<DefineConstants>FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
<DefineConstants>$(DefineConstants);FEATURE_ASYNC_ENUMERABLE</DefineConstants>
</PropertyGroup>
</Project>
24 changes: 13 additions & 11 deletions src/Renci.SshNet/SftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -91,7 +94,7 @@ public TimeSpan OperationTimeout
/// SSH_FXP_DATA protocol fields.
/// </para>
/// <para>
/// 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 <c>64 KB</c>
/// for SSH.NET. However, the peer can limit this even further.
/// </para>
Expand Down Expand Up @@ -530,32 +533,33 @@ public IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallbac
return InternalListDirectory(path, listCallback);
}

#if FEATURE_ASYNC_ENUMERABLE
/// <summary>
/// Asynchronously retrieves list of files in remote directory.
/// Asynchronously enumerates the files in remote directory.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
/// <returns>
/// A <see cref="Task{IEnumerable}"/> that represents the asynchronous list operation.
/// The task result contains an enumerable collection of <see cref="SftpFile"/> for the files in the directory specified by <paramref name="path" />.
/// An <see cref="IAsyncEnumerable{SftpFile}"/> that represents the asynchronous enumeration operation.
WojciechNagorski marked this conversation as resolved.
Show resolved Hide resolved
/// The enumeration contains an async stream of <see cref="SftpFile"/> for the files in the directory specified by <paramref name="path" />.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</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 async Task<IEnumerable<ISftpFile>> ListDirectoryAsync(string path, CancellationToken cancellationToken)
public async IAsyncEnumerable<ISftpFile> 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<SftpFile>();
var handle = await _sftpSession.RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false);
try
{
Expand All @@ -573,18 +577,16 @@ public async Task<IEnumerable<ISftpFile>> 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

/// <summary>
/// Begins an asynchronous operation of retrieving list of files in remote directory.
Expand Down