Skip to content

Commit ae9c394

Browse files
committed
Changes and tests for issue #82
1 parent 3ac3435 commit ae9c394

19 files changed

Lines changed: 220 additions & 185 deletions

samples/TestFtpServer/ServiceCollectionExtensions.cs

Lines changed: 9 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,13 @@
33
// </copyright>
44

55
using System;
6-
using System.Collections.Generic;
76
using System.IO;
8-
using System.Linq;
9-
using System.Security.Cryptography.X509Certificates;
107
using System.Text;
118
using System.Threading;
12-
using System.Threading.Tasks;
139

1410
using FubarDev.FtpServer;
1511
using FubarDev.FtpServer.AccountManagement.Directories.RootPerUser;
1612
using FubarDev.FtpServer.AccountManagement.Directories.SingleRootWithoutHome;
17-
using FubarDev.FtpServer.Authentication;
1813
using FubarDev.FtpServer.CommandExtensions;
1914
using FubarDev.FtpServer.Commands;
2015
using FubarDev.FtpServer.FileSystem;
@@ -226,33 +221,6 @@ public static IServiceCollection AddFtpServices(
226221
break;
227222
}
228223

229-
if (options.Ftps.Implicit)
230-
{
231-
var implicitFtpsCertificate = options.GetCertificate();
232-
if (implicitFtpsCertificate != null)
233-
{
234-
services
235-
.AddSingleton(new ImplicitFtpsControlConnectionStreamAdapterOptions(implicitFtpsCertificate))
236-
.AddSingleton<IFtpControlStreamAdapter, ImplicitFtpsControlConnectionStreamAdapter>();
237-
238-
// Ensure that PROT and PBSZ commands are working.
239-
services.Decorate<IFtpServer>(
240-
(ftpServer, _) =>
241-
{
242-
ftpServer.ConfigureConnection += (s, e) =>
243-
{
244-
var serviceProvider = e.Connection.ConnectionServices;
245-
var stateMachine = serviceProvider.GetRequiredService<IFtpLoginStateMachine>();
246-
var authTlsMechanism = serviceProvider.GetRequiredService<IEnumerable<IAuthenticationMechanism>>()
247-
.Single(x => x.CanHandle("TLS"));
248-
stateMachine.Activate(authTlsMechanism);
249-
};
250-
251-
return ftpServer;
252-
});
253-
}
254-
}
255-
256224
#if NETCOREAPP
257225
services.Decorate<IFtpServer>(
258226
(ftpServer, serviceProvider) =>
@@ -302,6 +270,15 @@ private static IFtpServerBuilder ConfigureServer(this IFtpServerBuilder builder,
302270
.EnableConnectionCheck();
303271
}
304272

273+
if (options.Ftps.Implicit)
274+
{
275+
var implicitFtpsCertificate = options.GetCertificate();
276+
if (implicitFtpsCertificate != null)
277+
{
278+
builder = builder.UseImplicitTls(implicitFtpsCertificate);
279+
}
280+
}
281+
305282
return builder;
306283
}
307284

@@ -329,41 +306,11 @@ private static UserCredential GetUserCredential(
329306
return credential;
330307
}
331308

332-
private class ImplicitFtpsControlConnectionStreamAdapterOptions
333-
{
334-
public ImplicitFtpsControlConnectionStreamAdapterOptions(X509Certificate2 certificate)
335-
{
336-
Certificate = certificate;
337-
}
338-
339-
public X509Certificate2 Certificate { get; }
340-
}
341-
342309
private static TimeSpan? ToTimeSpan(int? seconds)
343310
{
344311
return seconds == null
345312
? (TimeSpan?)null
346313
: TimeSpan.FromSeconds(seconds.Value);
347314
}
348-
349-
private class ImplicitFtpsControlConnectionStreamAdapter : IFtpControlStreamAdapter
350-
{
351-
private readonly ImplicitFtpsControlConnectionStreamAdapterOptions _options;
352-
private readonly ISslStreamWrapperFactory _sslStreamWrapperFactory;
353-
354-
public ImplicitFtpsControlConnectionStreamAdapter(
355-
ImplicitFtpsControlConnectionStreamAdapterOptions options,
356-
ISslStreamWrapperFactory sslStreamWrapperFactory)
357-
{
358-
_options = options;
359-
_sslStreamWrapperFactory = sslStreamWrapperFactory;
360-
}
361-
362-
/// <inheritdoc />
363-
public Task<Stream> WrapAsync(Stream stream, CancellationToken cancellationToken)
364-
{
365-
return _sslStreamWrapperFactory.WrapStreamAsync(stream, false, _options.Certificate, cancellationToken);
366-
}
367-
}
368315
}
369316
}

