internal static bool ProcessRangeRequest(HttpContext context, string physicalPath, long fileLength, string rangeHeader, string etag, DateTime lastModified) { HttpRequest request = context.Request; HttpResponse response = context.Response; bool handled = false; // return "416 Requested range not satisfiable" if the file length is zero. if (fileLength <= 0) { SendRangeNotSatisfiable(response, fileLength); handled = true; return handled; } string ifRangeHeader = request.Headers["If-Range"]; if (ifRangeHeader != null && ifRangeHeader.Length > 1) { // Is this an ETag or a Date? We only need to check two // characters; an ETag either begins with W/ or it is quoted. if (ifRangeHeader[0] == '"') { // it's a strong ETag if (ifRangeHeader != etag) { // the etags do not match, and we will therefore return the entire response return handled; } } else if (ifRangeHeader[0] == 'W' && ifRangeHeader[1] == '/') { // it's a weak ETag, and is therefore not usable for sub-range retrieval and // we will return the entire response return handled; } else { // It's a date. If it is greater than or equal to the last-write time of the file, we can send the range. if (IsOutDated(ifRangeHeader, lastModified)) { return handled; } } } // the expected format is "bytes = <range1>[, <range2>, ...]" // where <range> is "<first_byte_pos>-[<last_byte_pos>]" or "-<last_n_bytes>". int indexOfEquals = rangeHeader.IndexOf('='); if (indexOfEquals == -1 || indexOfEquals == rangeHeader.Length - 1) { //invalid syntax return handled; } // iterate through the byte ranges and write each satisfiable range to the response int startIndex = indexOfEquals + 1; bool isRangeHeaderSyntacticallyValid = true; long offset; long length; bool isSatisfiable; bool exceededMax = false; ByteRange[] byteRanges = null; int byteRangesCount = 0; long totalBytes = 0; while (startIndex < rangeHeader.Length && isRangeHeaderSyntacticallyValid) { isRangeHeaderSyntacticallyValid = GetNextRange(rangeHeader, ref startIndex, fileLength, out offset, out length, out isSatisfiable); if (!isRangeHeaderSyntacticallyValid) { break; } if (!isSatisfiable) { continue; } if (byteRanges == null) { byteRanges = new ByteRange[16]; } if (byteRangesCount >= byteRanges.Length) { // grow byteRanges array ByteRange[] buffer = new ByteRange[byteRanges.Length * 2]; int byteCount = byteRanges.Length * Marshal.SizeOf(byteRanges[0]); unsafe { fixed (ByteRange * src = byteRanges, dst = buffer) { StringUtil.memcpyimpl((byte*)src, (byte*)dst, byteCount); } } byteRanges = buffer; } byteRanges[byteRangesCount].Offset = offset; byteRanges[byteRangesCount].Length = length; byteRangesCount++; // IIS imposes this limitation too, and sends "400 Bad Request" if exceeded totalBytes += length; if (totalBytes > fileLength * MAX_RANGE_ALLOWED) { exceededMax = true; break; } } if (!isRangeHeaderSyntacticallyValid) { return handled; } if (exceededMax) { SendBadRequest(response); handled = true; return handled; } if (byteRangesCount == 0) { // we parsed the Range header and found no satisfiable byte ranges, so return "416 Requested Range Not Satisfiable" SendRangeNotSatisfiable(response, fileLength); handled = true; return handled; } string contentType = MimeMapping.GetMimeMapping(physicalPath); if (byteRangesCount == 1) { offset = byteRanges[0].Offset; length = byteRanges[0].Length; response.ContentType = contentType; string contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); response.AppendHeader("Content-Range", contentRange); SendFile(physicalPath, offset, length, fileLength, context); } else { response.ContentType = MULTIPART_CONTENT_TYPE; string contentRange; string partialContentType = "Content-Type: " + contentType + "\r\n"; for(int i = 0; i < byteRangesCount; i++) { offset = byteRanges[i].Offset; length = byteRanges[i].Length; response.Write(MULTIPART_RANGE_DELIMITER); response.Write(partialContentType); response.Write("Content-Range: "); contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); response.Write(contentRange); response.Write("\r\n\r\n"); SendFile(physicalPath, offset, length, fileLength, context); response.Write("\r\n"); } response.Write(MULTIPART_RANGE_END); } // if we make it here, we're sending a "206 Partial Content" status response.StatusCode = 206; response.AppendHeader("Last-Modified", HttpUtility.FormatHttpDateTime(lastModified)); response.AppendHeader("Accept-Ranges", "bytes"); response.AppendHeader("ETag", etag); response.AppendHeader("Cache-Control", "public"); handled = true; return handled; }
internal static bool ProcessRangeRequest(HttpContext context, string physicalPath, long fileLength, string rangeHeader, string etag, DateTime lastModified) { HttpRequest request = context.Request; HttpResponse response = context.Response; bool handled = false; // return "416 Requested range not satisfiable" if the file length is zero. if (fileLength <= 0) { SendRangeNotSatisfiable(response, fileLength); handled = true; return(handled); } string ifRangeHeader = request.Headers["If-Range"]; if (ifRangeHeader != null && ifRangeHeader.Length > 1) { // Is this an ETag or a Date? We only need to check two // characters; an ETag either begins with W/ or it is quoted. if (ifRangeHeader[0] == '"') { // it's a strong ETag if (ifRangeHeader != etag) { // the etags do not match, and we will therefore return the entire response return(handled); } } else if (ifRangeHeader[0] == 'W' && ifRangeHeader[1] == '/') { // it's a weak ETag, and is therefore not usable for sub-range retrieval and // we will return the entire response return(handled); } else { // It's a date. If it is greater than or equal to the last-write time of the file, we can send the range. if (IsOutDated(ifRangeHeader, lastModified)) { return(handled); } } } // the expected format is "bytes = <range1>[, <range2>, ...]" // where <range> is "<first_byte_pos>-[<last_byte_pos>]" or "-<last_n_bytes>". int indexOfEquals = rangeHeader.IndexOf('='); if (indexOfEquals == -1 || indexOfEquals == rangeHeader.Length - 1) { //invalid syntax return(handled); } // iterate through the byte ranges and write each satisfiable range to the response int startIndex = indexOfEquals + 1; bool isRangeHeaderSyntacticallyValid = true; long offset; long length; bool isSatisfiable; bool exceededMax = false; ByteRange[] byteRanges = null; int byteRangesCount = 0; long totalBytes = 0; while (startIndex < rangeHeader.Length && isRangeHeaderSyntacticallyValid) { isRangeHeaderSyntacticallyValid = GetNextRange(rangeHeader, ref startIndex, fileLength, out offset, out length, out isSatisfiable); if (!isRangeHeaderSyntacticallyValid) { break; } if (!isSatisfiable) { continue; } if (byteRanges == null) { byteRanges = new ByteRange[16]; } if (byteRangesCount >= byteRanges.Length) { // grow byteRanges array ByteRange[] buffer = new ByteRange[byteRanges.Length * 2]; int byteCount = byteRanges.Length * Marshal.SizeOf(byteRanges[0]); unsafe { fixed(ByteRange *src = byteRanges, dst = buffer) { StringUtil.memcpyimpl((byte *)src, (byte *)dst, byteCount); } } byteRanges = buffer; } byteRanges[byteRangesCount].Offset = offset; byteRanges[byteRangesCount].Length = length; byteRangesCount++; // IIS imposes this limitation too, and sends "400 Bad Request" if exceeded totalBytes += length; if (totalBytes > fileLength * MAX_RANGE_ALLOWED) { exceededMax = true; break; } } if (!isRangeHeaderSyntacticallyValid) { return(handled); } if (exceededMax) { SendBadRequest(response); handled = true; return(handled); } if (byteRangesCount == 0) { // we parsed the Range header and found no satisfiable byte ranges, so return "416 Requested Range Not Satisfiable" SendRangeNotSatisfiable(response, fileLength); handled = true; return(handled); } string contentType = MimeMapping.GetMimeMapping(physicalPath); if (byteRangesCount == 1) { offset = byteRanges[0].Offset; length = byteRanges[0].Length; response.ContentType = contentType; string contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); response.AppendHeader("Content-Range", contentRange); SendFile(physicalPath, offset, length, fileLength, context); } else { response.ContentType = MULTIPART_CONTENT_TYPE; string contentRange; string partialContentType = "Content-Type: " + contentType + "\r\n"; for (int i = 0; i < byteRangesCount; i++) { offset = byteRanges[i].Offset; length = byteRanges[i].Length; response.Write(MULTIPART_RANGE_DELIMITER); response.Write(partialContentType); response.Write("Content-Range: "); contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); response.Write(contentRange); response.Write("\r\n\r\n"); SendFile(physicalPath, offset, length, fileLength, context); response.Write("\r\n"); } response.Write(MULTIPART_RANGE_END); } // if we make it here, we're sending a "206 Partial Content" status response.StatusCode = 206; response.AppendHeader("Last-Modified", HttpUtility.FormatHttpDateTime(lastModified)); response.AppendHeader("Accept-Ranges", "bytes"); response.AppendHeader("ETag", etag); response.AppendHeader("Cache-Control", "public"); handled = true; return(handled); }
internal static unsafe bool ProcessRangeRequest(HttpContext context, string physicalPath, long fileLength, string rangeHeader, string etag, DateTime lastModified) { long offset; long length; HttpRequest request = context.Request; HttpResponse response = context.Response; bool flag = false; if (fileLength <= 0L) { SendRangeNotSatisfiable(response, fileLength); return true; } string ifRangeHeader = request.Headers["If-Range"]; if ((ifRangeHeader != null) && (ifRangeHeader.Length > 1)) { if (ifRangeHeader[0] == '"') { if (ifRangeHeader != etag) { return flag; } } else { if ((ifRangeHeader[0] == 'W') && (ifRangeHeader[1] == '/')) { return flag; } if (IsOutDated(ifRangeHeader, lastModified)) { return flag; } } } int index = rangeHeader.IndexOf('='); if ((index == -1) || (index == (rangeHeader.Length - 1))) { return flag; } int startIndex = index + 1; bool flag2 = true; bool flag4 = false; ByteRange[] rangeArray = null; int num5 = 0; long num6 = 0L; while ((startIndex < rangeHeader.Length) && flag2) { bool flag3; flag2 = GetNextRange(rangeHeader, ref startIndex, fileLength, out offset, out length, out flag3); if (!flag2) { break; } if (flag3) { if (rangeArray == null) { rangeArray = new ByteRange[0x10]; } if (num5 >= rangeArray.Length) { ByteRange[] rangeArray2 = new ByteRange[rangeArray.Length * 2]; int len = rangeArray.Length * Marshal.SizeOf(rangeArray[0]); fixed (ByteRange* rangeRef = rangeArray) { fixed (ByteRange* rangeRef2 = rangeArray2) { StringUtil.memcpyimpl((byte*) rangeRef, (byte*) rangeRef2, len); } } rangeArray = rangeArray2; } rangeArray[num5].Offset = offset; rangeArray[num5].Length = length; num5++; num6 += length; if (num6 > (fileLength * 5L)) { flag4 = true; break; } } } if (!flag2) { return flag; } if (flag4) { SendBadRequest(response); return true; } if (num5 == 0) { SendRangeNotSatisfiable(response, fileLength); return true; } string mimeMapping = MimeMapping.GetMimeMapping(physicalPath); if (num5 == 1) { offset = rangeArray[0].Offset; length = rangeArray[0].Length; response.ContentType = mimeMapping; string str3 = string.Format(CultureInfo.InvariantCulture, "bytes {0}-{1}/{2}", new object[] { offset, (offset + length) - 1L, fileLength }); response.AppendHeader("Content-Range", str3); SendFile(physicalPath, offset, length, fileLength, context); } else { response.ContentType = "multipart/byteranges; boundary=<q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl>"; string s = "Content-Type: " + mimeMapping + "\r\n"; for (int i = 0; i < num5; i++) { offset = rangeArray[i].Offset; length = rangeArray[i].Length; response.Write("--<q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl>\r\n"); response.Write(s); response.Write("Content-Range: "); string str4 = string.Format(CultureInfo.InvariantCulture, "bytes {0}-{1}/{2}", new object[] { offset, (offset + length) - 1L, fileLength }); response.Write(str4); response.Write("\r\n\r\n"); SendFile(physicalPath, offset, length, fileLength, context); response.Write("\r\n"); } response.Write("--<q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl>--\r\n\r\n"); } response.StatusCode = 0xce; response.AppendHeader("Last-Modified", HttpUtility.FormatHttpDateTime(lastModified)); response.AppendHeader("Accept-Ranges", "bytes"); response.AppendHeader("ETag", etag); response.AppendHeader("Cache-Control", "public"); return true; }
internal static unsafe bool ProcessRangeRequest(HttpContext context, string physicalPath, long fileLength, string rangeHeader, string etag, DateTime lastModified) { long offset; long length; HttpRequest request = context.Request; HttpResponse response = context.Response; bool flag = false; if (fileLength <= 0L) { SendRangeNotSatisfiable(response, fileLength); return(true); } string ifRangeHeader = request.Headers["If-Range"]; if ((ifRangeHeader != null) && (ifRangeHeader.Length > 1)) { if (ifRangeHeader[0] == '"') { if (ifRangeHeader != etag) { return(flag); } } else { if ((ifRangeHeader[0] == 'W') && (ifRangeHeader[1] == '/')) { return(flag); } if (IsOutDated(ifRangeHeader, lastModified)) { return(flag); } } } int index = rangeHeader.IndexOf('='); if ((index == -1) || (index == (rangeHeader.Length - 1))) { return(flag); } int startIndex = index + 1; bool flag2 = true; bool flag4 = false; ByteRange[] rangeArray = null; int num5 = 0; long num6 = 0L; while ((startIndex < rangeHeader.Length) && flag2) { bool flag3; flag2 = GetNextRange(rangeHeader, ref startIndex, fileLength, out offset, out length, out flag3); if (!flag2) { break; } if (flag3) { if (rangeArray == null) { rangeArray = new ByteRange[0x10]; } if (num5 >= rangeArray.Length) { ByteRange[] rangeArray2 = new ByteRange[rangeArray.Length * 2]; int len = rangeArray.Length * Marshal.SizeOf(rangeArray[0]); fixed(ByteRange *rangeRef = rangeArray) { fixed(ByteRange *rangeRef2 = rangeArray2) { StringUtil.memcpyimpl((byte *)rangeRef, (byte *)rangeRef2, len); } } rangeArray = rangeArray2; } rangeArray[num5].Offset = offset; rangeArray[num5].Length = length; num5++; num6 += length; if (num6 > (fileLength * 5L)) { flag4 = true; break; } } } if (!flag2) { return(flag); } if (flag4) { SendBadRequest(response); return(true); } if (num5 == 0) { SendRangeNotSatisfiable(response, fileLength); return(true); } string mimeMapping = MimeMapping.GetMimeMapping(physicalPath); if (num5 == 1) { offset = rangeArray[0].Offset; length = rangeArray[0].Length; response.ContentType = mimeMapping; string str3 = string.Format(CultureInfo.InvariantCulture, "bytes {0}-{1}/{2}", new object[] { offset, (offset + length) - 1L, fileLength }); response.AppendHeader("Content-Range", str3); SendFile(physicalPath, offset, length, fileLength, context); } else { response.ContentType = "multipart/byteranges; boundary=<q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl>"; string s = "Content-Type: " + mimeMapping + "\r\n"; for (int i = 0; i < num5; i++) { offset = rangeArray[i].Offset; length = rangeArray[i].Length; response.Write("--<q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl>\r\n"); response.Write(s); response.Write("Content-Range: "); string str4 = string.Format(CultureInfo.InvariantCulture, "bytes {0}-{1}/{2}", new object[] { offset, (offset + length) - 1L, fileLength }); response.Write(str4); response.Write("\r\n\r\n"); SendFile(physicalPath, offset, length, fileLength, context); response.Write("\r\n"); } response.Write("--<q1w2e3r4t5y6u7i8o9p0zaxscdvfbgnhmjklkl>--\r\n\r\n"); } response.StatusCode = 0xce; response.AppendHeader("Last-Modified", HttpUtility.FormatHttpDateTime(lastModified)); response.AppendHeader("Accept-Ranges", "bytes"); response.AppendHeader("ETag", etag); response.AppendHeader("Cache-Control", "public"); return(true); }
private static string WriteMultipartDetails(ByteRange range, string contentType, long fileLength) { StringBuilder sb = new StringBuilder(); sb.AppendLine(string.Format("--{0}", RANGE_BOUNDARY)); sb.AppendLine(string.Format("Content-Type: {0}", contentType)); sb.AppendLine(string.Format("Content-Range: {0}", string.Format(CultureInfo.InvariantCulture, "bytes {0}-{1}/{2}", range.Offset, range.Length, fileLength))); sb.AppendLine(); return sb.ToString(); }