/// <summary>
        /// Initializes a new instance of the <see cref="FileUploadProvider"/> <see langword="class"/>.
        /// </summary>
        /// <param name="ticket">The value of <see cref="Ticket"/>.</param>
        /// <param name="requireSynchronousIO">The value of <see cref="requireSynchronousIO"/></param>
        public FileUploadProvider(FileTicketResponse ticket, bool requireSynchronousIO)
        {
            Ticket = ticket ?? throw new ArgumentNullException(nameof(ticket));

            ticketExpiryCts           = new CancellationTokenSource();
            taskCompletionSource      = new TaskCompletionSource <Stream>();
            completionTcs             = new TaskCompletionSource <object>();
            this.requireSynchronousIO = requireSynchronousIO;
        }
        public async Task <IActionResult> Download([Required, FromQuery] string ticket, CancellationToken cancellationToken)
        {
            if (ticket == null)
            {
                return(BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)));
            }

            var streamAccept = new MediaTypeHeaderValue(MediaTypeNames.Application.Octet);

            if (!Request.GetTypedHeaders().Accept.Any(x => streamAccept.IsSubsetOf(x)))
            {
                return(StatusCode(HttpStatusCode.NotAcceptable, new ErrorMessageResponse(ErrorCode.BadHeaders)
                {
                    AdditionalData = $"File downloads must accept both {MediaTypeNames.Application.Octet} and {MediaTypeNames.Application.Json}!"
                }));
            }

            var fileTicketResult = new FileTicketResponse
            {
                FileTicket = ticket
            };

            var tuple = await fileTransferService.RetrieveDownloadStream(fileTicketResult, cancellationToken).ConfigureAwait(false);

            var stream = tuple.Item1;

            try
            {
                if (tuple.Item2 != null)
                {
                    return(Conflict(tuple.Item2));
                }

                if (stream == null)
                {
                    return(Gone());
                }

                return(new LimitedFileStreamResult(stream));
            }
            catch
            {
                stream.Dispose();
                throw;
            }
        }
        /// <inheritdoc />
        public async Task <ErrorMessageResponse> SetUploadStream(FileTicketResponse ticket, Stream stream, CancellationToken cancellationToken)
        {
            if (ticket == null)
            {
                throw new ArgumentNullException(nameof(ticket));
            }

            FileUploadProvider uploadProvider;

            lock (uploadTickets)
            {
                if (!uploadTickets.TryGetValue(ticket.FileTicket, out uploadProvider))
                {
                    logger.LogTrace("Upload ticket {0} not found!", ticket.FileTicket);
                    return(new ErrorMessageResponse(ErrorCode.ResourceNotPresent));
                }

                uploadTickets.Remove(ticket.FileTicket);
            }

            return(await uploadProvider.Completion(stream, cancellationToken).ConfigureAwait(false));
        }
        public async Task <IActionResult> Upload([Required, FromQuery] string ticket, CancellationToken cancellationToken)
        {
            if (ticket == null)
            {
                return(BadRequest(new ErrorMessageResponse(ErrorCode.ModelValidationFailure)));
            }

            var fileTicketResult = new FileTicketResponse
            {
                FileTicket = ticket
            };

            var result = await fileTransferService.SetUploadStream(fileTicketResult, Request.Body, cancellationToken).ConfigureAwait(false);

            if (result != null)
            {
                return(result.ErrorCode == ErrorCode.ResourceNotPresent
                                        ? Gone()
                                        : Conflict(result));
            }

            return(NoContent());
        }
        /// <inheritdoc />
        public async Task <Tuple <FileStream, ErrorMessageResponse> > RetrieveDownloadStream(FileTicketResponse ticket, CancellationToken cancellationToken)
        {
            if (ticket == null)
            {
                throw new ArgumentNullException(nameof(ticket));
            }

            FileDownloadProvider downloadProvider;

            lock (downloadTickets)
            {
                if (!downloadTickets.TryGetValue(ticket.FileTicket, out downloadProvider))
                {
                    logger.LogTrace("Download ticket {0} not found!", ticket.FileTicket);
                    return(Tuple.Create <FileStream, ErrorMessageResponse>(null, null));
                }

                downloadTickets.Remove(ticket.FileTicket);
            }

            var errorCode = downloadProvider.ActivationCallback();

            if (errorCode.HasValue)
            {
                logger.LogDebug("Download ticket {0} failed activation!", ticket.FileTicket);
                return(Tuple.Create <FileStream, ErrorMessageResponse>(null, new ErrorMessageResponse(errorCode.Value)));
            }

            FileStream stream;

            try
            {
                if (downloadProvider.FileStreamProvider != null)
                {
                    stream = await downloadProvider.FileStreamProvider(cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    stream = ioManager.GetFileStream(downloadProvider.FilePath, downloadProvider.ShareWrite);
                }
            }
            catch (IOException ex)
            {
                return(Tuple.Create <FileStream, ErrorMessageResponse>(
                           null,
                           new ErrorMessageResponse(ErrorCode.IOError)
                {
                    AdditionalData = ex.ToString()
                }));
            }

            try
            {
                logger.LogTrace("Ticket {0} downloading...", ticket.FileTicket);
                return(Tuple.Create <FileStream, ErrorMessageResponse>(stream, null));
            }
            catch
            {
                stream.Dispose();
                throw;
            }
        }