/// <summary> /// Handles the SMTP session. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which performs the operation.</returns> async Task RunAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return; } await OutputGreetingAsync(cancellationToken).ConfigureAwait(false); while (_retryCount-- > 0 && Context.IsQuitRequested == false && cancellationToken.IsCancellationRequested == false) { var text = await Context.Text.ReadLineAsync(cancellationToken).ConfigureAwait(false); SmtpCommand command; SmtpResponse errorResponse; if (_stateMachine.TryAccept(new TokenEnumerator(new StringTokenReader(text)), out command, out errorResponse) == false) { await OuputErrorMessageAsync(errorResponse, cancellationToken).ConfigureAwait(false); continue; } // the command was a normal command so we can reset the retry count _retryCount = 5; await command.ExecuteAsync(Context, cancellationToken).ConfigureAwait(false); } }
/// <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); } } }
/// <summary> /// Advances the enumerator to the next command in the stream. /// </summary> /// <param name="segments">The list of array segments to read the command from.</param> /// <param name="command">The command that was found.</param> /// <param name="errorResponse">The error response that indicates why a command could not be accepted.</param> /// <returns>true if a valid command was found, false if not.</returns> bool TryAccept(IReadOnlyList <ArraySegment <byte> > segments, out SmtpCommand command, out SmtpResponse errorResponse) { return(_stateMachine.TryAccept(new TokenEnumerator(new ByteArrayTokenReader(segments)), out command, out errorResponse)); }
/// <summary> /// Advances the enumerator to the next command in the stream. /// </summary> /// <param name="context">The session context to use when making session based transitions.</param> /// <param name="segments">The list of array segments to read the command from.</param> /// <param name="command">The command that was found.</param> /// <param name="errorResponse">The error response that indicates why a command could not be accepted.</param> /// <returns>true if a valid command was found, false if not.</returns> bool TryAccept(SmtpSessionContext context, IReadOnlyList <ArraySegment <byte> > segments, out SmtpCommand command, out SmtpResponse errorResponse) { var tokenEnumerator = new TokenEnumerator(new ByteArrayTokenReader(segments)); return(_stateMachine.TryAccept(context, tokenEnumerator, out command, out errorResponse)); }