Skip to content

Commit 6061b81

Browse files
committed
Control channel almost working
1 parent d8324ae commit 6061b81

23 files changed

+487
-104
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// <copyright file="FtpClientInactivityJob.cs" company="Fubar Development Junker">
2+
// Copyright (c) Fubar Development Junker. All rights reserved.
3+
// </copyright>
4+
5+
using Quartz;
6+
7+
namespace FtpServer;
8+
9+
public class FtpClientInactivityJob : IJob
10+
{
11+
private readonly IFtpClientManager _clientManager;
12+
private readonly TimeSpan _inactivityTimeSpan = TimeSpan.FromSeconds(10);
13+
14+
public FtpClientInactivityJob(IFtpClientManager clientManager)
15+
{
16+
_clientManager = clientManager;
17+
}
18+
19+
public async Task Execute(IJobExecutionContext context)
20+
{
21+
foreach (var clientInfo in _clientManager.Clients)
22+
{
23+
var client = clientInfo.Client;
24+
// clientInfo.ConnectionContext.ConnectionId;
25+
var inactivity = await client.Control.GetInactivityAsync(context.CancellationToken);
26+
if (inactivity >= _inactivityTimeSpan)
27+
{
28+
clientInfo.ConnectionContext.Abort();
29+
}
30+
}
31+
}
32+
}

samples/FtpServer/FtpClientInformation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
using Microsoft.AspNetCore.Connections;
88

9-
namespace FtpServerRestart01;
9+
namespace FtpServer;
1010

