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)); }
/// <summary> /// Initializes a new instance of the <see cref="FtpCommandSelection"/> class. /// </summary> /// <param name="handler">The FTP command handler.</param> /// <param name="handlerInformation">The FTP command handler information.</param> public FtpCommandSelection( IFtpCommandBase handler, IFtpCommandInformation handlerInformation) { Handler = handler; Information = handlerInformation; }
/// <summary> /// Initializes a new instance of the <see cref="FtpCommandSelection"/> class. /// </summary> /// <param name="handler">The FTP command handler.</param> /// <param name="handlerInformation">The FTP command handler information.</param> public FtpCommandSelection( [NotNull] IFtpCommandBase handler, [NotNull] IFtpCommandInformation handlerInformation) { Handler = handler; Information = handlerInformation; }
/// <summary> /// Initializes a new instance of the <see cref="FtpExecutionContext"/> class. /// </summary> /// <param name="ftpContext">The FTP context.</param> /// <param name="commandHandler">The FTP command handler.</param> /// <param name="commandAborted">The cancellation token signalling an aborted command.</param> public FtpExecutionContext( [NotNull] FtpContext ftpContext, [NotNull] IFtpCommandBase commandHandler, CancellationToken commandAborted) : base(ftpContext.Command, ftpContext.ServerCommandWriter, ftpContext.Connection) { CommandHandler = commandHandler; CommandAborted = commandAborted; }
/// <summary> /// Initializes a new instance of the <see cref="BackgroundTaskLifetimeFeature"/> class. /// </summary> /// <param name="command">The FTP command to be run in the background.</param> /// <param name="commandHandler">The FTP command handler.</param> /// <param name="backgroundTask">The task that gets run in the background.</param> /// <param name="cancellationToken">The cancellation token.</param> public BackgroundTaskLifetimeFeature( [NotNull] IFtpCommandBase commandHandler, [NotNull] FtpCommand command, [NotNull] Func <CancellationToken, Task> backgroundTask, CancellationToken cancellationToken) { Command = command; Handler = commandHandler; Task = Task.Run( async() => { var registration = cancellationToken.Register(() => _taskCts.Cancel()); try { await backgroundTask(_taskCts.Token) .ConfigureAwait(false); } finally { registration.Dispose(); } }); }
/// <inheritdoc /> public Task <FtpResponse> Execute(IFtpCommandBase handler, FtpCommand command) { lock (_syncRoot) { if (_handlerTask != null) { return(null); } _cancellationTokenSource = new CancellationTokenSource(); _handlerTask = handler.Process(command, _cancellationTokenSource.Token); } var taskCanceled = _handlerTask .ContinueWith( t => { var response = new FtpResponse(426, "Connection closed; transfer aborted."); Debug.WriteLine($"Background task cancelled with response {response}"); return(response); }, TaskContinuationOptions.OnlyOnCanceled); var taskCompleted = _handlerTask .ContinueWith( t => { var response = t.Result; Debug.WriteLine($"{DateTimeOffset.UtcNow} Background task finished successfully with response {response}"); return(response); }, TaskContinuationOptions.OnlyOnRanToCompletion); var taskFaulted = _handlerTask .ContinueWith( t => { var ex = t.Exception; _connection.Log?.LogError(ex, "Error while processing background command {0}", command); var response = new FtpResponse(501, "Syntax error in parameters or arguments."); Debug.WriteLine($"Background task failed with response {response}"); return(response); }, TaskContinuationOptions.OnlyOnFaulted); taskFaulted.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled); taskCompleted.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled); taskCanceled.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled); return(Task.Run( () => { var tasks = new List <Task <FtpResponse> > { taskCompleted, taskCanceled, taskFaulted }; do { try { var waitTasks = tasks.Where(x => !x.IsCompleted).Cast <Task>().ToArray(); if (waitTasks.Length != 0) { Debug.WriteLine($"Waiting for {waitTasks.Length} background tasks"); Task.WaitAll(waitTasks); } } catch (AggregateException ex) { ex.Handle(e => e is TaskCanceledException); } }while (tasks.Any(t => !t.IsCompleted)); var response = tasks.Single(x => x.Status == TaskStatus.RanToCompletion).Result; Debug.WriteLine($"{DateTimeOffset.UtcNow} Background task finished with response {response}"); lock (_syncRoot) { _handlerTask = null; } return response; })); }