Skip to content

Commit

Permalink
Shell for test FTP server can now list and close connections
Browse files Browse the repository at this point in the history
Copied ReadLine into third-party and changed to allow async auto completion handlers.
  • Loading branch information
fubar-coder committed Nov 6, 2019
1 parent 318fb3c commit a45b452
Show file tree
Hide file tree
Showing 26 changed files with 926 additions and 36 deletions.
10 changes: 7 additions & 3 deletions FubarDev.FtpServer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.dockerignore = .dockerignore
.editorconfig = .editorconfig
azure-pipelines.yml = azure-pipelines.yml
FubarDev.FtpServer.ruleset = FubarDev.FtpServer.ruleset
Global.props = Global.props
LICENSE.md = LICENSE.md
PackageLibrary.props = PackageLibrary.props
README.md = README.md
stylecop.json = stylecop.json
azure-pipelines.yml = azure-pipelines.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FubarDev.FtpServer.FileSystem.DotNet", "src\FubarDev.FtpServer.FileSystem.DotNet\FubarDev.FtpServer.FileSystem.DotNet.csproj", "{9A83C6F5-378B-48B3-B32A-151DB90B390C}"
Expand Down Expand Up @@ -60,14 +60,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFtpServer.Shell", "samp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestFtpServer.Api", "samples\TestFtpServer.Api\TestFtpServer.Api.csproj", "{02D103B2-B9CA-4A7F-AB79-6FEC68C62B47}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickStart.GenericHost", "samples\QuickStart.GenericHost\QuickStart.GenericHost.csproj", "{23752176-0F1A-4352-B677-CD7878B7E8FC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuickStart.GenericHost", "samples\QuickStart.GenericHost\QuickStart.GenericHost.csproj", "{23752176-0F1A-4352-B677-CD7878B7E8FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuickStart.AspNetCoreHost", "samples\QuickStart.AspNetCoreHost\QuickStart.AspNetCoreHost.csproj", "{D03594D1-84CC-4B55-A56D-20ED446CFDB1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickStart.AspNetCoreHost", "samples\QuickStart.AspNetCoreHost\QuickStart.AspNetCoreHost.csproj", "{D03594D1-84CC-4B55-A56D-20ED446CFDB1}"
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ReadLine", "third-party\ReadLine\ReadLine.shproj", "{F68F448F-E8A4-4536-9A83-12018B6294B0}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\FubarDev.FtpServer.Shared\FubarDev.FtpServer.Shared.projitems*{52126b62-de83-4597-a832-d144f9a5a870}*SharedItemsImports = 13
third-party\DotNet.Glob\DotNet.Glob.projitems*{a45290b4-20f7-48e9-8543-c69240bfb9f8}*SharedItemsImports = 13
third-party\ReadLine\ReadLine.projitems*{f68f448f-e8a4-4536-9a83-12018b6294b0}*SharedItemsImports = 13
third-party\GnuSslStream\GnuSslStream.projitems*{fd524518-e097-4ac0-aac8-d5ea24f31545}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -160,6 +163,7 @@ Global
{02D103B2-B9CA-4A7F-AB79-6FEC68C62B47} = {7004709E-5C0D-4D3E-AF27-89968FA47C19}
{23752176-0F1A-4352-B677-CD7878B7E8FC} = {7004709E-5C0D-4D3E-AF27-89968FA47C19}
{D03594D1-84CC-4B55-A56D-20ED446CFDB1} = {7004709E-5C0D-4D3E-AF27-89968FA47C19}
{F68F448F-E8A4-4536-9A83-12018B6294B0} = {441F13FB-D457-402E-8DAC-4B6485AFCE30}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DAA7D00E-C61D-4640-A54E-D307E4682263}
Expand Down
31 changes: 31 additions & 0 deletions samples/TestFtpServer.Api/FtpConnectionStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// <copyright file="FtpConnectionStatus.cs" company="Fubar Development Junker">
// Copyright (c) Fubar Development Junker. All rights reserved.
// </copyright>

namespace TestFtpServer.Api
{
/// <summary>
/// Information about a single FTP connection.
/// </summary>
public class FtpConnectionStatus
{
/// <summary>
/// Initializes a new instance of the <see cref="FtpConnectionStatus"/> class.
/// </summary>
/// <param name="id">The ID of the connection.</param>
public FtpConnectionStatus(string id)
{
Id = id;
}

/// <summary>
/// Gets or sets the ID of the connection.
/// </summary>
public string Id { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the connection is alive (read: not expired).
/// </summary>
public bool IsAlive { get; set; }
}
}
13 changes: 13 additions & 0 deletions samples/TestFtpServer.Api/IFtpServerHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ public interface IFtpServerHost
/// <returns>The task.</returns>
Task StopAsync();

/// <summary>
/// Gets all active connections.
/// </summary>
/// <returns></returns>
ICollection<FtpConnectionStatus> GetConnections();

/// <summary>
/// Closes the connection.
/// </summary>
/// <param name="connectionId">The ID of the connection to be closed.</param>
/// <returns>The task.</returns>
Task CloseConnectionAsync(string connectionId);

/// <summary>
/// Get the list of registered simple modules.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// <copyright file="CloseConnectionCommandHandler.cs" company="Fubar Development Junker">
// Copyright (c) Fubar Development Junker. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

using JKang.IpcServiceFramework;

using TestFtpServer.Api;

namespace TestFtpServer.Shell.Commands
{
/// <summary>
/// Command handler for closing a client FTP connection.
/// </summary>
public class CloseConnectionCommandHandler : ICommandInfo
{
private readonly IpcServiceClient<IFtpServerHost> _client;

/// <summary>
/// Initializes a new instance of the <see cref="CloseConnectionCommandHandler"/> class.
/// </summary>
/// <param name="client">The IPC client.</param>
public CloseConnectionCommandHandler(IpcServiceClient<IFtpServerHost> client)
{
_client = client;
}

/// <inheritdoc />
public string Name { get; } = "connection";

/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = Array.Empty<string>();

/// <inheritdoc />
public async IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(
[EnumeratorCancellation] CancellationToken cancellationToken)
{
var connections = await _client
.InvokeAsync(host => host.GetConnections(), cancellationToken)
.ConfigureAwait(false);
foreach (var connection in connections)
{
yield return new CloseConnectionFinalCommandHandler(_client, connection.Id);
}
}

private class CloseConnectionFinalCommandHandler : IExecutableCommandInfo
{
private readonly IpcServiceClient<IFtpServerHost> _client;

/// <summary>
/// Initializes a new instance of the <see cref="CloseConnectionFinalCommandHandler"/> class.
/// </summary>
/// <param name="client">The IPC client.</param>
/// <param name="connectionId">The FTP connection ID.</param>
public CloseConnectionFinalCommandHandler(
IpcServiceClient<IFtpServerHost> client,
string connectionId)
{
_client = client;
Name = connectionId;
}

/// <inheritdoc />
public string Name { get; }

/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = Array.Empty<string>();

/// <inheritdoc />
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken)
=> AsyncEnumerable.Empty<ICommandInfo>();

/// <inheritdoc />
public Task ExecuteAsync(CancellationToken cancellationToken)
{
return _client.InvokeAsync(host => host.CloseConnectionAsync(Name), cancellationToken);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -33,8 +34,10 @@ public ContinueCommandHandler(IpcServiceClient<Api.IFtpServerHost> client)
/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = new[] { "resume" };

/// <param name="cancellationToken"></param>
/// <inheritdoc />
public IReadOnlyCollection<ICommandInfo> SubCommands { get; } = Array.Empty<ICommandInfo>();
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken)
=> AsyncEnumerable.Empty<ICommandInfo>();

/// <inheritdoc />
public Task ExecuteAsync(CancellationToken cancellationToken)
Expand Down
23 changes: 19 additions & 4 deletions samples/TestFtpServer.Shell/Commands/ExitCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
// Copyright (c) Fubar Development Junker. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using JKang.IpcServiceFramework;

using TestFtpServer.Api;

namespace TestFtpServer.Shell.Commands
{
/// <summary>
Expand All @@ -15,24 +19,35 @@ namespace TestFtpServer.Shell.Commands
public class ExitCommandHandler : IRootCommandInfo, IExecutableCommandInfo
{
private readonly IShellStatus _status;
private readonly IAsyncEnumerable<ICommandInfo> _subCommands;

/// <summary>
/// Initializes a new instance of the <see cref="IExecutableCommandInfo"/> class.
/// </summary>
/// <param name="client">The IPC client.</param>
/// <param name="status">The shell status.</param>
public ExitCommandHandler(IShellStatus status)
public ExitCommandHandler(
IpcServiceClient<IFtpServerHost> client,
IShellStatus status)
{
_status = status;
_subCommands = new ICommandInfo[]
{
new CloseConnectionCommandHandler(client),
}
.ToAsyncEnumerable();
}

/// <inheritdoc />
public string Name { get; } = "exit";

/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = new[] { "quit" };
public IReadOnlyCollection<string> AlternativeNames { get; } = new[] { "quit", "close" };

/// <param name="cancellationToken"></param>
/// <inheritdoc />
public IReadOnlyCollection<ICommandInfo> SubCommands { get; } = Array.Empty<ICommandInfo>();
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken)
=> _subCommands;

/// <inheritdoc />
public Task ExecuteAsync(CancellationToken cancellationToken)
Expand Down
5 changes: 4 additions & 1 deletion samples/TestFtpServer.Shell/Commands/HelpCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -28,8 +29,10 @@ public HelpCommandHandler(
/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = Array.Empty<string>();

/// <param name="cancellationToken"></param>
/// <inheritdoc />
public IReadOnlyCollection<ICommandInfo> SubCommands { get; } = Array.Empty<ICommandInfo>();
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken)
=> AsyncEnumerable.Empty<ICommandInfo>();

/// <inheritdoc />
public Task ExecuteAsync(CancellationToken cancellationToken)
Expand Down
5 changes: 4 additions & 1 deletion samples/TestFtpServer.Shell/Commands/PauseCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -35,8 +36,10 @@ public PauseCommandHandler(IpcServiceClient<IFtpServerHost> client)
/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = Array.Empty<string>();

/// <param name="cancellationToken"></param>
/// <inheritdoc />
public IReadOnlyCollection<ICommandInfo> SubCommands { get; } = Array.Empty<ICommandInfo>();
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken)
=> AsyncEnumerable.Empty<ICommandInfo>();

/// <inheritdoc />
public Task ExecuteAsync(CancellationToken cancellationToken)
Expand Down
21 changes: 16 additions & 5 deletions samples/TestFtpServer.Shell/Commands/ShowCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ namespace TestFtpServer.Shell.Commands
/// </summary>
public class ShowCommandHandler : IRootCommandInfo
{
private readonly IAsyncEnumerable<ICommandInfo> _subCommands;

/// <summary>
/// Initializes a new instance of the <see cref="ShowCommandHandler"/> class.
/// </summary>
Expand All @@ -28,19 +30,26 @@ public ShowCommandHandler(
IpcServiceClient<IFtpServerHost> client,
IShellStatus status)
{
SubCommands = status.ExtendedModuleInfoName
_subCommands = status.ExtendedModuleInfoName
.Select(x => new ModuleCommandInfo(client, x))
.ToList();
.Concat(
new ICommandInfo[]
{
new ShowConnectionsCommandInfo(client),
})
.ToList()
.ToAsyncEnumerable();
}

/// <inheritdoc />
public string Name { get; } = "show";

/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = Array.Empty<string>();
public IReadOnlyCollection<string> AlternativeNames { get; } = new[] { "list" };

/// <param name="cancellationToken"></param>
/// <inheritdoc />
public IReadOnlyCollection<ICommandInfo> SubCommands { get; }
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken) => _subCommands;

private class ModuleCommandInfo : IExecutableCommandInfo
{
Expand All @@ -65,8 +74,10 @@ public ModuleCommandInfo(
/// <inheritdoc />
public IReadOnlyCollection<string> AlternativeNames { get; } = Array.Empty<string>();

/// <param name="cancellationToken"></param>
/// <inheritdoc />
public IReadOnlyCollection<ICommandInfo> SubCommands { get; } = Array.Empty<ICommandInfo>();
public IAsyncEnumerable<ICommandInfo> GetSubCommandsAsync(CancellationToken cancellationToken)
=> AsyncEnumerable.Empty<ICommandInfo>();

/// <inheritdoc />
public async Task ExecuteAsync(CancellationToken cancellationToken)
Expand Down
Loading

0 comments on commit a45b452

Please sign in to comment.