private Task ExecuteBackgroundCommandAsync(
            FtpContext context,
            IFtpCommandBase handler,
            CancellationToken cancellationToken)
        {
            var backgroundTaskFeature = _connection.Features.Get <IBackgroundTaskLifetimeFeature?>();

            if (backgroundTaskFeature == null)
            {
                backgroundTaskFeature = new BackgroundTaskLifetimeFeature(
                    handler,
                    context.Command,
                    ct =>
                {
                    var executionContext = new FtpExecutionContext(context, handler, ct);
                    return(_executionDelegate(executionContext));
                },
                    cancellationToken);
                _connection.Features.Set(backgroundTaskFeature);
                return(Task.CompletedTask);
            }

            return(SendResponseAsync(
                       new FtpResponse(503, T("Parallel commands aren't allowed.")),
                       cancellationToken));
        }
        /// <inheritdoc />
        public async Task DispatchAsync(FtpContext context, CancellationToken cancellationToken)
        {
            var loginStateMachine =
                _loginStateMachine
                ?? throw new InvalidOperationException("Login state machine not initialized.");

            var commandHandlerContext = new FtpCommandHandlerContext(context);
            var result = _commandActivator.Create(commandHandlerContext);

            if (result == null)
            {
                await SendResponseAsync(
                    new FtpResponse(500, T("Syntax error, command unrecognized.")),
                    cancellationToken)
                .ConfigureAwait(false);

                return;
            }

            var handler         = result.Handler;
            var isLoginRequired = result.Information.IsLoginRequired;

            if (isLoginRequired && loginStateMachine.Status != SecurityStatus.Authorized)
            {
                await SendResponseAsync(
                    new FtpResponse(530, T("Not logged in.")),
                    cancellationToken)
                .ConfigureAwait(false);

                return;
            }

            if (result.Information.IsAbortable)
            {
                await ExecuteBackgroundCommandAsync(context, handler, cancellationToken)
                .ConfigureAwait(false);
            }
            else
            {
                var executionContext = new FtpExecutionContext(context, handler, cancellationToken);
                await _executionDelegate(executionContext)
                .ConfigureAwait(false);
            }
        }
        private async Task ExecuteCommandAsync(
            FtpExecutionContext context)
        {
            var response = await _connection.ExecuteCommand(
                context.Command,
                (command, ct) => context.CommandHandler.Process(command, ct),
                _logger,
                context.CommandAborted);

            if (response != null)
            {
                try
                {
                    await SendResponseAsync(response, context.Connection.CancellationToken)
                    .ConfigureAwait(false);
                }
                catch (Exception ex) when(ex.Is <OperationCanceledException>())
                {
                    _logger?.LogWarning("Sending the response cancelled: {response}", response);
                }
            }
        }
        private async Task ExecuteCommandAsync(
            FtpExecutionContext context)
        {
            var          localizationFeature = context.Connection.Features.Get <ILocalizationFeature>();
            var          command             = context.Command;
            IFtpResponse?response;

            try
            {
                response = await context.CommandHandler.Process(command, context.CommandAborted)
                           .ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                var exception = ex;
                while (exception is AggregateException aggregateException)
                {
                    exception = aggregateException.InnerException;
                }

                switch (exception)
                {
                case ValidationException validationException:
                    response = new FtpResponse(
                        425,
                        validationException.Message);
                    _logger?.LogWarning(validationException.Message);
                    break;

#if !NETSTANDARD1_3
                case SocketException se when se.ErrorCode == (int)SocketError.ConnectionAborted:
#endif
                case OperationCanceledException _:
                    response = new FtpResponse(426, localizationFeature.Catalog.GetString("Connection closed; transfer aborted."));
                    Debug.WriteLine($"Command {command} cancelled with response {response}");
                    break;

                case FileSystemException fse:
                {
                    var message = fse.Message != null ? $"{fse.FtpErrorName}: {fse.Message}" : fse.FtpErrorName;
                    _logger?.LogInformation($"Rejected command ({command}) with error {fse.FtpErrorCode} {message}");
                    response = new FtpResponse(fse.FtpErrorCode, message);
                    break;
                }

                case NotSupportedException nse:
                {
                    var message = nse.Message ?? T("Command {0} not supported", command);
                    _logger?.LogInformation(message);
                    response = new FtpResponse(502, message);
                    break;
                }

                default:
                    _logger?.LogError(0, ex, "Failed to process message ({0})", command);
                    response = new FtpResponse(501, T("Syntax error in parameters or arguments."));
                    break;
                }
            }

            if (response != null)
            {
                try
                {
                    await SendResponseAsync(response, context.Connection.CancellationToken)
                    .ConfigureAwait(false);
                }
                catch (Exception ex) when(ex.Is <OperationCanceledException>())
                {
                    _logger?.LogWarning("Sending the response cancelled: {response}", response);
                }
            }
        }