Skip to content

Commit

Permalink
Allow Exceptions in the session to be bubbled up by raising a Session…
Browse files Browse the repository at this point in the history
…Faulted event. (cosullivan#107)
  • Loading branch information
jaredrsowers authored and cosullivan committed Jun 15, 2019
1 parent 60154eb commit 7712401
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 27 deletions.
22 changes: 22 additions & 0 deletions Src/SmtpServer/SessionFaultedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace SmtpServer
{
public class SessionFaultedEventArgs : SessionEventArgs
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The session context.</param>
/// <param name="exception">The exception that occured</param>
public SessionFaultedEventArgs(ISessionContext context, Exception exception) : base(context)
{
Exception = exception;
}

/// <summary>
/// Returns the exception.
/// </summary>
public Exception Exception { get; }
}
}
81 changes: 54 additions & 27 deletions Src/SmtpServer/SmtpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public class SmtpServer
/// </summary>
public event EventHandler<SessionEventArgs> SessionCompleted;

/// <summary>
/// Raised when a session has faulted.
/// </summary>
public event EventHandler<SessionFaultedEventArgs> SessionFaulted;

readonly ISmtpServerOptions _options;

/// <summary>
Expand Down Expand Up @@ -48,6 +53,15 @@ protected virtual void OnSessionCompleted(SessionEventArgs args)
SessionCompleted?.Invoke(this, args);
}

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

/// <summary>
/// Starts the SMTP server.
/// </summary>
Expand Down Expand Up @@ -75,39 +89,52 @@ async Task ListenAsync(IEndpointDefinition endpointDefinition, CancellationToken
{
var sessionContext = new SmtpSessionContext(_options, endpointDefinition);

// wait for a client connection
var stream = await endpointListener.GetStreamAsync(sessionContext, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();

sessionContext.NetworkClient = new NetworkClient(stream, _options.NetworkBufferSize, _options.NetworkBufferReadTimeout);

if (endpointDefinition.IsSecure && _options.ServerCertificate != null)
try
{
await sessionContext.NetworkClient.UpgradeAsync(_options.ServerCertificate, _options.SupportedSslProtocols, cancellationToken).ConfigureAwait(false);
// wait for a client connection
var stream = await endpointListener.GetStreamAsync(sessionContext, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
}

// create a new session to handle the connection
var session = new SmtpSession(sessionContext);
sessions.TryAdd(session, session);

OnSessionCreated(new SessionEventArgs(sessionContext));

session.Run(cancellationToken);
sessionContext.NetworkClient = new NetworkClient(stream, _options.NetworkBufferSize, _options.NetworkBufferReadTimeout);

#pragma warning disable 4014
session.Task
.ContinueWith(t =>
if (endpointDefinition.IsSecure && _options.ServerCertificate != null)
{
if (sessions.TryRemove(session, out var s))
{
sessionContext.NetworkClient.Dispose();
}
await sessionContext.NetworkClient.UpgradeAsync(_options.ServerCertificate, _options.SupportedSslProtocols, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
}

// create a new session to handle the connection
var session = new SmtpSession(sessionContext);
sessions.TryAdd(session, session);

OnSessionCreated(new SessionEventArgs(sessionContext));

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

#pragma warning disable 4014
session.Task
.ContinueWith(t =>
{
if (sessions.TryRemove(session, out var s))
{
sessionContext.NetworkClient.Dispose();
}

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

OnSessionCompleted(new SessionEventArgs(sessionContext));
},
cancellationToken);
#pragma warning restore 4014
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
OnSessionFaulted(new SessionFaultedEventArgs(sessionContext, ex));
}
}

// the server has been cancelled, wait for the tasks to complete
Expand Down
5 changes: 5 additions & 0 deletions Src/SmtpServer/SmtpSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public void Run(CancellationToken cancellationToken)
{
try
{
if (t.Exception != null)
{
_taskCompletionSource.TrySetException(t.Exception.InnerExceptions);
}

_taskCompletionSource.SetResult(t.IsCompleted);
}
catch
Expand Down

0 comments on commit 7712401

Please sign in to comment.