samples/TestFtpServer/appsettings.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
"Microsoft": "Information",
88
"FubarDev.FtpServer.CommandHandlers.ListCommandHandler": "Verbose",
99
"FubarDev.FtpServer.CommandHandlers.MlstCommandHandler": "Verbose"
10-
},
10+
}
1111
},
1212
"WriteTo": [
13-
{ "Name": "Console" }
13+
{
14+
"Name": "Console",
15+
"OutputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {ConnectionId} {Message:lj}{NewLine}{Exception}"
16+
}
1417
],
1518
"Enrich": ["FromLogContext"]
1619
},

src/FubarDev.FtpServer/ConnectionHandlers/IFtpConnectionAdapter.cs

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

5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
58
namespace FubarDev.FtpServer.ConnectionHandlers
69
{
710
/// <summary>
@@ -21,5 +24,12 @@ public interface IFtpConnectionAdapter : IFtpService
2124
/// Gets the pausable receiver for this connection adapter.
2225
/// </summary>
2326
IPausableFtpService Receiver { get; }
27+
28+
/// <summary>
29+
/// Writes all pending data to the output.
30+
/// </summary>
31+
/// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
32+
/// <returns>The task.</returns>
33+
Task FlushAsync(CancellationToken cancellationToken);
2434
}
2535
}

src/FubarDev.FtpServer/ConnectionHandlers/PassThroughConnectionAdapter.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace FubarDev.FtpServer.ConnectionHandlers
1818
/// </summary>
1919
internal class PassThroughConnectionAdapter : IFtpConnectionAdapter
2020
{
21-
private readonly IFtpService _transmitService;
21+
private readonly IPausableFtpService _transmitService;
2222
private readonly IPausableFtpService _receiverService;
2323

2424
/// <summary>
@@ -38,12 +38,12 @@ public PassThroughConnectionAdapter(
3838
socketPipe.Input,
3939
connectionPipe.Output,
4040
connectionClosed,
41-
loggerFactory?.CreateLogger(typeof(PassThroughConnectionAdapter).FullName + ":Receiver"));
41+
loggerFactory?.CreateLogger(typeof(PassThroughConnectionAdapter).FullName + ".Receiver"));
4242
_transmitService = new NonClosingNetworkPassThrough(
4343
connectionPipe.Input,
4444
socketPipe.Output,
4545
connectionClosed,
46-
loggerFactory?.CreateLogger(typeof(PassThroughConnectionAdapter).FullName + ":Transmitter"));
46+
loggerFactory?.CreateLogger(typeof(PassThroughConnectionAdapter).FullName + ".Transmitter"));
4747
}
4848

