Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit b3aa691

Browse files
authored
Add EnableRangeProcessingSwitch for FileContentResult and Fil… (#6839)
Addresses #6792
1 parent 13e29e2 commit b3aa691

File tree

7 files changed

+219
-80
lines changed

7 files changed

+219
-80
lines changed

src/Microsoft.AspNetCore.Mvc.Core/Internal/FileContentResultExecutor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ public virtual Task ExecuteAsync(ActionContext context, FileContentResult result
2828
throw new ArgumentNullException(nameof(result));
2929
}
3030

31+
AppContext.TryGetSwitch(EnableRangeProcessingSwitch, out var enableRangeProcessingSwitch);
3132
var (range, rangeLength, serveBody) = SetHeadersAndLog(
3233
context,
3334
result,
3435
result.FileContents.Length,
3536
result.LastModified,
36-
result.EntityTag);
37+
result.EntityTag,
38+
enableRangeProcessingSwitch);
3739

3840
if (!serveBody)
3941
{

src/Microsoft.AspNetCore.Mvc.Core/Internal/FileResultExecutorBase.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
1717
{
1818
public class FileResultExecutorBase
1919
{
20+
internal const string EnableRangeProcessingSwitch = "Switch.Microsoft.AspNetCore.Mvc.EnableRangeProcessing";
21+
2022
private const string AcceptRangeHeaderValue = "bytes";
2123

2224
protected const int BufferSize = 64 * 1024;
@@ -41,7 +43,8 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody)
4143
FileResult result,
4244
long? fileLength,
4345
DateTimeOffset? lastModified = null,
44-
EntityTagHeaderValue etag = null)
46+
EntityTagHeaderValue etag = null,
47+
bool enableRangeProcessing = true)
4548
{
4649
if (context == null)
4750
{
@@ -84,19 +87,23 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody)
8487

8588
if (fileLength.HasValue)
8689
{
87-
SetAcceptRangeHeader(context);
8890
// Assuming the request is not a range request, the Content-Length header is set to the length of the entire file.
8991
// If the request is a valid range request, this header is overwritten with the length of the range as part of the
9092
// range processing (see method SetContentLength).
9193
response.ContentLength = fileLength.Value;
92-
if (HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
94+
95+
if (enableRangeProcessing)
9396
{
94-
if ((preconditionState == PreconditionState.Unspecified ||
95-
preconditionState == PreconditionState.ShouldProcess))
97+
SetAcceptRangeHeader(context);
98+
if (HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
9699
{
97-
if (IfRangeValid(context, httpRequestHeaders, lastModified, etag))
100+
if ((preconditionState == PreconditionState.Unspecified ||
101+
preconditionState == PreconditionState.ShouldProcess))
98102
{
99-
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
103+
if (IfRangeValid(context, httpRequestHeaders, lastModified, etag))
104+
{
105+
return SetRangeHeaders(context, httpRequestHeaders, fileLength.Value);
106+
}
100107
}
101108
}
102109
}

src/Microsoft.AspNetCore.Mvc.Core/Internal/FileStreamResultExecutor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ public virtual Task ExecuteAsync(ActionContext context, FileStreamResult result)
3333
fileLength = result.FileStream.Length;
3434
}
3535

36+
AppContext.TryGetSwitch(EnableRangeProcessingSwitch, out var enableRangeProcessingSwitch);
3637
var (range, rangeLength, serveBody) = SetHeadersAndLog(
3738
context,
3839
result,
3940
fileLength,
4041
result.LastModified,
41-
result.EntityTag);
42+
result.EntityTag,
43+
enableRangeProcessingSwitch);
4244

4345
if (!serveBody)
4446
{

src/Microsoft.AspNetCore.Mvc.Core/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
using System.Runtime.CompilerServices;
55

66
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Core.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
7+
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
78
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

test/Microsoft.AspNetCore.Mvc.Core.Test/FileContentResultTest.cs

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public async Task WriteFileAsync_CopiesBuffer_ToOutputStream()
9999
[InlineData(6, 10, "World", 5)]
100100
[InlineData(null, 5, "World", 5)]
101101
[InlineData(6, null, "World", 5)]
102-
public async Task WriteFileAsync_PreconditionStateShouldProcess_WritesRangeRequested(long? start, long? end, string expectedString, long contentLength)
102+
public async Task WriteFileAsync_PreconditionStateShouldProcess_RangeRequestIgnored_WritesRangeRequested_IfRangeProcessingOn(long? start, long? end, string expectedString, long contentLength)
103103
{
104104
// Arrange
105105
var contentType = "text/plain";
@@ -134,18 +134,29 @@ public async Task WriteFileAsync_PreconditionStateShouldProcess_WritesRangeReque
134134
httpResponse.Body.Seek(0, SeekOrigin.Begin);
135135
var streamReader = new StreamReader(httpResponse.Body);
136136
var body = streamReader.ReadToEndAsync().Result;
137-
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
138-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
139-
var contentRange = new ContentRangeHeaderValue(start.Value, end.Value, byteArray.Length);
140-
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
141137
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
142138
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
143-
Assert.Equal(contentLength, httpResponse.ContentLength);
144-
Assert.Equal(expectedString, body);
139+
140+
if (AppContext.TryGetSwitch(FileResultExecutorBase.EnableRangeProcessingSwitch, out var enableRangeProcessingSwitch)
141+
&& enableRangeProcessingSwitch)
142+
{
143+
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
144+
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
145+
var contentRange = new ContentRangeHeaderValue(start.Value, end.Value, byteArray.Length);
146+
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
147+
Assert.Equal(contentLength, httpResponse.ContentLength);
148+
Assert.Equal(expectedString, body);
149+
}
150+
else
151+
{
152+
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
153+
Assert.Equal(11, httpResponse.ContentLength);
154+
Assert.Equal("Hello World", body);
155+
}
145156
}
146157

147158
[Fact]
148-
public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
159+
public async Task WriteFileAsync_IfRangeHeaderValid_WritesRangeRequest_IfRangeProcessingOn()
149160
{
150161
// Arrange
151162
var contentType = "text/plain";
@@ -179,18 +190,29 @@ public async Task WriteFileAsync_IfRangeHeaderValid_WritesRequestedRange()
179190
httpResponse.Body.Seek(0, SeekOrigin.Begin);
180191
var streamReader = new StreamReader(httpResponse.Body);
181192
var body = streamReader.ReadToEndAsync().Result;
182-
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
183-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
184-
var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
185-
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
186193
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
187194
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
188-
Assert.Equal(5, httpResponse.ContentLength);
189-
Assert.Equal("Hello", body);
195+
196+
if (AppContext.TryGetSwitch(FileResultExecutorBase.EnableRangeProcessingSwitch, out var enableRangeProcessingSwitch)
197+
&& enableRangeProcessingSwitch)
198+
{
199+
Assert.Equal(StatusCodes.Status206PartialContent, httpResponse.StatusCode);
200+
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
201+
var contentRange = new ContentRangeHeaderValue(0, 4, byteArray.Length);
202+
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
203+
Assert.Equal(5, httpResponse.ContentLength);
204+
Assert.Equal("Hello", body);
205+
}
206+
else
207+
{
208+
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
209+
Assert.Equal(11, httpResponse.ContentLength);
210+
Assert.Equal("Hello World", body);
211+
}
190212
}
191213

192214
[Fact]
193-
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedIgnored()
215+
public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestIgnored()
194216
{
195217
// Arrange
196218
var contentType = "text/plain";
@@ -225,7 +247,6 @@ public async Task WriteFileAsync_IfRangeHeaderInvalid_RangeRequestedIgnored()
225247
var streamReader = new StreamReader(httpResponse.Body);
226248
var body = streamReader.ReadToEndAsync().Result;
227249
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
228-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
229250
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
230251
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
231252
Assert.Equal("Hello World", body);
@@ -265,7 +286,6 @@ public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestIgnore
265286
var body = streamReader.ReadToEndAsync().Result;
266287
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
267288
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
268-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
269289
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
270290
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
271291
Assert.Equal("Hello World", body);
@@ -303,16 +323,26 @@ public async Task WriteFileAsync_PreconditionStateUnspecified_RangeRequestedNotS
303323
var streamReader = new StreamReader(httpResponse.Body);
304324
var body = streamReader.ReadToEndAsync().Result;
305325
var contentRange = new ContentRangeHeaderValue(byteArray.Length);
306-
Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
307-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
308-
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
309326
Assert.Equal(lastModified.ToString("R"), httpResponse.Headers[HeaderNames.LastModified]);
310327
Assert.Equal(entityTag.ToString(), httpResponse.Headers[HeaderNames.ETag]);
311-
Assert.Empty(body);
328+
329+
if (AppContext.TryGetSwitch(FileResultExecutorBase.EnableRangeProcessingSwitch, out var enableRangeProcessingSwitch)
330+
&& enableRangeProcessingSwitch)
331+
{
332+
Assert.Equal(StatusCodes.Status416RangeNotSatisfiable, httpResponse.StatusCode);
333+
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
334+
Assert.Equal(contentRange.ToString(), httpResponse.Headers[HeaderNames.ContentRange]);
335+
Assert.Empty(body);
336+
}
337+
else
338+
{
339+
Assert.Equal(StatusCodes.Status200OK, httpResponse.StatusCode);
340+
Assert.Equal("Hello World", body);
341+
}
312342
}
313343

314344
[Fact]
315-
public async Task WriteFileAsync_RangeRequested_PreconditionFailed()
345+
public async Task WriteFileAsync_PreconditionFailed()
316346
{
317347
// Arrange
318348
var contentType = "text/plain";
@@ -346,15 +376,14 @@ public async Task WriteFileAsync_RangeRequested_PreconditionFailed()
346376
var streamReader = new StreamReader(httpResponse.Body);
347377
var body = streamReader.ReadToEndAsync().Result;
348378
Assert.Equal(StatusCodes.Status412PreconditionFailed, httpResponse.StatusCode);
349-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
350379
Assert.Equal(11, httpResponse.ContentLength);
351380
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
352381
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);
353382
Assert.Empty(body);
354383
}
355384

356385
[Fact]
357-
public async Task WriteFileAsync_RangeRequested_NotModified()
386+
public async Task WriteFileAsync_NotModified()
358387
{
359388
// Arrange
360389
var contentType = "text/plain";
@@ -388,7 +417,6 @@ public async Task WriteFileAsync_RangeRequested_NotModified()
388417
var streamReader = new StreamReader(httpResponse.Body);
389418
var body = streamReader.ReadToEndAsync().Result;
390419
Assert.Equal(StatusCodes.Status304NotModified, httpResponse.StatusCode);
391-
Assert.Equal("bytes", httpResponse.Headers[HeaderNames.AcceptRanges]);
392420
Assert.Equal(11, httpResponse.ContentLength);
393421
Assert.Empty(httpResponse.Headers[HeaderNames.ContentRange]);
394422
Assert.NotEmpty(httpResponse.Headers[HeaderNames.LastModified]);

0 commit comments

Comments
 (0)