Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
cosullivan committed Jun 21, 2020
1 parent ed92f42 commit b231b3e
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 55 deletions.
62 changes: 62 additions & 0 deletions Src/SampleApp/Examples/ServerCancellingExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using SmtpServer;

namespace SampleApp.Examples
{
public static class ServerCancellingExample
{
public static void Run()
{
var cancellationTokenSource = new CancellationTokenSource();

var options = new SmtpServerOptionsBuilder()
.ServerName("SmtpServer SampleApp")
.Port(9025)
.MailboxFilter(new SampleMailboxFilter(TimeSpan.FromSeconds(5)))
.Build();

var server = new SmtpServer.SmtpServer(options);
server.SessionCreated += OnSessionCreated;
server.SessionCompleted += OnSessionCompleted;
server.SessionFaulted += OnSessionFaulted;
server.SessionCancelled += OnSessionCancelled;

var serverTask = server.StartAsync(cancellationTokenSource.Token);

// ReSharper disable once MethodSupportsCancellation
Task.Run(() => SampleMailClient.Send());

Console.WriteLine("Press any key to cancel the server.");
Console.ReadKey();

Console.WriteLine("Forcibily cancelling the server and any active sessions");

cancellationTokenSource.Cancel();
serverTask.WaitWithoutException();

Console.WriteLine("The server has been cancelled.");
}

static void OnSessionCreated(object sender, SessionEventArgs e)
{
Console.WriteLine("Session Created.");
}

static void OnSessionCompleted(object sender, SessionEventArgs e)
{
Console.WriteLine("Session Completed");
}

static void OnSessionFaulted(object sender, SessionFaultedEventArgs e)
{
Console.WriteLine("Session Faulted: {0}", e.Exception);
}

static void OnSessionCancelled(object sender, SessionEventArgs e)
{
Console.WriteLine("Session Cancelled");
}
}
}
66 changes: 66 additions & 0 deletions Src/SampleApp/Examples/ServerShutdownExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using SmtpServer;

namespace SampleApp.Examples
{
public static class ServerShutdownExample
{
public static void Run()
{
var cancellationTokenSource = new CancellationTokenSource();

var options = new SmtpServerOptionsBuilder()
.ServerName("SmtpServer SampleApp")
.Port(9025)
.MailboxFilter(new SampleMailboxFilter(TimeSpan.FromSeconds(2)))
.Build();

var server = new SmtpServer.SmtpServer(options);
server.SessionCreated += OnSessionCreated;
server.SessionCompleted += OnSessionCompleted;
server.SessionFaulted += OnSessionFaulted;
server.SessionCancelled += OnSessionCancelled;

var serverTask = server.StartAsync(cancellationTokenSource.Token);

// ReSharper disable once MethodSupportsCancellation
Task.Run(() => SampleMailClient.Send());

Console.WriteLine("Press any key to shudown the server.");
Console.ReadKey();

Console.WriteLine("Gracefully shutting down the server.");
server.Shutdown();

server.ShutdownTask.WaitWithoutException();
Console.WriteLine("The server is no longer accepting new connections.");

Console.WriteLine("Waiting for active sessions to complete.");
serverTask.WaitWithoutException();

Console.WriteLine("All active sessions are complete.");
}

static void OnSessionCreated(object sender, SessionEventArgs e)
{
Console.WriteLine("Session Created.");
}

static void OnSessionCompleted(object sender, SessionEventArgs e)
{
Console.WriteLine("Session Completed");
}

static void OnSessionFaulted(object sender, SessionFaultedEventArgs e)
{
Console.WriteLine("Session Faulted: {0}", e.Exception);
}

static void OnSessionCancelled(object sender, SessionEventArgs e)
{
Console.WriteLine("Session Cancelled");
}
}
}
52 changes: 27 additions & 25 deletions Src/SampleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,48 @@ static async Task Main(string[] args)

ServicePointManager.ServerCertificateValidationCallback = SmtpServerTests.IgnoreCertificateValidationFailureForTestingOnly;

var options = new SmtpServerOptionsBuilder()
.ServerName("SmtpServer SampleApp")
.Port(587, false)
//.Certificate(SmtpServerTests.CreateCertificate())
.Build();
ServerShutdownExample.Run();

//var options = new SmtpServerOptionsBuilder()
// .ServerName("SmtpServer SampleApp")
// .Endpoint(endpoint =>
// endpoint
// .Port(587)
// .AllowUnsecureAuthentication(true)
// .AuthenticationRequired(false))
// .UserAuthenticator(new SampleUserAuthenticator())
// .Port(587, false)
// //.Certificate(SmtpServerTests.CreateCertificate())
// .Build();

var server = new SmtpServer.SmtpServer(options);
////var options = new SmtpServerOptionsBuilder()
//// .ServerName("SmtpServer SampleApp")
//// .Endpoint(endpoint =>
//// endpoint
//// .Port(587)
//// .AllowUnsecureAuthentication(true)
//// .AuthenticationRequired(false))
//// .UserAuthenticator(new SampleUserAuthenticator())
//// //.Certificate(SmtpServerTests.CreateCertificate())
//// .Build();

server.SessionCreated += OnSessionCreated;
server.SessionCompleted += OnSessionCompleted;
server.SessionFaulted += OnSessionFaulted;
//var server = new SmtpServer.SmtpServer(options);

var serverTask = server.StartAsync(CancellationToken.None);
//server.SessionCreated += OnSessionCreated;
//server.SessionCompleted += OnSessionCompleted;
//server.SessionFaulted += OnSessionFaulted;

Console.WriteLine("Starting, press any key to shutdown.");

Console.ReadKey();
//var serverTask = server.StartAsync(CancellationToken.None);