1111
public record FtpClientInformation(
1212
IFtpClient Client,
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// <copyright file="FtpClientManager.cs" company="Fubar Development Junker">
2+
// Copyright (c) Fubar Development Junker. All rights reserved.
3+
// </copyright>
4+
5+
using System.Collections.Immutable;
6+
using System.Diagnostics.CodeAnalysis;
7+
8+
using FubarDev.FtpServer.Abstractions;
9+
10+
using Microsoft.AspNetCore.Connections;
11+
12+
namespace FtpServer;
13+
14+
public class FtpClientManager : IFtpClientManager
15+
{
16+
private readonly object _activeClientsLock = new();
17+
private readonly IFtpClientFactory _clientFactory;
18+
private readonly ILogger<FtpClientManager> _logger;
19+
private volatile ImmutableList<FtpClientInformation> _activeClients = ImmutableList<FtpClientInformation>.Empty;
20+
21+
public FtpClientManager(
22+
IFtpClientFactory clientFactory,
23+
ILogger<FtpClientManager> logger)
24+
{
25+
_clientFactory = clientFactory;
26+
_logger = logger;
27+
}
28+
29+
public IEnumerable<FtpClientInformation> Clients
30+
{
31+
get
32+
{
33+
lock (_activeClientsLock)
34+
{
35+
return _activeClients;
36+
}
37+
}
38+
}
39+
40+
public ValueTask StartAsync(ConnectionContext connectionContext, CancellationToken stoppingToken = default)
41+
{
42+
Run(connectionContext, stoppingToken);
43+
return default;
44+
}
45+
46+
[SuppressMessage(
47+
"Usage",
48+
"VSTHRD100",
49+
Justification = "It's what we want here, because we're not using the returning task anyway")]
50+
private async void Run(ConnectionContext connectionContext, CancellationToken stoppingToken = default)
51+
{
52+
await using var registration = stoppingToken.Register(
53+
connectionContext.Abort);
54+
try
55+
{
56+
var client = await _clientFactory.CreateClientAsync(connectionContext, stoppingToken);
57+
var clientInfo = new FtpClientInformation(client, connectionContext);
58+
lock (_activeClientsLock)
59+
{
60+
_activeClients = _activeClients.Add(clientInfo);
61+
}
62+
63+
_logger.LogTrace("FTP client added");
64+
try
65+
{
66+
await client.RunAsync(connectionContext.ConnectionClosed);
67+
}
68+
finally
69+
{
70+
lock (_activeClientsLock)
71+
{
72+
_activeClients = _activeClients.Remove(clientInfo);
73+
_logger.LogTrace("FTP client removed");
74+
}
75+
}
76+
}
77+
catch (Exception exception)
78+
{
79+
_logger.LogError(exception, "Error during FTP client handling: {ErrorMessage}", exception.Message);
80+
}
81+
finally
82+
{
83+
await connectionContext.DisposeAsync();
84+
}
85+
}
86+
}

samples/FtpServer/FtpServer.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</ItemGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Sjm.IO" Version="0.1.0" />
16+
<PackageReference Include="Quartz.AspNetCore" Version="3.3.3" />
1717
<PackageReference Include="System.Interactive.Async" Version="5.0.0" />
1818
</ItemGroup>
1919

samples/FtpServer/FtpServerOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
using System.Net;
66

7-
namespace FtpServerRestart01;
7+
namespace FtpServer;
88

99
public class FtpServerOptions
1010
{

samples/FtpServer/FtpServerService.cs

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,30 @@
22
// Copyright (c) Fubar Development Junker. All rights reserved.
33
// </copyright>
44

5-
using System.Collections.Immutable;
65
using System.Net;
76
using System.Threading.Channels;
87

9-
using FubarDev.FtpServer.Abstractions;
10-
118
using Microsoft.AspNetCore.Connections;
129
using Microsoft.Extensions.Options;
1310

14-
namespace FtpServerRestart01;
11+
namespace FtpServer;
1512

1613
public class FtpServerService : BackgroundService
1714
{
18-
private readonly object _activeClientsLock = new();
19-
private readonly IFtpClientFactory _clientFactory;
2015
private readonly Channel<ConnectionContext> _connectionContextChannel;
2116
private readonly IConnectionListenerFactory _connectionListenerFactory;
17+
private readonly IFtpClientManager _clientManager;
2218
private readonly ILogger<FtpServerService> _logger;
2319
private readonly FtpServerOptions _serverOptions;
24-
private volatile ImmutableList<FtpClientInformation> _activeClients = ImmutableList<FtpClientInformation>.Empty;
2520

2621
public FtpServerService(
2722
IOptions<FtpServerOptions> serverOptions,
2823
IConnectionListenerFactory connectionListenerFactory,
29-
IFtpClientFactory clientFactory,
24+
IFtpClientManager clientManager,
3025
ILogger<FtpServerService> logger)
3126
{
3227
_connectionListenerFactory = connectionListenerFactory;
33-
_clientFactory = clientFactory;
28+
_clientManager = clientManager;
3429
_logger = logger;
3530
_serverOptions = serverOptions.Value;
3631
_connectionContextChannel = Channel.CreateUnbounded<ConnectionContext>();
@@ -65,9 +60,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
6560
while (!stoppingToken.IsCancellationRequested)
6661
{
6762
var connectionContext = await _connectionContextChannel.Reader.ReadAsync(stoppingToken);
68-
_ = Task.Run(
69-
() => ExecuteClientAsync(connectionContext, stoppingToken),
70-
CancellationToken.None);
63+
await _clientManager.StartAsync(connectionContext, stoppingToken);
7164
}
7265
}
7366
catch (OperationCanceledException)
@@ -116,34 +109,4 @@ private static IAsyncEnumerable<IPEndPoint> GetListenEndPointsAsync(
116109

117110
return options.ListenEndPoints.ToAsyncEnumerable();
118111
}
119-
120-
private async Task ExecuteClientAsync(
121-
ConnectionContext connectionContext,
122-
CancellationToken cancellationToken)
123-
{
124-
await using var registration = cancellationToken.Register(
125-
connectionContext.Abort);
126-
try
127-
{
128-
var client = await _clientFactory.CreateClientAsync(connectionContext, cancellationToken);
129-
var clientInfo = new FtpClientInformation(client, connectionContext);
130-
lock (_activeClientsLock)
131-
{
132-
_activeClients = _activeClients.Add(clientInfo);
133-
}
134-
135-
_logger.LogTrace("FTP client added");
136-
await client.RunAsync(connectionContext.ConnectionClosed);
137-
138-
lock (_activeClientsLock)
139-
{
140-
_activeClients = _activeClients.Remove(clientInfo);
141-
_logger.LogTrace("FTP client removed");
142-
}
143-
}
144-
finally
145-
{
146-
await connectionContext.DisposeAsync();
147-
}
148-
}
149112
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// <copyright file="IClientManager.cs" company="Fubar Development Junker">
2+
// Copyright (c) Fubar Development Junker. All rights reserved.
3+
// </copyright>
4+
5+
using Microsoft.AspNetCore.Connections;
6+
7+
namespace FtpServer;
8+
9+
public interface IFtpClientManager
10+
{
11+
IEnumerable<FtpClientInformation> Clients { get; }
12+
13+
ValueTask StartAsync(
14+
ConnectionContext connectionContext,
15+
CancellationToken stoppingToken = default);
16+
}

samples/FtpServer/Program.cs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
using System.Net;
66

7-
using FtpServerRestart01;
7+
using FtpServer;
88

99
using Microsoft.AspNetCore.Connections;
1010
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
1111

12+
using Quartz;
13+
1214
using var host = new HostBuilder()
1315
.ConfigureDefaults(args)
1416
.UseConsoleLifetime()
@@ -18,9 +20,25 @@
1820
.AddLogging()
1921
.AddOptions()
2022
.AddSingleton<IConnectionListenerFactory, SocketTransportFactory>()
21-
.AddEmbeddedClient()
22-
//.AddExternalClient()
23-
.AddSingleton<IHostedService, FtpServerService>();
23+
//.AddEmbeddedClient()
24+
.AddExternalClient()
25+
.AddSingleton<IHostedService, FtpServerService>()
26+
.AddSingleton<IFtpClientManager, FtpClientManager>()
27+
.AddQuartz(cfg =>
28+
{
29+
cfg.UseMicrosoftDependencyInjectionJobFactory();
30+
cfg.AddJob<FtpClientInactivityJob>(
31+
jc => jc.WithIdentity("client-cleanup", "ftp-server"));
32+
cfg.AddTrigger(tc =>
33+
{
34+
tc.WithSimpleSchedule(b =>
35+
b.WithInterval(TimeSpan.FromSeconds(1))
36+
.RepeatForever())
37+
.ForJob("client-cleanup", "ftp-server")
38+
.WithIdentity("client-cleanup-trigger", "ftp-server");
39+
});
40+
})
41+
.AddQuartzServer();
2442
services
2543
.Configure<FtpServerOptions>(opt => { opt.ListenEndPoints.Add(new IPEndPoint(IPAddress.IPv6Any, 8021)); });
2644
})

samples/FtpServer/appsettings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"LogLevel": {
44
"Default": "Trace",
55
"System": "Information",
6-
"Microsoft": "Information"
6+
"Microsoft": "Information",
7+
"Quartz": "Information"
78
}
89
}
910
}

src/FubarDev.FtpServer.Abstractions/IFtpClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ namespace FubarDev.FtpServer.Abstractions;
66

77
public interface IFtpClient
88
{
9+
IFtpClientControl Control { get; }
10+
911
ValueTask RunAsync(CancellationToken cancellationToken = default);
1012
}

0 commit comments

Comments
 (0)