private static IEnumerable <Tuple <long, long> > CreateByteRanges(string rangeString, int size) { return(RangeHeader.Parse(new Dictionary <string, string[]>(StringComparer.OrdinalIgnoreCase) { { "Range", new[] { rangeString } } }, size)); }
public async Task GetWithUnsatisfiableRangeTest() { var ct = CancellationToken.None; var root = await FileSystem.Root; var testFile = await root.CreateDocumentAsync("test1.txt", ct); await FillAsync(testFile, int.MaxValue, ct); using (var client = Server.CreateClient()) { var range = new RangeHeader("bytes", new RangeHeaderItem(_testBlock.Value.Length - 1, _testBlock.Value.Length)); var request = new HttpRequestMessage(HttpMethod.Get, "test1.txt") { Headers = { Range = RangeHeaderValue.Parse(range.ToString()), }, }; using (var response = await client.SendAsync(request, ct)) { Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode); } } }
/// <summary> /// Create an instance of RangeHeader using RangeHeaderValues /// </summary> /// <param name="values">A collection of RangeHeaderValues that represent upper and lower byte ranges</param> /// <returns></returns> public static RangeHeader Create(RangeHeaderValue value) { var toReturn = new RangeHeader(); toReturn.RawRange = value; return(toReturn); }
public void CheckHeaderRangePopulatedCorrectly() { var headers = new ServerResponseHeaders(); headers.SetRange(RangeHeader.Create(new RangeHeaderValue(0, 50))); Assert.AreEqual("bytes 0-50/50", headers.Range); headers.SetRange(RangeHeader.Create(new RangeHeaderValue())); Assert.AreEqual("bytes 0-0/0", headers.Range); headers = new ServerResponseHeaders(); Assert.AreEqual(null, headers.Range); }
public void RangeHeader_Reports_Syntactically_Invalid_Byte_Ranges() { Assert.IsFalse(RangeHeader.IsValid(CreateHeadersWithRange(""))); Assert.IsFalse(RangeHeader.IsValid(CreateHeadersWithRange("foobar"))); Assert.IsFalse(RangeHeader.IsValid(CreateHeadersWithRange("furlongs=123-456"))); Assert.IsFalse(RangeHeader.IsValid(CreateHeadersWithRange("bytes="))); Assert.IsFalse(RangeHeader.IsValid(CreateHeadersWithRange(""))); // A range of non-positive length is syntactically invalid and ignored: Assert.IsNull(CreateByteRanges("bytes=123,456", 500)); Assert.IsNull(CreateByteRanges("bytes=456-123", 500)); Assert.IsNull(CreateByteRanges("bytes=456-455", 500)); }
public void OverlapDescendingTest() { var range = RangeHeader.Parse("bytes=300-599,0-499"); var rangeItems = range.Normalize(10000); Assert.Collection( rangeItems, rangeItem => { Assert.Equal(0, rangeItem.From); Assert.Equal(599, rangeItem.To); }); }
public void FromToTest() { var range = RangeHeader.Parse("bytes=0-499"); var rangeItems = range.Normalize(10000); Assert.Collection( rangeItems, rangeItem => { Assert.Equal(0, rangeItem.From); Assert.Equal(499, rangeItem.To); }); }
public void OverlapWithFromTest() { var range = RangeHeader.Parse("bytes=4000-5999,5000-"); var rangeItems = range.Normalize(10000); Assert.Collection( rangeItems, rangeItem => { Assert.Equal(4000, rangeItem.From); Assert.Equal(9999, rangeItem.To); }); }
public async Task OnRequest(HttpContext context) { var request = context.Request; var requestPath = request.Path; if (string.Equals(requestPath, "/favicon.ico", StringComparison.OrdinalIgnoreCase)) { context.Response.StatusCode = 200; await context.Response.Body.WriteAsync(Resources.Favicon); return; } var decodedPath = HttpUtility.UrlDecode(requestPath); var physicalPath = _webHostEnvironment.ContentRootFileProvider.GetFileInfo(decodedPath).PhysicalPath; if (Directory.Exists(physicalPath) && request.Method == "GET" || request.Method == "HEAD") { var url = $"{request.Scheme}://{request.Host}{requestPath}"; var uri = new Uri(url); var isRootPath = string.Equals(requestPath, "/") || true; var mainPayload = _tinfoilIndexBuilder.Build(physicalPath, uri, _appSettings.IndexType, isRootPath ? _appSettings.MessageOfTheDay : null); var json = JsonSerializer.Serialize(mainPayload, new JsonSerializerOptions { WriteIndented = true }); context.Response.StatusCode = 200; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(json, Encoding.UTF8); } else if (_fileFilter.IsFileAllowed(physicalPath) && File.Exists(physicalPath) && request.Method == "GET" || request.Method == "HEAD") { var rangeHeader = new RangeHeader { RawValue = request.Headers["range"] }; var ranges = rangeHeader.Ranges; var range = ranges.Count == 1 ? ranges[0] : null; await context.Response.WriteFile(physicalPath, range : range); } else { context.Response.StatusCode = 404; await context.Response.WriteAsync("<!DOCTYPE html><http><head><title>Oops!</title></head><body style='text-align:center'>404<br>Not found...</body></html>"); } }
private Task Serve(IDictionary <string, object> env) { Request request = new Request(env); Response response = new Response(env); var fileInfo = new FileInfo(path); var size = fileInfo.Length; if (!RangeHeader.IsValid(request.Headers)) { response.StatusCode = OK; range = new Tuple <long, long>(0, size - 1); } else { var ranges = RangeHeader.Parse(request.Headers, size); if (ranges == null) { // Unsatisfiable. Return error and file size. return(Fail( RequestedRangeNotSatisfiable, "Byte range unsatisfiable", "Content-Range", "bytes */" + size) .Invoke(env)); } if (ranges.Count() > 1) { // TODO: Support multiple byte ranges. response.StatusCode = OK; range = new Tuple <long, long>(0, size - 1); } else { // Partial content range = ranges.First(); response.StatusCode = PartialContent; response.Headers.SetHeader("Content-Range", "bytes " + range.Item1 + "-" + range.Item2 + "/" + size); size = range.Item2 - range.Item1 + 1; } } response.Headers .SetHeader("Last-Modified", fileInfo.LastWriteTimeUtc.ToHttpDateString()) .SetHeader("Content-Type", Mime.MimeType(fileInfo.Extension, "text/plain")) .SetHeader("Content-Length", size.ToString(CultureInfo.InvariantCulture)); return(new FileBody(path, range).Start(response.OutputStream)); }
public void Must_parse_correctly(object[] parameters) { var headerValue = (string)parameters[0]; var byteRanges = new List <Tuple <ulong?, ulong?> >(); for (int i = 1; i < parameters.Length; i += 2) { byteRanges.Add(new Tuple <ulong?, ulong?>((ulong?)parameters[i], (ulong?)parameters[i + 1])); } IEnumerable <RangeHeader> headers = RangeHeader.ParseMany(headerValue); Assert.That(headers.Select(arg => new Tuple <ulong?, ulong?>(arg.FirstBytePos, arg.LastBytePos)), Is.EquivalentTo(byteRanges)); }
public async Task GetWithTwoRangesTest() { var ct = CancellationToken.None; var root = await FileSystem.Root; var testFile = await root.CreateDocumentAsync("test1.txt", ct); await FillAsync(testFile, int.MaxValue, ct); using (var client = Server.CreateClient()) { var range = new RangeHeader("bytes", new RangeHeaderItem(0, 1), new RangeHeaderItem(3, 4)); var request = new HttpRequestMessage(HttpMethod.Get, "test1.txt") { Headers = { Range = RangeHeaderValue.Parse(range.ToString()), }, }; using (var response = await client.SendAsync(request, ct)) { var content = response .EnsureSuccessStatusCode().Content; Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode); var multipart = await ReadMultipartAsync(content, ct); Assert.Equal(2, multipart.Count); Assert.All(multipart, entity => Assert.True(entity.ContentType.IsMimeType("text", "plain"))); Assert.Collection( multipart, entity => { var textPart = Assert.IsType <TextPart>(entity); Assert.Equal($"bytes 0-1/{_testBlock.Value.Length}", textPart.Headers["Content-Range"]); Assert.Equal("12", textPart.Text); }, entity => { var textPart = Assert.IsType <TextPart>(entity); Assert.Equal($"bytes 3-4/{_testBlock.Value.Length}", textPart.Headers["Content-Range"]); Assert.Equal("45", textPart.Text); }); } } }
public async Task <HttpResponseMessage> Stream(int id) { Media media = await _mediaService.Get(id); HttpResponseMessage response = new HttpResponseMessage(); //Check if media exists if (media == null) { response.StatusCode = HttpStatusCode.NotFound; return(response); } FileInfo fileInfo = new FileInfo(media.FileLocation); if (!FileHelper.FileExists(fileInfo)) { response.StatusCode = HttpStatusCode.NotFound; return(response); } string rangeHeaderString = Request.Headers["range"]; response.Headers.AcceptRanges.Add("bytes"); // The request will be treated as normal request if there is no Range header. if (rangeHeaderString == null) { CreateNoRangeResponse(response, fileInfo); return(response); } RangeHeader rangeHeader = StreamHelper.ParseRangeHeader(rangeHeaderString); //If the range is not satisfiable, return a RequestedRangeNotSatisfiable response if (rangeHeader.Unit != "bytes" || /*rangeHeader.Ranges.Count > 1 ||*/ !FileHelper.TryReadRangeItem(rangeHeader.Value, fileInfo.Length, out long start, out long end)) { CreateRangeNotSatisfiableResponse(response, fileInfo); return(response); } //The range header is fine, return partial content CreatePartialContentResponse(response, fileInfo, start, end); return(response); }
public async Task GetWithSingleRangeTest() { var ct = CancellationToken.None; var root = await FileSystem.Root; var testFile = await root.CreateDocumentAsync("test1.txt", ct); await FillAsync(testFile, int.MaxValue, ct); using (var client = Server.CreateClient()) { var range = new RangeHeader("bytes", new RangeHeaderItem(0, 1)); var request = new HttpRequestMessage(HttpMethod.Get, "test1.txt") { Headers = { Range = RangeHeaderValue.Parse(range.ToString()), }, }; using (var response = await client.SendAsync(request, ct)) { var content = response .EnsureSuccessStatusCode().Content; Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode); Assert.Equal(2, content.Headers.ContentLength); var contentRange = content.Headers.ContentRange; Assert.NotNull(contentRange); Assert.Equal(0, contentRange.From); Assert.Equal(1, contentRange.To); Assert.Equal(_testBlock.Value.Length, contentRange.Length); var data = await content .ReadAsByteArrayAsync(); var s = Encoding.UTF8.GetString(data); Assert.Equal("12", s); } } }
public void SetRange(RangeHeader header) { Range = header.Range; }
private Task Serve(IDictionary <string, object> env, string path) { Request request = new Request(env); Response response = new Response(env); var fileInfo = new FileInfo(path); var size = fileInfo.Length; Tuple <long, long> range; if (!RangeHeader.IsValid(request.Headers)) { response.StatusCode = OK; range = new Tuple <long, long>(0, size - 1); } else { var ranges = RangeHeader.Parse(request.Headers, size); if (ranges == null) { // Unsatisfiable. Return error and file size. return(Fail( RequestedRangeNotSatisfiable, "Byte range unsatisfiable", "Content-Range", "bytes */" + size) .Invoke(env)); } if (ranges.Count() > 1) { // TODO: Support multiple byte ranges. response.StatusCode = OK; range = new Tuple <long, long>(0, size - 1); } else { // Partial content range = ranges.First(); response.StatusCode = PartialContent; response.Headers.SetHeader("Content-Range", "bytes " + range.Item1 + "-" + range.Item2 + "/" + size); size = range.Item2 - range.Item1 + 1; } } response.Headers .SetHeader("Last-Modified", fileInfo.LastWriteTimeUtc.ToHttpDateString()) .SetHeader("Content-Type", Mime.MimeType(fileInfo.Extension, "text/plain")) .SetHeader("Content-Length", size.ToString(CultureInfo.InvariantCulture)); if ("HEAD".Equals(request.Method, StringComparison.OrdinalIgnoreCase)) { // Suppress the body. return(TaskHelpers.Completed()); } FileBody body = new FileBody(path, range); //TODO: update for current send file spec //var req = new Request(env); //SendFileFunc sendFile = env.Get<SendFileFunc>("sendfile.Func"); //if (sendFile != null) //{ // return body.Start(sendFile); //} return(body.Start(response.OutputStream)); }
public RangeHeaderTest() { _rangeHeader = new RangeHeader(); }
public void Must_not_result_in_header(string headerValue) { Assert.That(RangeHeader.ParseMany(headerValue), Is.Empty); }