Console.WriteLine("Shutting down the server.");
//Console.WriteLine("Starting, press any key to shutdown.");

server.Shutdown();
//Console.ReadKey();

await server.ShutdownTask;
//Console.WriteLine("Shutting down the server.");

await serverTask.ConfigureAwait(false);
//server.Shutdown();

Console.WriteLine("Finished");
//await server.ShutdownTask;

//CustomEndpointListenerExample.Run();
//await serverTask.ConfigureAwait(false);

//Console.WriteLine("Finished");

////CustomEndpointListenerExample.Run();
}

static void OnSessionFaulted(object sender, SessionFaultedEventArgs e)
Expand Down
2 changes: 2 additions & 0 deletions Src/SampleApp/SampleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
<ItemGroup>
<Compile Include="Examples\SecureServerExample.cs" />
<Compile Include="Examples\CustomEndpointListenerExample.cs" />
<Compile Include="Examples\ServerCancellingExample.cs" />
<Compile Include="Examples\ServerShutdownExample.cs" />
<Compile Include="Examples\SimpleServerExample.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down
27 changes: 20 additions & 7 deletions Src/SampleApp/SampleMailboxFilter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net;
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using SmtpServer;
Expand All @@ -10,6 +11,14 @@ namespace SampleApp
{
public class SampleMailboxFilter : MailboxFilter
{
readonly TimeSpan _delay;
public SampleMailboxFilter() : this(TimeSpan.Zero) { }

public SampleMailboxFilter(TimeSpan delay)
{
_delay = delay;
}

/// <summary>
/// Returns a value indicating whether the given mailbox can be accepted as a sender.
/// </summary>
Expand All @@ -18,25 +27,27 @@ public class SampleMailboxFilter : MailboxFilter
/// <param name="size">The estimated message size to accept.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The acceptance state of the mailbox.</returns>
public override Task<MailboxFilterResult> CanAcceptFromAsync(
public override async Task<MailboxFilterResult> CanAcceptFromAsync(
ISessionContext context,
IMailbox @from,
int size,
CancellationToken cancellationToken)
{
await Task.Delay(_delay, cancellationToken);

if (@from == Mailbox.Empty)
{
return Task.FromResult(MailboxFilterResult.NoPermanently);
return MailboxFilterResult.NoPermanently;
}

var endpoint = (IPEndPoint)context.Properties[EndpointListener.RemoteEndPointKey];

if (endpoint.Address.Equals(IPAddress.Parse("127.0.0.1")))
{
return Task.FromResult(MailboxFilterResult.Yes);
return MailboxFilterResult.Yes;
}

return Task.FromResult(MailboxFilterResult.NoPermanently);
return MailboxFilterResult.NoPermanently;
}

/// <summary>
Expand All @@ -47,13 +58,15 @@ public override Task<MailboxFilterResult> CanAcceptFromAsync(
/// <param name="from">The sender's mailbox.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The acceptance state of the mailbox.</returns>
public override Task<MailboxFilterResult> CanDeliverToAsync(
public override async Task<MailboxFilterResult> CanDeliverToAsync(
ISessionContext context,
IMailbox to,
IMailbox @from,
CancellationToken cancellationToken)
{
return Task.FromResult(MailboxFilterResult.Yes);
await Task.Delay(_delay, cancellationToken);

return MailboxFilterResult.Yes;
}
}
}
41 changes: 29 additions & 12 deletions Src/SmtpServer/SmtpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ public class SmtpServer
/// </summary>
public event EventHandler<SessionFaultedEventArgs> SessionFaulted;

/// <summary>
/// Raised when a session has been cancelled through the cancellation token.
/// </summary>
public event EventHandler<SessionEventArgs> SessionCancelled;

readonly ISmtpServerOptions _options;
readonly SessionManager _sessions;
readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource();
Expand Down Expand Up @@ -67,6 +72,15 @@ protected virtual void OnSessionFaulted(SessionFaultedEventArgs args)
SessionFaulted?.Invoke(this, args);
}

/// <summary>
/// Raises the SessionCancelled Event.
/// </summary>
/// <param name="args">The event data.</param>
protected virtual void OnSessionCancelled(SessionEventArgs args)
{
SessionCancelled?.Invoke(this, args);
}

/// <summary>
/// Starts the SMTP server.
/// </summary>
Expand Down Expand Up @@ -109,6 +123,13 @@ async Task ListenAsync(IEndpointDefinition endpointDefinition, CancellationToken
{
await ListenAsync(sessionContext, endpointListener, cancellationToken);
}
catch (OperationCanceledException) when (_shutdownTokenSource.Token.IsCancellationRequested == false)
{
if (sessionContext.NetworkClient != null)
{
OnSessionCancelled(new SessionEventArgs(sessionContext));
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
Expand Down Expand Up @@ -164,25 +185,21 @@ public void Run(SmtpSessionContext sessionContext, CancellationToken cancellatio

_smtpServer.OnSessionCreated(new SessionEventArgs(sessionContext));

session.Run(cancellationToken);

#pragma warning disable 4014
session.Task.ContinueWith(
task =>
session.Run(
exception =>
{
Remove(session);

sessionContext.NetworkClient.Dispose();
if (task.Exception != null)

if (exception != null)
{
_smtpServer.OnSessionFaulted(new SessionFaultedEventArgs(sessionContext, task.Exception));
_smtpServer.OnSessionFaulted(new SessionFaultedEventArgs(sessionContext, exception));
}

_smtpServer.OnSessionCompleted(new SessionEventArgs(sessionContext));
},
},
cancellationToken);
#pragma warning restore 4014
}

public Task WaitAsync()
Expand Down
Loading

0 comments on commit b231b3e

Please sign in to comment.