/// <summary> /// Execute the command handler against the specified session context. /// </summary> /// <param name="context">The session context to execute the command handler against.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { // The PROXY protocol requires that the receiver must wait for the // proxy command to be fully received before it starts processing the // session. Since the receiver is expected to speak first in SMTP, // i.e. sending the greeting on connect, we wait for the proxy // command to be consumed and processed before speaking to the // remote client. if (!_context.ServerOptions.Proxy) { await OutputGreetingAsync(cancellationToken).ReturnOnAnyThread(); } if (_context.ServerOptions.Proxy) { await IngestProxyAsync(context, cancellationToken).ReturnOnAnyThread(); } var retries = _context.ServerOptions.MaxRetryCount; while (retries-- > 0 && context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) { var text = await ReadCommandInputAsync(context, cancellationToken); if (text == null) { return; } if (TryMake(context, text, out var command, out var response)) { try { if (await ExecuteAsync(command, context, cancellationToken).ReturnOnAnyThread()) { _stateMachine.Transition(context); } retries = _context.ServerOptions.MaxRetryCount; continue; } catch (SmtpResponseException responseException) { context.IsQuitRequested = responseException.IsQuitRequested; response = responseException.Response; } catch (OperationCanceledException) { await context.NetworkClient.ReplyAsync(new SmtpResponse(SmtpReplyCode.ServiceClosingTransmissionChannel, "The session has be cancelled."), cancellationToken); return; } } await context.NetworkClient.ReplyAsync(CreateErrorResponse(response, retries), cancellationToken); } }
/// <summary> /// Execute the command handler against the specified session context. /// </summary> /// <param name="context">The session context to execute the command handler against.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var retries = _context.ServerOptions.MaxRetryCount; while (retries-- > 0 && context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) { var text = await ReadCommandInputAsync(context, cancellationToken).ConfigureAwait(false); if (text == null) { return; } if (TryMake(context, text, out var command, out var response) == false) { await context.NetworkClient.ReplyAsync(CreateErrorResponse(response, retries), cancellationToken).ConfigureAwait(false); continue; } try { if (await ExecuteAsync(command, context, cancellationToken).ConfigureAwait(false)) { _stateMachine.Transition(context); } retries = _context.ServerOptions.MaxRetryCount; } catch (SmtpResponseException responseException) when(responseException.IsQuitRequested) { await context.NetworkClient.ReplyAsync(responseException.Response, cancellationToken).ConfigureAwait(false); return; } catch (SmtpResponseException responseException) { response = CreateErrorResponse(responseException.Response, retries); await context.NetworkClient.ReplyAsync(response, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { await context.NetworkClient.ReplyAsync(new SmtpResponse(SmtpReplyCode.ServiceClosingTransmissionChannel, "The session has be cancelled."), cancellationToken).ConfigureAwait(false); return; } } }
/// <summary> /// Execute the command handler against the specified session context. /// </summary> /// <param name="context">The session context to execute the command handler against.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var retries = _context.ServerOptions.MaxRetryCount; while (retries-- > 0 && context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) { try { var command = await ReadCommandAsync(context, cancellationToken).ConfigureAwait(false); if (command == null) { return; } if (_stateMachine.TryAccept(command, out var errorResponse) == false) { throw new SmtpResponseException(errorResponse); } if (await ExecuteAsync(command, context, cancellationToken).ConfigureAwait(false)) { _stateMachine.Transition(context); } retries = _context.ServerOptions.MaxRetryCount; } catch (SmtpResponseException responseException) when(responseException.IsQuitRequested) { await context.Pipe.Output.WriteReplyAsync(responseException.Response, cancellationToken).ConfigureAwait(false); context.IsQuitRequested = true; } catch (SmtpResponseException responseException) { var response = CreateErrorResponse(responseException.Response, retries); await context.Pipe.Output.WriteReplyAsync(response, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { await context.Pipe.Output.WriteReplyAsync(new SmtpResponse(SmtpReplyCode.ServiceClosingTransmissionChannel, "The session has be cancelled."), CancellationToken.None).ConfigureAwait(false); } } }