4949
/// <inheritdoc />
@@ -52,6 +52,13 @@ public PassThroughConnectionAdapter(
5252
/// <inheritdoc />
5353
public IPausableFtpService Receiver => _receiverService;
5454

55+
/// <inheritdoc />
56+
public async Task FlushAsync(CancellationToken cancellationToken)
57+
{
58+
await _transmitService.PauseAsync(cancellationToken);
59+
await _transmitService.ContinueAsync(cancellationToken);
60+
}
61+
5562
/// <inheritdoc />
5663
public Task StartAsync(CancellationToken cancellationToken)
5764
{

src/FubarDev.FtpServer/ConnectionHandlers/SecureConnectionAdapter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ public SecureConnectionAdapter(
5959
/// <inheritdoc />
6060
public IPausableFtpService Receiver => _activeCommunicationService.Receiver;
6161

62+
/// <inheritdoc />
63+
public Task FlushAsync(CancellationToken cancellationToken)
64+
{
65+
return _activeCommunicationService.FlushAsync(cancellationToken);
66+
}
67+
6268
/// <inheritdoc />
6369
public async Task ResetAsync(CancellationToken cancellationToken)
6470
{

src/FubarDev.FtpServer/ConnectionHandlers/SslStreamConnectionAdapter.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ public IPausableFtpService Receiver
5757
=> _info?.ReceiverService
5858
?? throw new InvalidOperationException("Receiver can only be accessed when the connection service was started.");
5959

60+
/// <inheritdoc />
61+
public async Task FlushAsync(CancellationToken cancellationToken)
62+
{
63+
if (_info != null)
64+
{
65+
await _info.TransmitterService.PauseAsync(cancellationToken);
66+
await _info.TransmitterService.ContinueAsync(cancellationToken);
67+
}
68+
}
69+
6070
/// <inheritdoc />
6171
public async Task StartAsync(CancellationToken cancellationToken)
6272
{
@@ -67,7 +77,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
6777
.ConfigureAwait(false);
6878

6979
var receiverLogger = _loggerFactory
70-
?.CreateLogger(typeof(SslStreamConnectionAdapter).FullName + ":Receiver");
80+
?.CreateLogger(typeof(SslStreamConnectionAdapter).FullName + ".Receiver");
7181
var receiverService = new NonClosingNetworkStreamReader(
7282
sslStream,
7383
_connectionPipe.Output,
@@ -76,7 +86,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
7686
receiverLogger);
7787

7888
var transmitterLogger = _loggerFactory
79-
?.CreateLogger(typeof(SslStreamConnectionAdapter).FullName + ":Transmitter");
89+
?.CreateLogger(typeof(SslStreamConnectionAdapter).FullName + ".Transmitter");
8090
var transmitterService = new NonClosingNetworkStreamWriter(
8191
sslStream,
8292
_connectionPipe.Input,
@@ -118,15 +128,15 @@ await _sslStreamWrapperFactory.CloseStreamAsync(info.SslStream, cancellationToke
118128
private class SslCommunicationInfo
119129
{
120130
public SslCommunicationInfo(
121-
IFtpService transmitterService,
131+
IPausableFtpService transmitterService,
122132
IPausableFtpService receiverService,
123133
Stream sslStream)
124134
{
125135
TransmitterService = transmitterService;
126136
ReceiverService = receiverService;
127137
SslStream = sslStream;
128138
}
129-
public IFtpService TransmitterService { get; }
139+
public IPausableFtpService TransmitterService { get; }
130140
public IPausableFtpService ReceiverService { get; }
131141
public Stream SslStream { get; }
132142
}

src/FubarDev.FtpServer/FtpConnection.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,12 @@ public FtpConnection(
194194
originalStream,
195195
_socketCommandPipe.Writer,
196196
_cancellationTokenSource,
197-
loggerFactory?.CreateLogger($"{nameof(StreamPipeWriterService)}:Socket:Receive"));
197+
loggerFactory?.CreateLogger($"{typeof(StreamPipeWriterService).FullName}.Socket.Receive"));
198198
_streamWriterService = new StreamPipeWriterService(
199199
originalStream,
200200
_socketResponsePipe.Reader,
201201
_cancellationTokenSource.Token,
202-
loggerFactory?.CreateLogger($"{nameof(StreamPipeWriterService)}:Socket:Transmit"));
202+
loggerFactory?.CreateLogger($"{typeof(StreamPipeWriterService).FullName}.Socket.Transmit"));
203203

204204
_networkStreamFeature = new NetworkStreamFeature(
205205
new SecureConnectionAdapter(
@@ -669,7 +669,7 @@ private Task DispatchCommandAsync(FtpContext context)
669669

670670
private void OnClosed()
671671
{
672-
Closed?.Invoke(this, new EventArgs());
672+
Closed?.Invoke(this, EventArgs.Empty);
673673
}
674674

675675
private async Task ReadCommandsFromPipeline(
@@ -922,7 +922,14 @@ protected override async Task<int> ReadFromStreamAsync(byte[] buffer, int offset
922922
using var registration = cancellationToken.Register(() => tcs.TrySetResult(null));
923923
var resultTask = await Task.WhenAny(readTask, tcs.Task)
924924
.ConfigureAwait(false);
925-
if (resultTask != readTask || cancellationToken.IsCancellationRequested)
925+
926+
if (cancellationToken.IsCancellationRequested)
927+
{
928+
Logger?.LogTrace("Cancelled through CancellationToken");
929+
return 0;
930+
}
931+
932+
if (resultTask != readTask)
926933
{
927934
Logger?.LogTrace("Cancelled through Task.Delay");
928935
return 0;

src/FubarDev.FtpServer/Networking/FtpServerListenerService.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,14 @@ await base.OnFailedAsync(exception, cancellationToken)
9797
/// <inheritdoc />
9898
protected override Task OnStoppedAsync(CancellationToken cancellationToken)
9999
{
100-
// Tell the channel that there's no more data coming
101-
_newClientWriter.Complete(_exception);
100+
if (!_connectionClosedCts.IsCancellationRequested)
101+
{
102+
// Tell the channel that there's no more data coming
103+
_newClientWriter.Complete(_exception);
102104

103-
// Signal a closed connection.
104-
_connectionClosedCts.Cancel();
105+
// Signal a closed connection.
106+
_connectionClosedCts.Cancel();
107+
}
105108

106109
return Task.CompletedTask;
107110
}

src/FubarDev.FtpServer/Networking/PassThroughService.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ protected override async Task ExecuteAsync(CancellationToken cancellationToken)
7676
await _writer.WriteAsync(memory, CancellationToken.None)
7777
.ConfigureAwait(false);
7878

79+
await _writer.FlushAsync(CancellationToken.None)
80+
.ConfigureAwait(false);
81+
7982
if (readResult.IsCanceled || readResult.IsCompleted)
8083
{
8184
break;

0 commit comments

Comments
 (0)