public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            ResumeRequest resumingRequest = new ResumeRequest(context.HttpContext, FileContents.Length)
            {
                FileName = _fileName
            };

            context.HttpContext.Response.Headers[HttpHeaders.AccessControlExposeHeaders] = HttpHeaders.ContentDisposition;
            ExecuteResultBody(context, resumingRequest);
        }
        public virtual void ExecuteResultBody(ControllerContext context, ResumeRequest resumingRequest)
        {
            WriteCommonHeaders(context, resumingRequest);

            if (ShouldProceedAfterEvaluatingPreconditions(context.HttpContext, resumingRequest))
            {
                using (FileContents)
                {
                    if (resumingRequest.IsRangeRequest)
                    {
                        WritePartialContent(context, FileContents, resumingRequest);
                    }
                    else
                    {
                        WriteFullContent(context, FileContents);
                    }
                }
            }
        }
        protected virtual void WriteCommonHeaders(ControllerContext context, ResumeRequest resumingRequest)
        {
            context.HttpContext.Response.ContentType = resumingRequest.IsMultipartRequest ? $"multipart/byteranges; boundary={MultipartBoundary}" : ContentType;

            context.HttpContext.Response.AddHeader(HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderAcceptRanges), "bytes");

            if (!string.IsNullOrEmpty(resumingRequest.FileName))
            {
                context.HttpContext.Response.AddHeader("Content-Disposition", $"inline; filename=\"{resumingRequest.FileName}\"");
            }

            if (!string.IsNullOrEmpty(EntityTag))
            {
                context.HttpContext.Response.AddHeader(HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderEtag), EntityTag);
            }

            if (LastModified.HasValue)
            {
                context.HttpContext.Response.AddHeader(HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderLastModified), LastModified.Value.ToString("R"));
            }
        }
        protected virtual bool ShouldProceedAfterEvaluatingPreconditions(HttpContextBase context, ResumeRequest resumingRequest)
        {
            var            request = context.Request;
            string         check;
            DateTimeOffset preconditionDateTime;

            if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfRange)])))
            {
                if (DateTimeOffset.TryParse(check, out preconditionDateTime))
                {
                    if ((LastModified.Value - preconditionDateTime).TotalSeconds > 1)
                    {
                        resumingRequest.Ranges = null;
                    }
                }
                else
                {
                    if (!check.Equals(EntityTag))
                    {
                        resumingRequest.Ranges = null;
                    }
                }
            }


            if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfMatch)])))
            {
                IEnumerable <string> entitiesTags = check.Split(',');

                if ((string.IsNullOrEmpty(EntityTag) && entitiesTags.Any()) || (!entitiesTags.Any(entity => entitiesTags.Equals(EntityTag))))
                {
                    context.Response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                    return(false);
                }
            }

            if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfNoneMatch)])))
            {
                IEnumerable <string> entitiesTag = check.Split(',');
                if ((!string.IsNullOrEmpty(EntityTag) && entitiesTag.Contains("*")) || (entitiesTag.Any(entity => entity.Equals(EntityTag))))
                {
                    if (context.Request.RequestType == "GET" || context.Request.RequestType == "HEAD")
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.NotModified;
                        return(false);
                    }
                    else
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                        return(false);
                    }
                }
            }

            if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfUnmodifiedSince)])))
            {
                if (DateTimeOffset.TryParse(check, out preconditionDateTime))
                {
                    if (!LastModified.HasValue || ((LastModified.Value - preconditionDateTime).TotalSeconds > 0))
                    {
                        context.Response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                        return(false);
                    }
                }
            }


            if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfModifiedSince)])))
            {
                if (DateTimeOffset.TryParse(check, out preconditionDateTime))
                {
                    if (LastModified.HasValue)
                    {
                        if ((LastModified.Value - preconditionDateTime).TotalSeconds < 1)
                        {
                            context.Response.StatusCode = (int)HttpStatusCode.NotModified;
                            return(false);
                        }
                    }
                }
            }

            return(true);
        }
        public virtual void WritePartialContent(ControllerContext context, Stream fileContent, ResumeRequest resumingRequest)
        {
            var response = context.HttpContext.Response;

            response.StatusCode = (int)HttpStatusCode.PartialContent;

            if (!resumingRequest.IsMultipartRequest)
            {
                context.HttpContext.Response.AddHeader(HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentRange), $"bytes {resumingRequest.Ranges.First().Start}-{resumingRequest.Ranges.First().End}/{fileContent.Length}");
            }

            foreach (var range in resumingRequest.Ranges)
            {
                if (!response.IsClientConnected)
                {
                    return;
                }

                if (resumingRequest.IsMultipartRequest)
                {
                    response.Output.WriteLine($"--{MultipartBoundary}");
                    response.Output.WriteLine($"{HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentType)}: {ContentType}");
                    response.Output.WriteLine($"{HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentRange)}: bytes {resumingRequest.Ranges.First().Start}-{resumingRequest.Ranges.First().End}/{fileContent.Length}");
                    response.Output.WriteLine();
                }

                WriteBinaryData(context, fileContent, range.Start, range.End);

                if (resumingRequest.IsMultipartRequest)
                {
                    response.Output.WriteLine();
                }
            }

            if (resumingRequest.IsMultipartRequest)
            {
                response.Output.WriteLine($"--{MultipartBoundary}--");
                response.Output.WriteLine();
            }
        }