Respond to RangeHelper refactor (#6348)
Respond to https://github.com/aspnet/StaticFiles/pull/200
This commit is contained in:
parent
f1dd475eae
commit
7ffd88757d
|
|
@ -31,8 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Unspecified,
|
||||
NotModified,
|
||||
ShouldProcess,
|
||||
PreconditionFailed,
|
||||
IgnoreRangeRequest
|
||||
PreconditionFailed
|
||||
}
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
|
@ -61,15 +60,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var httpRequestHeaders = request.GetTypedHeaders();
|
||||
var response = context.HttpContext.Response;
|
||||
var httpResponseHeaders = response.GetTypedHeaders();
|
||||
if (fileLength.HasValue)
|
||||
{
|
||||
SetAcceptRangeHeader(context);
|
||||
// Assuming the request is not a range request, the Content-Length header is set to the length of the entire file.
|
||||
// If the request is a valid range request, this header is overwritten with the length of the range as part of the
|
||||
// range processing (see method SetContentLength).
|
||||
response.ContentLength = fileLength.Value;
|
||||
}
|
||||
|
||||
if (lastModified.HasValue)
|
||||
{
|
||||
httpResponseHeaders.LastModified = lastModified;
|
||||
|
|
@ -80,24 +70,35 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
var serveBody = !HttpMethods.IsHead(request.Method);
|
||||
if (HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
|
||||
var preconditionState = GetPreconditionState(context, httpRequestHeaders, lastModified, etag);
|
||||
if (preconditionState == PreconditionState.NotModified)
|
||||
{
|
||||
var preconditionState = GetPreconditionState(context, httpRequestHeaders, lastModified, etag);
|
||||
if (request.Headers.ContainsKey(HeaderNames.Range) &&
|
||||
(preconditionState == PreconditionState.Unspecified ||
|
||||
preconditionState == PreconditionState.ShouldProcess))
|
||||
serveBody = false;
|
||||
response.StatusCode = StatusCodes.Status304NotModified;
|
||||
}
|
||||
else if (preconditionState == PreconditionState.PreconditionFailed)
|
||||
{
|
||||
serveBody = false;
|
||||
response.StatusCode = StatusCodes.Status412PreconditionFailed;
|
||||
}
|
||||
|
||||
if (fileLength.HasValue)
|
||||
{
|
||||
SetAcceptRangeHeader(context);
|
||||
// Assuming the request is not a range request, the Content-Length header is set to the length of the entire file.
|
||||
// If the request is a valid range request, this header is overwritten with the length of the range as part of the
|
||||
// range processing (see method SetContentLength).
|
||||
response.ContentLength = fileLength.Value;
|
||||
if (HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
|
||||
{
|
||||
return SetRangeHeaders(context, httpRequestHeaders, fileLength, lastModified, etag);
|
||||
}
|
||||
if (preconditionState == PreconditionState.NotModified)
|
||||
{
|
||||
serveBody = false;
|
||||
response.StatusCode = StatusCodes.Status304NotModified;
|
||||
}
|
||||
else if (preconditionState == PreconditionState.PreconditionFailed)
|
||||
{
|
||||
serveBody = false;
|
||||
response.StatusCode = StatusCodes.Status412PreconditionFailed;
|
||||
if ((preconditionState == PreconditionState.Unspecified ||
|
||||
preconditionState == PreconditionState.ShouldProcess))
|
||||
{
|
||||
if (IfRangeValid(context, httpRequestHeaders, lastModified, etag))
|
||||
{
|
||||
return SetRangeHeaders(context, httpRequestHeaders, fileLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,6 +156,37 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return PreconditionState.Unspecified;
|
||||
}
|
||||
|
||||
internal static bool IfRangeValid(
|
||||
ActionContext context,
|
||||
RequestHeaders httpRequestHeaders,
|
||||
DateTimeOffset? lastModified = null,
|
||||
EntityTagHeaderValue etag = null)
|
||||
{
|
||||
// 14.27 If-Range
|
||||
var ifRange = httpRequestHeaders.IfRange;
|
||||
if (ifRange != null)
|
||||
{
|
||||
// If the validator given in the If-Range header field matches the
|
||||
// current validator for the selected representation of the target
|
||||
// resource, then the server SHOULD process the Range header field as
|
||||
// requested. If the validator does not match, the server MUST ignore
|
||||
// the Range header field.
|
||||
if (ifRange.LastModified.HasValue)
|
||||
{
|
||||
if (lastModified.HasValue && lastModified > ifRange.LastModified)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (etag != null && ifRange.EntityTag != null && !ifRange.EntityTag.Compare(etag, useStrongComparison: true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static PreconditionState GetPreconditionState(
|
||||
ActionContext context,
|
||||
|
|
@ -166,7 +198,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var ifNoneMatchState = PreconditionState.Unspecified;
|
||||
var ifModifiedSinceState = PreconditionState.Unspecified;
|
||||
var ifUnmodifiedSinceState = PreconditionState.Unspecified;
|
||||
var ifRangeState = PreconditionState.Unspecified;
|
||||
|
||||
// 14.24 If-Match
|
||||
var ifMatch = httpRequestHeaders.IfMatch;
|
||||
|
|
@ -208,28 +239,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
ifUnmodifiedSinceState = unmodified ? PreconditionState.ShouldProcess : PreconditionState.PreconditionFailed;
|
||||
}
|
||||
|
||||
var ifRange = httpRequestHeaders.IfRange;
|
||||
if (ifRange != null)
|
||||
{
|
||||
// If the validator given in the If-Range header field matches the
|
||||
// current validator for the selected representation of the target
|
||||
// resource, then the server SHOULD process the Range header field as
|
||||
// requested. If the validator does not match, the server MUST ignore
|
||||
// the Range header field.
|
||||
if (ifRange.LastModified.HasValue)
|
||||
{
|
||||
if (lastModified.HasValue && lastModified > ifRange.LastModified)
|
||||
{
|
||||
ifRangeState = PreconditionState.IgnoreRangeRequest;
|
||||
}
|
||||
}
|
||||
else if (etag != null && ifRange.EntityTag != null && !ifRange.EntityTag.Compare(etag, useStrongComparison: true))
|
||||
{
|
||||
ifRangeState = PreconditionState.IgnoreRangeRequest;
|
||||
}
|
||||
}
|
||||
|
||||
var state = GetMaxPreconditionState(ifMatchState, ifNoneMatchState, ifModifiedSinceState, ifUnmodifiedSinceState, ifRangeState);
|
||||
var state = GetMaxPreconditionState(ifMatchState, ifNoneMatchState, ifModifiedSinceState, ifUnmodifiedSinceState);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
|
@ -250,16 +260,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
private static (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetRangeHeaders(
|
||||
ActionContext context,
|
||||
RequestHeaders httpRequestHeaders,
|
||||
long? fileLength,
|
||||
DateTimeOffset? lastModified = null,
|
||||
EntityTagHeaderValue etag = null)
|
||||
long? fileLength)
|
||||
{
|
||||
var response = context.HttpContext.Response;
|
||||
var httpResponseHeaders = response.GetTypedHeaders();
|
||||
|
||||
// Checked for presence of Range header explicitly before calling this method.
|
||||
// Range may be null for parsing errors, multiple ranges and when the file length is missing.
|
||||
var range = fileLength.HasValue ? ParseRange(context, httpRequestHeaders, fileLength.Value, lastModified, etag) : null;
|
||||
// Range may be null for empty range header, invalid ranges, parsing errors, multiple ranges
|
||||
// and when the file length is zero.
|
||||
var (isRangeRequest, range) = RangeHelper.ParseRange(
|
||||
context.HttpContext,
|
||||
httpRequestHeaders,
|
||||
fileLength.Value);
|
||||
|
||||
if (!isRangeRequest)
|
||||
{
|
||||
return (range: null, rangeLength: 0, serveBody: true);
|
||||
}
|
||||
|
||||
if (range == null)
|
||||
{
|
||||
// 14.16 Content-Range - A server sending a response with status code 416 (Requested range not satisfiable)
|
||||
|
|
@ -295,32 +312,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return length;
|
||||
}
|
||||
|
||||
private static RangeItemHeaderValue ParseRange(
|
||||
ActionContext context,
|
||||
RequestHeaders httpRequestHeaders,
|
||||
long fileLength,
|
||||
DateTimeOffset? lastModified = null,
|
||||
EntityTagHeaderValue etag = null)
|
||||
{
|
||||
var httpContext = context.HttpContext;
|
||||
var response = httpContext.Response;
|
||||
|
||||
var range = RangeHelper.ParseRange(httpContext, httpRequestHeaders, lastModified, etag);
|
||||
|
||||
if (range != null)
|
||||
{
|
||||
var normalizedRanges = RangeHelper.NormalizeRanges(range, fileLength);
|
||||
if (normalizedRanges == null || normalizedRanges.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return normalizedRanges.Single();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static ILogger CreateLogger<T>(ILoggerFactory factory)
|
||||
{
|
||||
if (factory == null)
|
||||
|
|
|
|||
|
|
@ -233,8 +233,47 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-5")]
|
||||
[InlineData("bytes = 11-0")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var contentType = "text/plain";
|
||||
var lastModified = new DateTimeOffset();
|
||||
var entityTag = new EntityTagHeaderValue("\"Etag\"");
|
||||
var byteArray = Encoding.ASCII.GetBytes("Hello World");
|
||||
|
||||
var result = new FileContentResult(byteArray, contentType)
|
||||
{
|
||||
LastModified = lastModified,
|
||||
EntityTag = entityTag
|
||||
};
|
||||
|
||||
var httpContext = GetHttpContext();
|
||||
httpContext.Request.Headers[HeaderNames.Range] = rangeString;
|
||||
httpContext.Request.Method = HttpMethods.Get;
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
|
||||
// Act
|
||||
await result.ExecuteResultAsync(actionContext);
|
||||
|
||||
// Assert
|
||||
var httpResponse = actionContext.HttpContext.Response;
|
||||
httpResponse.Body.Seek(0, SeekOrigin.Begin);
|
||||
var streamReader = new StreamReader(httpResponse.Body);
|
||||
var body = streamReader.ReadToEndAsync().Result;
|
||||
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
||||
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
|
||||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
||||
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
||||
Assert.Equal("Hello World", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 12-13")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestedNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
|
|
|
|||
|
|
@ -378,8 +378,10 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Assert.Equal(FileResultExecutorBase.PreconditionState.NotModified, state);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPreconditionState_ShouldNotProcess_IgnoreRangeRequest()
|
||||
[Theory]
|
||||
[InlineData("\"NotEtag\"", false)]
|
||||
[InlineData("\"Etag\"", true)]
|
||||
public void IfRangeValid_IgnoreRangeRequest(string ifRangeString, bool expected)
|
||||
{
|
||||
// Arrange
|
||||
var actionContext = new ActionContext();
|
||||
|
|
@ -389,19 +391,19 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var lastModified = DateTimeOffset.MinValue;
|
||||
lastModified = new DateTimeOffset(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, TimeSpan.FromSeconds(0));
|
||||
var etag = new EntityTagHeaderValue("\"Etag\"");
|
||||
httpRequestHeaders.IfRange = new RangeConditionHeaderValue("\"NotEtag\"");
|
||||
httpRequestHeaders.IfRange = new RangeConditionHeaderValue(ifRangeString);
|
||||
httpRequestHeaders.IfModifiedSince = lastModified;
|
||||
actionContext.HttpContext = httpContext;
|
||||
|
||||
// Act
|
||||
var state = FileResultExecutorBase.GetPreconditionState(
|
||||
var ifRangeIsValid = FileResultExecutorBase.IfRangeValid(
|
||||
actionContext,
|
||||
httpRequestHeaders,
|
||||
lastModified,
|
||||
etag);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(FileResultExecutorBase.PreconditionState.IgnoreRangeRequest, state);
|
||||
Assert.Equal(expected, ifRangeIsValid);
|
||||
}
|
||||
|
||||
private static IServiceCollection CreateServices()
|
||||
|
|
|
|||
|
|
@ -219,9 +219,9 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-5")]
|
||||
[InlineData("bytes = 11-0")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestedNotSatisfiable(string rangeString)
|
||||
public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var contentType = "text/plain";
|
||||
|
|
@ -246,6 +246,46 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
// Act
|
||||
await result.ExecuteResultAsync(actionContext);
|
||||
|
||||
// Assert
|
||||
var httpResponse = actionContext.HttpContext.Response;
|
||||
httpResponse.Body.Seek(0, SeekOrigin.Begin);
|
||||
var streamReader = new StreamReader(httpResponse.Body);
|
||||
var body = streamReader.ReadToEndAsync().Result;
|
||||
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
||||
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
|
||||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
|
||||
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
||||
Assert.Equal("Hello World", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 12-13")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestedNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var contentType = "text/plain";
|
||||
var lastModified = new DateTimeOffset();
|
||||
var entityTag = new EntityTagHeaderValue("\"Etag\"");
|
||||
var byteArray = Encoding.ASCII.GetBytes("Hello World");
|
||||
var readStream = new MemoryStream(byteArray);
|
||||
|
||||
var result = new FileStreamResult(readStream, contentType)
|
||||
{
|
||||
LastModified = lastModified,
|
||||
EntityTag = entityTag,
|
||||
};
|
||||
|
||||
var httpContext = GetHttpContext();
|
||||
httpContext.Request.Headers[HeaderNames.Range] = rangeString;
|
||||
httpContext.Request.Method = HttpMethods.Get;
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
|
||||
// Act
|
||||
await result.ExecuteResultAsync(actionContext);
|
||||
|
||||
// Assert
|
||||
var httpResponse = actionContext.HttpContext.Response;
|
||||
httpResponse.Body.Seek(0, SeekOrigin.Begin);
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||
var contentRange = new ContentRangeHeaderValue(0, 3, 34);
|
||||
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
|
||||
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
||||
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
|
||||
Assert.Equal(4, httpResponse.ContentLength);
|
||||
Assert.Equal("File", body);
|
||||
|
|
@ -148,14 +149,46 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var body = streamReader.ReadToEndAsync().Result;
|
||||
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
|
||||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
||||
Assert.Equal("FilePathResultTestFile contents<74>", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("0-5")]
|
||||
[InlineData("bytes = 11-0")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task WriteFileAsync_RangeRequested_NotSatisfiable(string rangeString)
|
||||
public async Task WriteFileAsync_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
|
||||
var result = new TestPhysicalFileResult(path, "text/plain");
|
||||
var httpContext = GetHttpContext();
|
||||
var requestHeaders = httpContext.Request.GetTypedHeaders();
|
||||
requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
|
||||
httpContext.Request.Headers[HeaderNames.Range] = rangeString;
|
||||
httpContext.Request.Method = HttpMethods.Get;
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
|
||||
// Act
|
||||
await result.ExecuteResultAsync(actionContext);
|
||||
|
||||
// Assert
|
||||
var httpResponse = actionContext.HttpContext.Response;
|
||||
httpResponse.Body.Seek(0, SeekOrigin.Begin);
|
||||
var streamReader = new StreamReader(httpResponse.Body);
|
||||
var body = streamReader.ReadToEndAsync().Result;
|
||||
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
|
||||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
||||
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
||||
Assert.Equal("FilePathResultTestFile contents<74>", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 35-36")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task WriteFileAsync_RangeRequestedNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
|
||||
|
|
|
|||
|
|
@ -191,9 +191,9 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-5")]
|
||||
[InlineData("bytes = 11-0")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task WriteFileAsync_RangeRequested_NotSatisfiable(string rangeString)
|
||||
public async Task WriteFileAsync_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var path = Path.GetFullPath("helllo.txt");
|
||||
|
|
@ -201,15 +201,58 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var result = new TestVirtualFileResult(path, contentType);
|
||||
var appEnvironment = new Mock<IHostingEnvironment>();
|
||||
appEnvironment.Setup(app => app.WebRootFileProvider)
|
||||
.Returns(GetFileProvider(path));
|
||||
.Returns(GetFileProvider(path));
|
||||
|
||||
var httpContext = GetHttpContext();
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
httpContext.RequestServices = new ServiceCollection()
|
||||
.AddSingleton(appEnvironment.Object)
|
||||
.AddTransient<TestVirtualFileResultExecutor>()
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>()
|
||||
.BuildServiceProvider();
|
||||
.AddSingleton(appEnvironment.Object)
|
||||
.AddTransient<TestVirtualFileResultExecutor>()
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
var requestHeaders = httpContext.Request.GetTypedHeaders();
|
||||
httpContext.Request.Headers[HeaderNames.Range] = rangeString;
|
||||
requestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue.AddDays(1);
|
||||
httpContext.Request.Method = HttpMethods.Get;
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
|
||||
// Act
|
||||
await result.ExecuteResultAsync(actionContext);
|
||||
|
||||
// Assert
|
||||
var httpResponse = actionContext.HttpContext.Response;
|
||||
httpResponse.Body.Seek(0, SeekOrigin.Begin);
|
||||
var streamReader = new StreamReader(httpResponse.Body);
|
||||
var body = streamReader.ReadToEndAsync().Result;
|
||||
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
|
||||
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
|
||||
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
|
||||
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
|
||||
Assert.Equal("FilePathResultTestFile contents¡", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 35-36")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task WriteFileAsync_RangeRequestedNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var path = Path.GetFullPath("helllo.txt");
|
||||
var contentType = "text/plain; charset=us-ascii; p1=p1-value";
|
||||
var result = new TestVirtualFileResult(path, contentType);
|
||||
var appEnvironment = new Mock<IHostingEnvironment>();
|
||||
appEnvironment.Setup(app => app.WebRootFileProvider)
|
||||
.Returns(GetFileProvider(path));
|
||||
|
||||
var httpContext = GetHttpContext();
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
httpContext.RequestServices = new ServiceCollection()
|
||||
.AddSingleton(appEnvironment.Object)
|
||||
.AddTransient<TestVirtualFileResultExecutor>()
|
||||
.AddTransient<ILoggerFactory, LoggerFactory>()
|
||||
.BuildServiceProvider();
|
||||
|
||||
var requestHeaders = httpContext.Request.GetTypedHeaders();
|
||||
httpContext.Request.Headers[HeaderNames.Range] = rangeString;
|
||||
|
|
|
|||
|
|
@ -64,8 +64,28 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-6")]
|
||||
[InlineData("bytes = 11-6")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task FileFromDisk_CanBeEnabled_WithMiddleware_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromDisk");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content.Headers.ContentType);
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal("This is a sample text file", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 35-36")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task FileFromDisk_CanBeEnabled_WithMiddleware_RangeRequestNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -107,8 +127,28 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-6")]
|
||||
[InlineData("bytes = 11-6")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task FileFromDisk_CanBeEnabled_WithMiddleware_RangeRequestIgnored_WithLastModifiedAndEtag(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromDiskWithFileName_WithLastModifiedAndEtag");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content.Headers.ContentType);
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal("This is a sample text file", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 35-36")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task FileFromDisk_CanBeEnabled_WithMiddleware_RangeRequestNotSatisfiable_WithLastModifiedAndEtag(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -229,8 +269,28 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-6")]
|
||||
[InlineData("bytes = 11-6")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task FileFromStream_ReturnsFile_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromStream");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content.Headers.ContentType);
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal("This is sample text from a stream", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 35-36")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task FileFromStream_ReturnsFile_RangeRequestNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -349,8 +409,28 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-6")]
|
||||
[InlineData("bytes = 11-6")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task FileFromBinaryData_ReturnsFile_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromBinaryData");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content.Headers.ContentType);
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal("This is a sample text from a binary array", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 45-46")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task FileFromBinaryData_ReturnsFile_RangeRequestNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -524,8 +604,31 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
[Theory]
|
||||
[InlineData("0-6")]
|
||||
[InlineData("bytes = 11-6")]
|
||||
[InlineData("bytes = ")]
|
||||
[InlineData("bytes = 1-4, 5-11")]
|
||||
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeRequestIgnored(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EmbeddedFiles/DownloadFileWithFileName");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.NotNull(response.Content.Headers.ContentType);
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal("Sample text file as embedded resource.", body);
|
||||
var contentDisposition = response.Content.Headers.ContentDisposition.ToString();
|
||||
Assert.NotNull(contentDisposition);
|
||||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("bytes = 45-46")]
|
||||
[InlineData("bytes = -0")]
|
||||
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeRequestNotSatisfiable(string rangeString)
|
||||
{
|
||||
// Arrange
|
||||
|
|
|
|||
Loading…
Reference in New Issue