Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
51 changes: 27 additions & 24 deletions src/CoreFtp/Components/DirectoryListing/DirectoryProviderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,35 @@ internal abstract class DirectoryProviderBase : IDirectoryProvider
protected ILogger logger;
protected Stream stream;

protected IEnumerable<string> RetrieveDirectoryListing()
protected async Task<List<string>> RetrieveDirectoryListingAsync()
{
string line;
while ( ( line = ReadLine( ftpClient.ControlStream.Encoding ) ) != null )
var lines = new List<string>();
using (var reader = new StreamReader(stream, ftpClient.ControlStream.Encoding))
{
logger?.LogDebug( line );
yield return line;
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
logger?.LogDebug(line);
lines.Add(line);
}
}

return lines;
}

protected string ReadLine( Encoding encoding )
protected async IAsyncEnumerable<string> RetrieveDirectoryListingEnumerableAsync(
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
if ( encoding == null )
throw new ArgumentNullException( nameof( encoding ) );

var data = new List<byte>();
var buf = new byte[1];
string line = null;

while ( stream.Read( buf, 0, buf.Length ) > 0 )
using (var reader = new StreamReader(stream, ftpClient.ControlStream.Encoding))
{
data.Add( buf[ 0 ] );
if ( (char) buf[ 0 ] != '\n' )
continue;
line = encoding.GetString( data.ToArray() ).Trim( '\r', '\n' );
break;
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
cancellationToken.ThrowIfCancellationRequested();
logger?.LogDebug(line);
yield return line;
}
}

return line;
}

public virtual Task<ReadOnlyCollection<FtpNodeInformation>> ListAllAsync()
Expand All @@ -64,17 +64,20 @@ public virtual Task<ReadOnlyCollection<FtpNodeInformation>> ListDirectoriesAsync
throw new NotImplementedException();
}

public virtual IAsyncEnumerable<FtpNodeInformation> ListAllEnumerableAsync( CancellationToken cancellationToken = default )
public virtual IAsyncEnumerable<FtpNodeInformation> ListAllEnumerableAsync(
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}

public virtual IAsyncEnumerable<FtpNodeInformation> ListFilesEnumerableAsync( CancellationToken cancellationToken = default )
public virtual IAsyncEnumerable<FtpNodeInformation> ListFilesEnumerableAsync(
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}

