diff --git a/src/Microsoft.AspNetCore.Mvc.Core/SerializableError.cs b/src/Microsoft.AspNetCore.Mvc.Core/SerializableError.cs
index 25e0f28c1a..77adaeae1e 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/SerializableError.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/SerializableError.cs
@@ -26,7 +26,7 @@ public SerializableError()
///
/// Creates a new instance of .
///
- /// containing the validation errors.
+ /// containing the validation errors.
public SerializableError(ModelStateDictionary modelState)
: this()
{
@@ -57,4 +57,4 @@ public SerializableError(ModelStateDictionary modelState)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileContentResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileContentResultTest.cs
index 235f581d21..d7e12895a3 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileContentResultTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileContentResultTest.cs
@@ -144,11 +144,100 @@ public async Task WriteFileAsync_PreconditionStateShouldProcess_WritesRangeReque
Assert.Equal(expectedString, body);
}
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
+ {
+ // Arrange
+ var contentType = "text/plain";
+ var lastModified = DateTimeOffset.MinValue;
+ 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();
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfMatch = new[]
+ {
+ new EntityTagHeaderValue("\"Etag\""),
+ };
+ requestHeaders.Range = new RangeHeaderValue(0, 4);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(DateTimeOffset.MinValue);
+ 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.Status206PartialContent, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Equal(5, httpResponse.ContentLength);
+ Assert.Equal("Hello", body);
+ }
+
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedNotSatisfiable()
+ {
+ // Arrange
+ var contentType = "text/plain";
+ var lastModified = DateTimeOffset.MinValue.AddDays(1);
+ 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();
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfMatch = new[]
+ {
+ new EntityTagHeaderValue("\"Etag\""),
+ };
+ requestHeaders.Range = new RangeHeaderValue(0, 4);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+ 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;
+ var contentRange = new ContentRangeHeaderValue(byteArray.Length);
+ Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Empty(body);
+ }
+
[Theory]
[InlineData("0-5")]
[InlineData("bytes = 11-0")]
[InlineData("bytes = 1-4, 5-11")]
- public async Task WriteFileAsync__PreconditionStateUnspecified_RangeRequestedNotSatisfiable(string rangeString)
+ public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestedNotSatisfiable(string rangeString)
{
// Arrange
var contentType = "text/plain";
@@ -163,7 +252,6 @@ public async Task WriteFileAsync__PreconditionStateUnspecified_RangeRequestedNot
};
var httpContext = GetHttpContext();
- var requestHeaders = httpContext.Request.GetTypedHeaders();
httpContext.Request.Headers[HeaderNames.Range] = rangeString;
httpContext.Request.Method = HttpMethods.Get;
httpContext.Response.Body = new MemoryStream();
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs
index 0d09067b16..bc06c0fd4d 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs
@@ -270,7 +270,7 @@ public async Task SetsAcceptRangeHeader()
[InlineData("\"Etag\"", "\"NotEtag\"")]
[InlineData("\"Etag\"", null)]
[InlineData(null, "\"NotEtag\"")]
- public void ComputeConditionalHeaders_ShouldProcess(string ifMatch, string ifNoneMatch)
+ public void GetPreconditionState_ShouldProcess(string ifMatch, string ifNoneMatch)
{
var actionContext = new ActionContext();
var httpContext = new DefaultHttpContext();
@@ -293,21 +293,21 @@ public void ComputeConditionalHeaders_ShouldProcess(string ifMatch, string ifNon
httpRequestHeaders.IfUnmodifiedSince = lastModified;
httpRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(1);
actionContext.HttpContext = httpContext;
- var shouldProcess = FileResultExecutorBase.GetPreconditionState(
+ var state = FileResultExecutorBase.GetPreconditionState(
actionContext,
httpRequestHeaders,
lastModified,
etag);
// Assert
- Assert.Equal(FileResultExecutorBase.PreconditionState.ShouldProcess, shouldProcess);
+ Assert.Equal(FileResultExecutorBase.PreconditionState.ShouldProcess, state);
}
[Theory]
[InlineData("\"NotEtag\"", null)]
[InlineData("\"Etag\"", "\"Etag\"")]
[InlineData(null, null)]
- public void ComputeConditionalHeaders_ShouldNotProcess_PreconditionFailed(string ifMatch, string ifNoneMatch)
+ public void GetPreconditionState_ShouldNotProcess_PreconditionFailed(string ifMatch, string ifNoneMatch)
{
var actionContext = new ActionContext();
var httpContext = new DefaultHttpContext();
@@ -329,20 +329,20 @@ public void ComputeConditionalHeaders_ShouldNotProcess_PreconditionFailed(string
httpRequestHeaders.IfUnmodifiedSince = DateTimeOffset.MinValue;
httpRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue.AddDays(2);
actionContext.HttpContext = httpContext;
- var shouldProcess = FileResultExecutorBase.GetPreconditionState(
+ var state = FileResultExecutorBase.GetPreconditionState(
actionContext,
httpRequestHeaders,
lastModified,
etag);
// Assert
- Assert.Equal(FileResultExecutorBase.PreconditionState.PreconditionFailed, shouldProcess);
+ Assert.Equal(FileResultExecutorBase.PreconditionState.PreconditionFailed, state);
}
[Theory]
[InlineData(null, "\"Etag\"")]
[InlineData(null, null)]
- public void ComputeConditionalHeaders_ShouldNotProcess_NotModified(string ifMatch, string ifNoneMatch)
+ public void GetPreconditionState_ShouldNotProcess_NotModified(string ifMatch, string ifNoneMatch)
{
var actionContext = new ActionContext();
var httpContext = new DefaultHttpContext();
@@ -363,14 +363,14 @@ public void ComputeConditionalHeaders_ShouldNotProcess_NotModified(string ifMatc
};
httpRequestHeaders.IfModifiedSince = lastModified;
actionContext.HttpContext = httpContext;
- var shouldProcess = FileResultExecutorBase.GetPreconditionState(
+ var state = FileResultExecutorBase.GetPreconditionState(
actionContext,
httpRequestHeaders,
lastModified,
etag);
// Assert
- Assert.Equal(FileResultExecutorBase.PreconditionState.NotModified, shouldProcess);
+ Assert.Equal(FileResultExecutorBase.PreconditionState.NotModified, state);
}
private static IServiceCollection CreateServices()
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileStreamResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileStreamResultTest.cs
index 7e3d0a52f3..dda8daf11c 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileStreamResultTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileStreamResultTest.cs
@@ -127,6 +127,99 @@ public async Task WriteFileAsync_PreconditionStateShouldProcess_WritesRangeReque
Assert.Equal(expectedString, body);
}
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
+ {
+ // Arrange
+ var contentType = "text/plain";
+ var lastModified = DateTimeOffset.MinValue;
+ var entityTag = new EntityTagHeaderValue("\"Etag\"");
+ var byteArray = Encoding.ASCII.GetBytes("Hello World");
+ var readStream = new MemoryStream(byteArray);
+ readStream.SetLength(11);
+
+ var result = new FileStreamResult(readStream, contentType)
+ {
+ LastModified = lastModified,
+ EntityTag = entityTag,
+ };
+
+ var httpContext = GetHttpContext();
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfMatch = new[]
+ {
+ new EntityTagHeaderValue("\"Etag\""),
+ };
+ requestHeaders.Range = new RangeHeaderValue(0, 4);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+ 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.Status206PartialContent, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Equal(5, httpResponse.ContentLength);
+ Assert.Equal("Hello", body);
+ }
+
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedNotSatisfiable()
+ {
+ // Arrange
+ var contentType = "text/plain";
+ var lastModified = DateTimeOffset.MinValue.AddDays(1);
+ var entityTag = new EntityTagHeaderValue("\"Etag\"");
+ var byteArray = Encoding.ASCII.GetBytes("Hello World");
+ var readStream = new MemoryStream(byteArray);
+ readStream.SetLength(11);
+
+ var result = new FileStreamResult(readStream, contentType)
+ {
+ LastModified = lastModified,
+ EntityTag = entityTag,
+ };
+
+ var httpContext = GetHttpContext();
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfMatch = new[]
+ {
+ new EntityTagHeaderValue("\"Etag\""),
+ };
+ requestHeaders.Range = new RangeHeaderValue(0, 4);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(DateTimeOffset.MinValue);
+ 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;
+ var contentRange = new ContentRangeHeaderValue(byteArray.Length);
+ Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Empty(body);
+ }
+
[Theory]
[InlineData("0-5")]
[InlineData("bytes = 11-0")]
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/PhysicalFileResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/PhysicalFileResultTest.cs
index 96bc3bd5e2..656cc5eea1 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/PhysicalFileResultTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/PhysicalFileResultTest.cs
@@ -89,6 +89,68 @@ public async Task WriteFileAsync_WritesRangeRequested(long? start, long? end, st
Assert.Equal(expectedString, body);
}
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
+ {
+ // Arrange
+ var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
+ var result = new TestPhysicalFileResult(path, "text/plain");
+ var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
+ var httpContext = GetHttpContext();
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
+ requestHeaders.Range = new RangeHeaderValue(0, 3);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+ 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.Status206PartialContent, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ var contentRange = new ContentRangeHeaderValue(0, 3, 34);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Equal(4, httpResponse.ContentLength);
+ Assert.Equal("File", body);
+ }
+
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedNotSatisfiable()
+ {
+ // Arrange
+ var path = Path.GetFullPath(Path.Combine("TestFiles", "FilePathResultTestFile.txt"));
+ var result = new TestPhysicalFileResult(path, "text/plain");
+ var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
+ var httpContext = GetHttpContext();
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
+ requestHeaders.Range = new RangeHeaderValue(0, 3);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+ 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.Status416RangeNotSatisfiable, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ Assert.Empty(body);
+ }
+
[Theory]
[InlineData("0-5")]
[InlineData("bytes = 11-0")]
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/VirtualFileResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/VirtualFileResultTest.cs
index b3e98dc638..e3995bb07b 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/VirtualFileResultTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/VirtualFileResultTest.cs
@@ -102,6 +102,95 @@ public async Task WriteFileAsync_WritesRangeRequested(long? start, long? end, st
Assert.Equal(expectedString, body);
}
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
+ {
+ // 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();
+ appEnvironment.Setup(app => app.WebRootFileProvider)
+ .Returns(GetFileProvider(path));
+
+ var httpContext = GetHttpContext();
+ httpContext.Response.Body = new MemoryStream();
+ httpContext.RequestServices = new ServiceCollection()
+ .AddSingleton(appEnvironment.Object)
+ .AddTransient()
+ .AddTransient()
+ .BuildServiceProvider();
+
+ var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
+ requestHeaders.Range = new RangeHeaderValue(0, 3);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+ 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.Status206PartialContent, httpResponse.StatusCode);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ var contentRange = new ContentRangeHeaderValue(0, 3, 33);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Equal(4, httpResponse.ContentLength);
+ Assert.Equal("File", body);
+ }
+
+ [Fact]
+ public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedNotSatisfiable()
+ {
+ // 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();
+ appEnvironment.Setup(app => app.WebRootFileProvider)
+ .Returns(GetFileProvider(path));
+
+ var httpContext = GetHttpContext();
+ httpContext.Response.Body = new MemoryStream();
+ httpContext.RequestServices = new ServiceCollection()
+ .AddSingleton(appEnvironment.Object)
+ .AddTransient()
+ .AddTransient()
+ .BuildServiceProvider();
+
+ var entityTag = result.EntityTag = new EntityTagHeaderValue("\"Etag\"");
+ var requestHeaders = httpContext.Request.GetTypedHeaders();
+ requestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
+ requestHeaders.Range = new RangeHeaderValue(0, 3);
+ requestHeaders.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+ 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.Status416RangeNotSatisfiable, httpResponse.StatusCode);
+ var contentRange = new ContentRangeHeaderValue(33);
+ Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
+ Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
+ Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
+ Assert.Empty(body);
+ }
+
[Theory]
[InlineData("0-5")]
[InlineData("bytes = 11-0")]
diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs
index 387fd9b437..be6e37d473 100644
--- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs
+++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs
@@ -149,6 +149,45 @@ public async Task FileFromDisk_ReturnsFileWithFileName()
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
+ [Fact]
+ public async Task FileFromDisk_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest_WithLastModifiedAndEtag()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromDiskWithFileName_WithLastModifiedAndEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.NotNull(body);
+ Assert.Equal("This is", body);
+ }
+
+ [Fact]
+ public async Task FileFromDisk_ReturnsFileWithFileName_IfRangeHeaderInvalid_RangeRequestNotSatisfiable_WithLastModifiedAndEtag()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromDiskWithFileName_WithLastModifiedAndEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Empty(body);
+ }
+
[Fact]
public async Task FileFromStream_ReturnsFile()
{
@@ -230,6 +269,45 @@ public async Task FileFromStream_ReturnsFileWithFileName()
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
+ [Fact]
+ public async Task FileFromStream_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromStreamWithFileName_WithEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.NotNull(body);
+ Assert.Equal("This is", body);
+ }
+
+ [Fact]
+ public async Task FileFromStream_ReturnsFileWithFileName_IfRangeHeaderInvalid_RangeRequestNotSatisfiable()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromStreamWithFileName_WithEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Empty(body);
+ }
+
[Fact]
public async Task FileFromBinaryData_ReturnsFile()
{
@@ -311,6 +389,45 @@ public async Task FileFromBinaryData_ReturnsFileWithFileName()
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
+ [Fact]
+ public async Task FileFromBinaryData_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName_WithEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.NotNull(body);
+ Assert.Equal("This is", body);
+ }
+
+ [Fact]
+ public async Task FileFromBinaryData_ReturnsFileWithFileName_IfRangeHeaderInvalid_RangeRequestNotSatisfiable()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName_WithEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Empty(body);
+ }
+
[Fact]
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName()
{
@@ -360,6 +477,51 @@ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeRequest
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
}
+ [Fact]
+ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_IfRangeHeaderValid_RangeRequest()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EmbeddedFiles/DownloadFileWithFileName_WithEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.PartialContent, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.NotNull(body);
+ Assert.Equal("Sample ", body);
+ var contentDisposition = response.Content.Headers.ContentDisposition.ToString();
+ Assert.NotNull(contentDisposition);
+ Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
+ }
+
+ [Fact]
+ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_IfRangeHeaderInvalid_RangeRequestotSatisfiable()
+ {
+ // Arrange
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EmbeddedFiles/DownloadFileWithFileName_WithEtag");
+ httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
+ httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"NotEtag\""));
+
+ // Act
+ var response = await Client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.RequestedRangeNotSatisfiable, response.StatusCode);
+ Assert.NotNull(response.Content.Headers.ContentType);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ var body = await response.Content.ReadAsStringAsync();
+ Assert.Empty(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("0-6")]
[InlineData("bytes = 11-6")]
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs
index d8985f0a9e..b6280158cb 100644
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs
@@ -36,7 +36,7 @@ public string Name
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "test.cshtml"));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -57,7 +57,7 @@ public class MyTestType {}";
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "test.cshtml"));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -79,7 +79,7 @@ public void Compile_ReturnsCompilationFailureWithPathsFromLinePragmas()
var compilationService = GetRoslynCompilationService();
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(fileContent, viewPath));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -103,7 +103,7 @@ public void Compile_ReturnsGeneratedCodePath_IfLinePragmaIsNotAvailable()
var compilationService = GetRoslynCompilationService();
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(fileContent, viewPath));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -134,7 +134,7 @@ public class MyNonCustomDefinedClass {}
var compilationService = GetRoslynCompilationService(options: options);
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", viewPath));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -237,7 +237,7 @@ public void Compile_RunsCallback()
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path"));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -258,7 +258,7 @@ public void Compile_DoesNotThrowIfReferencesWereClearedInCallback()
var compilationService = GetRoslynCompilationService(options: options);
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml"));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
@@ -285,7 +285,7 @@ public void Compile_SucceedsIfReferencesAreAddedInCallback()
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("Hello world", "some-relative-path.cshtml"));
- var csharpDocument = RazorCSharpDocument.Create(content, Array.Empty());
+ var csharpDocument = RazorCSharpDocument.Create(content, RazorCodeGenerationOptions.CreateDefault(), Array.Empty());
// Act
var result = compilationService.Compile(codeDocument, csharpDocument);
diff --git a/test/WebSites/FilesWebSite/Controllers/DownloadFilesController.cs b/test/WebSites/FilesWebSite/Controllers/DownloadFilesController.cs
index 0ef71143ac..e86e211932 100644
--- a/test/WebSites/FilesWebSite/Controllers/DownloadFilesController.cs
+++ b/test/WebSites/FilesWebSite/Controllers/DownloadFilesController.cs
@@ -69,6 +69,17 @@ public IActionResult DownloadFromStreamWithFileName()
return File(stream, "text/plain", "downloadName.txt");
}
+ public IActionResult DownloadFromStreamWithFileName_WithEtag()
+ {
+ var stream = new MemoryStream();
+ var writer = new StreamWriter(stream);
+ writer.Write("This is sample text from a stream");
+ writer.Flush();
+ stream.Seek(0, SeekOrigin.Begin);
+ var entityTag = new EntityTagHeaderValue("\"Etag\"");
+ return File(stream, "text/plain", "downloadName.txt", lastModified: null, entityTag: entityTag);
+ }
+
public IActionResult DownloadFromBinaryData()
{
var data = Encoding.UTF8.GetBytes("This is a sample text from a binary array");
@@ -80,5 +91,12 @@ public IActionResult DownloadFromBinaryDataWithFileName()
var data = Encoding.UTF8.GetBytes("This is a sample text from a binary array");
return File(data, "text/plain", "downloadName.txt");
}
+
+ public IActionResult DownloadFromBinaryDataWithFileName_WithEtag()
+ {
+ var data = Encoding.UTF8.GetBytes("This is a sample text from a binary array");
+ var entityTag = new EntityTagHeaderValue("\"Etag\"");
+ return File(data, "text/plain", "downloadName.txt", lastModified: null, entityTag: entityTag);
+ }
}
}
diff --git a/test/WebSites/FilesWebSite/Controllers/EmbeddedFilesController.cs b/test/WebSites/FilesWebSite/Controllers/EmbeddedFilesController.cs
index 5b471886b7..758aea7b7d 100644
--- a/test/WebSites/FilesWebSite/Controllers/EmbeddedFilesController.cs
+++ b/test/WebSites/FilesWebSite/Controllers/EmbeddedFilesController.cs
@@ -17,5 +17,17 @@ public IActionResult DownloadFileWithFileName()
FileDownloadName = "downloadName.txt"
};
}
+
+ public IActionResult DownloadFileWithFileName_WithEtag()
+ {
+ var file = new VirtualFileResult("/Greetings.txt", "text/plain")
+ {
+ FileProvider = new EmbeddedFileProvider(GetType().GetTypeInfo().Assembly, "FilesWebSite.EmbeddedResources"),
+ FileDownloadName = "downloadName.txt"
+ };
+
+ file.EntityTag = new Microsoft.Net.Http.Headers.EntityTagHeaderValue("\"Etag\"");
+ return file;
+ }
}
}
\ No newline at end of file