public virtual void ExecuteResultBody(ControllerContext context, ResumingRequest resumingRequest) { WriteCommonHeaders(context, resumingRequest); if (ShouldProceedAfterEvaluatingPreconditions(context.HttpContext, resumingRequest)) { using (FileContents) { if (resumingRequest.IsRangeRequest) WritePartialContent(context, FileContents, resumingRequest); else WriteFullContent(context, FileContents); } } }
public override void ExecuteResult(ControllerContext context) { if (context == null) throw new ArgumentNullException("context"); ResumingRequest resumingRequest = new ResumingRequest(context.HttpContext, FileContents.Length); ExecuteResultBody(context, resumingRequest); }
protected virtual bool ShouldProceedAfterEvaluatingPreconditions(HttpContextBase context, ResumingRequest 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) { //The request had a date check; requested entity is newer so we return the full entity resumingRequest.Ranges = null; } } else { if (!check.Equals(EntityTag)) { //The request had an entity tag; it didn't match our tag so we return the full entity resumingRequest.Ranges = null; } } } if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfMatch)]))) { IEnumerable<string> entitiesTags = check.Split(','); if ((string.IsNullOrEmpty(EntityTag) && entitiesTags.Count() > 0) || (!entitiesTags.Any(entity => entitiesTags.Equals(EntityTag)))) { context.Response.StatusCode = (int)HttpStatusCode.PreconditionFailed; return false; } } //If we have an EntityTag and the header has * or a matching tag: // If the request is GET or HEAD, we have to return NotModified; // Else we have to return PreconditionFailed //Else we allow continued processing for If-Modified check 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(this.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 the resource changed since (valid) supplied date, return PreconditionFailed //Else proceed if (DateTimeOffset.TryParse(check, out preconditionDateTime)) { if (!LastModified.HasValue || ((LastModified.Value - preconditionDateTime).TotalSeconds > 0)) { //Resource is newer or we don't know for sure. context.Response.StatusCode = (int)HttpStatusCode.PreconditionFailed; return false; } } } if (!string.IsNullOrEmpty(check = (request.Headers[HttpWorkerRequest.GetKnownRequestHeaderName(HttpWorkerRequest.HeaderIfModifiedSince)]))) { //If the resource changed since (valid) supplied date, return NotModified //Ele proceed 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; }
/// <summary> /// Writes the common headers to the response stream. /// </summary> /// <param name="context">The executing controller context.</param> protected virtual void WriteCommonHeaders(ControllerContext context, ResumingRequest resumingRequest) { if (resumingRequest.IsMultipartRequest) { context.HttpContext.Response.ContentType = string.Format("multipart/byteranges; boundary={0}", MultipartBoundary); } else { context.HttpContext.Response.ContentType = ContentType; } context.HttpContext.Response.AddHeader( HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderAcceptRanges), "bytes"); if (!string.IsNullOrEmpty(resumingRequest.FileName)) { context.HttpContext.Response.AddHeader( "Content-Disposition", string.Format(CustomDispositionType ?? "inline" + "; filename=\"{0}\"", !string.IsNullOrWhiteSpace(CustomDisposition) ? CustomDisposition :resumingRequest.FileName)); } if (!string.IsNullOrEmpty(this.EntityTag)) { context.HttpContext.Response.AddHeader( HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderEtag), this.EntityTag); } if (LastModified.HasValue) { context.HttpContext.Response.AddHeader( HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderLastModified), LastModified.Value.ToString("R")); } }
/// <summary> /// Write specific byte ranges to the output stream specified by the ResumableDownloadRequest object. /// </summary> /// <param name="context">The executing controller context.</param> /// <param name="fileContent">The stream from which the byte data is read.</param> // See http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html for multipart format information public virtual void WritePartialContent(ControllerContext context, Stream fileContent, ResumingRequest resumingRequest) { var response = context.HttpContext.Response; response.StatusCode = (int)HttpStatusCode.PartialContent; if (!resumingRequest.IsMultipartRequest) context.HttpContext.Response.AddHeader( HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentRange), string.Format("bytes {0}-{1}/{2}", resumingRequest.Ranges.First().StartByte, resumingRequest.Ranges.First().EndByte, fileContent.Length) ); foreach (var range in resumingRequest.Ranges) { if (!response.IsClientConnected) return; if (resumingRequest.IsMultipartRequest) { response.Output.WriteLine(string.Format("--{0}", MultipartBoundary)); response.Output.WriteLine(string.Format("{0}: {1}", HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentType), ContentType)); response.Output.WriteLine(string.Format("{0}: bytes {1}-{2}/{3}", HttpWorkerRequest.GetKnownResponseHeaderName(HttpWorkerRequest.HeaderContentRange), resumingRequest.Ranges.First().StartByte, resumingRequest.Ranges.First().EndByte, fileContent.Length)); response.Output.WriteLine(); } WriteBinaryData(context, fileContent, range.StartByte, range.EndByte); if (resumingRequest.IsMultipartRequest) response.Output.WriteLine(); } if (resumingRequest.IsMultipartRequest) { response.Output.WriteLine(string.Format("--{0}--", MultipartBoundary)); response.Output.WriteLine(); } }