public void CancelUpload(ProgressSocketSessionService sessionService, string sessionKey)
        {
            WebsocketSession targetSession = sessionService.GetSessionByKey(sessionKey);

            if (targetSession == null)
            {
                throw new Exception("The requested session could not be found.");
            }

            targetSession.SetAttribute("abort", true);
        }
        private string PrepareSession(string sessionType, int unitTotal = -1)
        {
            List <IProcessArtifact> attributes = new List <IProcessArtifact>();

            WebsocketSessionType type = WebsocketSession.GetSessionType(sessionType);

            if (type == WebsocketSessionType.Progress)
            {
                if (unitTotal > -1)
                {
                    attributes.Add(new ProcessArtifact <int>("unitTotal", unitTotal));
                }
                string sessionKey = ProgressSessionService.PrepareNewSession(attributes.ToArray());
                return(sessionKey);
            }
            else if (type == WebsocketSessionType.Messaging)
            {
                attributes.Add(new ProcessArtifact <List <Models.WebsocketSessionMessageResponse> >("messages", new List <Models.WebsocketSessionMessageResponse>()));
                string sessionKey = MessaginSessionService.PrepareNewSession(attributes.ToArray());
                return(sessionKey);
            }

            throw new Exception("Unknown Session Type: " + sessionType);
        }
        public async Task <Models.FileMap> StreamFileToDiskWithProgress(HttpRequest request, ModelStateDictionary modelState, ILogger logger, ProgressSocketSessionService sessionService, string sessionKey)
        {
            if (!modelState.IsValid)
            {
                return(null);
            }

            if (!IsMultipartContentType(request.ContentType))
            {
                modelState.AddModelError("File", "Form content type must be 'multipart'");
                return(null);
            }

            var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), sessionService.DefaultFormOptions.MultipartBoundaryLengthLimit);
            var reader   = new MultipartReader(boundary, request.Body);
            var section  = await reader.ReadNextSectionAsync();

            string filenameForDisplay = "";
            string filenameOnDisk     = "";
            string fileContentType    = "";

            WebsocketSession targetSession = sessionService.GetSessionByKey(sessionKey);

            if (targetSession == null)
            {
                modelState.AddModelError("File", "The requested session could not be found.");
                return(null);
            }
            targetSession.SetAttribute("abort", false);

            int totalBytes = targetSession.GetAttributeValue <int>("unitTotal");

            if (section == null)
            {
                sessionService.UpdateProgress(targetSession.SessionKey, -1, -1, "");
            }

            while (section != null)
            {
                var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);

                if (hasContentDispositionHeader)
                {
                    // This check assumes that there's a file
                    // present without form data. If form data
                    // is present, this method immediately fails
                    // and returns the model error.
                    if (!HasFileContentDisposition(contentDisposition))
                    {
                        modelState.AddModelError("File", $"The request couldn't be processed (Error 2).");
                        return(null);
                    }
                    else
                    {
                        // Don't trust the file name sent by the client. To display
                        // the file name, HTML-encode the value.
                        filenameForDisplay = WebUtility.HtmlEncode(contentDisposition.FileName.Value);
                        filenameOnDisk     = Path.GetRandomFileName();
                        fileContentType    = GetStaticFileContentType(contentDisposition.FileName.Value);

                        if (!Directory.Exists(sessionService.QuarantinedFilePath))
                        {
                            Directory.CreateDirectory(sessionService.QuarantinedFilePath);
                        }
                        string quarantinedPath = Path.Combine(sessionService.QuarantinedFilePath, filenameOnDisk);

                        await SaveFileToDiskWithProgress(section, contentDisposition, modelState, quarantinedPath, sessionService.PermittedExtensions, sessionService.FileSizeLimit, sessionService, targetSession, totalBytes);

                        if (!modelState.IsValid)
                        {
                            return(null);
                        }

                        if (!Directory.Exists(sessionService.FileStoragePath))
                        {
                            Directory.CreateDirectory(sessionService.FileStoragePath);
                        }

                        System.IO.File.Move(quarantinedPath, Path.Combine(sessionService.FileStoragePath, filenameOnDisk));
                        logger.LogInformation("Uploaded file '{TrustedFileNameForDisplay}' saved to '{TargetFilePath}' as {TrustedFileNameForFileStorage}",
                                              filenameForDisplay,
                                              sessionService.FileStoragePath,
                                              filenameOnDisk);
                    }
                }

                // Drain any remaining section body that hasn't been consumed and
                // read the headers for the next section.
                section = await reader.ReadNextSectionAsync();
            }

            return(new Models.FileMap()
            {
                FilenameForDisplay = filenameForDisplay, FilenameOnDisk = filenameOnDisk, ContentType = fileContentType
            });
        }
        private async Task CopyToStreamWithProgress(Stream source, Stream destination, ProgressSocketSessionService sessionService, WebsocketSession targetSession, int totalBytes = -1, string stepId = "", CancellationToken cancellationToken = default(CancellationToken), int bufferSize = 0x1000)
        {
            byte[] buffer = new byte[bufferSize];
            int    currentBytesRead;
            int    totalBytesRead = 0;

            while ((currentBytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
            {
                bool shouldAbort = targetSession.GetAttributeValue <bool>("abort");
                if (shouldAbort)
                {
                    break;
                }
                await destination.WriteAsync(buffer, 0, currentBytesRead, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();

                totalBytesRead += currentBytesRead;
                sessionService.UpdateProgress(targetSession.SessionKey, totalBytesRead, totalBytes, stepId);

                //await Task.Delay(1); // Stops worker threads from going crazy with CPU
            }
        }
        public async Task SaveFileToDiskWithProgress(MultipartSection section, ContentDispositionHeaderValue contentDisposition, ModelStateDictionary modelState, string filepath, string[] permittedExtensions, long sizeLimit, ProgressSocketSessionService sessionService, WebsocketSession targetSession, int totalBytes)
        {
            using (FileStream stream = new FileStream(filepath, FileMode.Create, System.IO.FileAccess.Write))
            {
                // this is useful for guarding with smaller files; larger files that require streaming directly to disk can't validate;
                //if (!IsValidFileExtensionAndSignature(contentDisposition.FileName.Value, stream, permittedExtensions))
                //{
                //    modelState.AddModelError("File", "The file type isn't permitted or the file's signature doesn't match the file's extension.");
                //}

                await CopyToStreamWithProgress(section.Body, stream, sessionService, targetSession, totalBytes);

                if (stream.Length == 0)
                {
                    modelState.AddModelError("File", "The file is empty.");
                }
                else if (stream.Length > sizeLimit)
                {
                    var megabyteSizeLimit = sizeLimit / 1048576;
                    modelState.AddModelError("File", $"The file exceeds {megabyteSizeLimit:N1} MB.");
                }
            }
        }