public virtual IAsyncEnumerable<FtpNodeInformation> ListDirectoriesEnumerableAsync( CancellationToken cancellationToken = default )
public virtual IAsyncEnumerable<FtpNodeInformation> ListDirectoriesEnumerableAsync(
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
Expand Down
102 changes: 53 additions & 49 deletions src/CoreFtp/Components/DirectoryListing/ListDirectoryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ internal class ListDirectoryProvider : DirectoryProviderBase
{
private readonly List<IListDirectoryParser> directoryParsers;

public ListDirectoryProvider( FtpClient ftpClient, ILogger logger, FtpClientConfiguration configuration )
public ListDirectoryProvider(FtpClient ftpClient, ILogger logger, FtpClientConfiguration configuration)
{
this.ftpClient = ftpClient;
this.logger = logger;
this.configuration = configuration;

directoryParsers = new List<IListDirectoryParser>
{
new UnixDirectoryParser( logger ),
new DosDirectoryParser( logger ),
new UnixDirectoryParser(logger),
new DosDirectoryParser(logger),
};
}

Expand All @@ -40,8 +40,8 @@ internal void AddParser(IListDirectoryParser parser)

private void EnsureLoggedIn()
{
if ( !ftpClient.IsConnected || !ftpClient.IsAuthenticated )
throw new FtpException( "User must be logged in" );
if (!ftpClient.IsConnected || !ftpClient.IsAuthenticated)
throw new FtpException("User must be logged in");
}

public override async Task<ReadOnlyCollection<FtpNodeInformation>> ListAllAsync()
Expand All @@ -62,7 +62,7 @@ public override async Task<ReadOnlyCollection<FtpNodeInformation>> ListFilesAsyn
try
{
await ftpClient.dataSocketSemaphore.WaitAsync();
return await ListNodesAsync( FtpNodeType.File );
return await ListNodesAsync(FtpNodeType.File);
}
finally
{
Expand All @@ -75,49 +75,53 @@ public override async Task<ReadOnlyCollection<FtpNodeInformation>> ListDirectori
try
{
await ftpClient.dataSocketSemaphore.WaitAsync();
return await ListNodesAsync( FtpNodeType.Directory );
return await ListNodesAsync(FtpNodeType.Directory);
}
finally
{
ftpClient.dataSocketSemaphore.Release();
}
}

public override IAsyncEnumerable<FtpNodeInformation> ListAllEnumerableAsync( CancellationToken cancellationToken = default )
=> ListNodesEnumerableAsync( null, cancellationToken );
public override IAsyncEnumerable<FtpNodeInformation> ListAllEnumerableAsync(
CancellationToken cancellationToken = default)
=> ListNodesEnumerableAsync(null, cancellationToken);

public override IAsyncEnumerable<FtpNodeInformation> ListFilesEnumerableAsync( CancellationToken cancellationToken = default )
=> ListNodesEnumerableAsync( FtpNodeType.File, cancellationToken );
public override IAsyncEnumerable<FtpNodeInformation> ListFilesEnumerableAsync(
CancellationToken cancellationToken = default)
=> ListNodesEnumerableAsync(FtpNodeType.File, cancellationToken);

public override IAsyncEnumerable<FtpNodeInformation> ListDirectoriesEnumerableAsync( CancellationToken cancellationToken = default )
=> ListNodesEnumerableAsync( FtpNodeType.Directory, cancellationToken );
public override IAsyncEnumerable<FtpNodeInformation> ListDirectoriesEnumerableAsync(
CancellationToken cancellationToken = default)
=> ListNodesEnumerableAsync(FtpNodeType.Directory, cancellationToken);

/// <summary>
/// Lists all nodes (files and directories) in the current working directory
/// </summary>
/// <param name="ftpNodeType"></param>
/// <returns></returns>
private async Task<ReadOnlyCollection<FtpNodeInformation>> ListNodesAsync( FtpNodeType? ftpNodeType = null )
private async Task<ReadOnlyCollection<FtpNodeInformation>> ListNodesAsync(FtpNodeType? ftpNodeType = null)
{
EnsureLoggedIn();
logger?.LogDebug( $"[ListDirectoryProvider] Listing {ftpNodeType}" );
logger?.LogDebug($"[ListDirectoryProvider] Listing {ftpNodeType}");

try
{
stream = await ftpClient.ConnectDataStreamAsync();

var result = await ftpClient.ControlStream.SendCommandAsync( new FtpCommandEnvelope
var result = await ftpClient.ControlStream.SendCommandAsync(new FtpCommandEnvelope
{
FtpCommand = FtpCommand.LIST
} );
});

if ( ( result.FtpStatusCode != FtpStatusCode.DataAlreadyOpen ) && ( result.FtpStatusCode != FtpStatusCode.OpeningData ) )
throw new FtpException( "Could not retrieve directory listing " + result.ResponseMessage );
if ((result.FtpStatusCode != FtpStatusCode.DataAlreadyOpen) &&
(result.FtpStatusCode != FtpStatusCode.OpeningData))
throw new FtpException("Could not retrieve directory listing " + result.ResponseMessage);

var directoryListing = RetrieveDirectoryListing();
var directoryListing = await RetrieveDirectoryListingAsync();

var nodes = ParseLines( directoryListing.ToList().AsReadOnly() )
.Where( x => !ftpNodeType.HasValue || x.NodeType == ftpNodeType )
var nodes = ParseLines(directoryListing.AsReadOnly())
.Where(x => !ftpNodeType.HasValue || x.NodeType == ftpNodeType)
.ToList();

return nodes.AsReadOnly();
Expand All @@ -131,47 +135,47 @@ private async Task<ReadOnlyCollection<FtpNodeInformation>> ListNodesAsync( FtpNo
/// <summary>
/// Streams nodes as they are parsed from the LIST response
/// </summary>
private async IAsyncEnumerable<FtpNodeInformation> ListNodesEnumerableAsync( FtpNodeType? ftpNodeType, [EnumeratorCancellation] CancellationToken cancellationToken )
private async IAsyncEnumerable<FtpNodeInformation> ListNodesEnumerableAsync(FtpNodeType? ftpNodeType,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
EnsureLoggedIn();
logger?.LogDebug( $"[ListDirectoryProvider] Streaming {ftpNodeType}" );
logger?.LogDebug($"[ListDirectoryProvider] Streaming {ftpNodeType}");

await ftpClient.dataSocketSemaphore.WaitAsync( cancellationToken );
await ftpClient.dataSocketSemaphore.WaitAsync(cancellationToken);
try
{
stream = await ftpClient.ConnectDataStreamAsync();
if ( stream == null )
throw new FtpException( "Could not establish a data connection" );
if (stream == null)
throw new FtpException("Could not establish a data connection");

var result = await ftpClient.ControlStream.SendCommandAsync( new FtpCommandEnvelope
var result = await ftpClient.ControlStream.SendCommandAsync(new FtpCommandEnvelope
{
FtpCommand = FtpCommand.LIST
} );
});

if ( ( result.FtpStatusCode != FtpStatusCode.DataAlreadyOpen ) && ( result.FtpStatusCode != FtpStatusCode.OpeningData ) )
throw new FtpException( "Could not retrieve directory listing " + result.ResponseMessage );
if ((result.FtpStatusCode != FtpStatusCode.DataAlreadyOpen) &&
(result.FtpStatusCode != FtpStatusCode.OpeningData))
throw new FtpException("Could not retrieve directory listing " + result.ResponseMessage);

IListDirectoryParser parser = null;
bool parserResolved = false;

foreach ( string line in RetrieveDirectoryListing() )
await foreach (string line in RetrieveDirectoryListingEnumerableAsync(cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();

if ( !parserResolved )
if (!parserResolved)
{
parser = directoryParsers.Count == 1
? directoryParsers[ 0 ]
: directoryParsers.FirstOrDefault( x => x.Test( line ) );
? directoryParsers[0]
: directoryParsers.FirstOrDefault(x => x.Test(line));
parserResolved = true;
}

if ( parser == null )
if (parser == null)
yield break;

var parsed = parser.Parse( line );
var parsed = parser.Parse(line);

if ( parsed != null && ( !ftpNodeType.HasValue || parsed.NodeType == ftpNodeType ) )
if (parsed != null && (!ftpNodeType.HasValue || parsed.NodeType == ftpNodeType))
yield return parsed;
}
}
Expand All @@ -183,23 +187,23 @@ private async IAsyncEnumerable<FtpNodeInformation> ListNodesEnumerableAsync( Ftp
}
}

private IEnumerable<FtpNodeInformation> ParseLines( IReadOnlyList<string> lines )
private IEnumerable<FtpNodeInformation> ParseLines(IReadOnlyList<string> lines)
{
if ( !lines.Any() )
if (!lines.Any())
yield break;

var parser = directoryParsers.Count == 1
? directoryParsers[ 0 ]
: directoryParsers.FirstOrDefault( x => x.Test( lines[ 0 ] ) );
var parser = directoryParsers.Count == 1
? directoryParsers[0]
: directoryParsers.FirstOrDefault(x => x.Test(lines[0]));

if ( parser == null )
if (parser == null)
yield break;

foreach ( string line in lines )
foreach (string line in lines)
{
var parsed = parser.Parse( line );
var parsed = parser.Parse(line);

if ( parsed != null )
if (parsed != null)
yield return parsed;
}
}
Expand Down
Loading