parent
2aae8774f6
commit
9438a453b0
|
|
@ -64,20 +64,16 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
var response = context.HttpContext.Response;
|
||||
SetLastModifiedAndEtagHeaders(response, lastModified, etag);
|
||||
|
||||
var serveBody = !HttpMethods.IsHead(request.Method);
|
||||
|
||||
// Short circuit if the preconditional headers process to 304 (NotModified) or 412 (PreconditionFailed)
|
||||
if (preconditionState == PreconditionState.NotModified)
|
||||
{
|
||||
serveBody = false;
|
||||
response.StatusCode = StatusCodes.Status304NotModified;
|
||||
return (range: null, rangeLength: 0, serveBody);
|
||||
return (range: null, rangeLength: 0, serveBody: false);
|
||||
}
|
||||
else if (preconditionState == PreconditionState.PreconditionFailed)
|
||||
{
|
||||
serveBody = false;
|
||||
response.StatusCode = StatusCodes.Status412PreconditionFailed;
|
||||
return (range: null, rangeLength: 0, serveBody);
|
||||
return (range: null, rangeLength: 0, serveBody: false);
|
||||
}
|
||||
|
||||
if (fileLength.HasValue)
|
||||
|
|
@ -86,10 +82,8 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
// 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).
|
||||
if (serveBody)
|
||||
{
|
||||
response.ContentLength = fileLength.Value;
|
||||
}
|
||||
|
||||
response.ContentLength = fileLength.Value;
|
||||
|
||||
// Handle range request
|
||||
if (enableRangeProcessing)
|
||||
|
|
@ -111,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
return (range: null, rangeLength: 0, serveBody);
|
||||
return (range: null, rangeLength: 0, serveBody: !HttpMethods.IsHead(request.Method));
|
||||
}
|
||||
|
||||
private static void SetContentType(ActionContext context, FileResult result)
|
||||
|
|
@ -295,6 +289,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
{
|
||||
var response = context.HttpContext.Response;
|
||||
var httpResponseHeaders = response.GetTypedHeaders();
|
||||
var serveBody = !HttpMethods.IsHead(context.HttpContext.Request.Method);
|
||||
|
||||
// Range may be null for empty range header, invalid ranges, parsing errors, multiple ranges
|
||||
// and when the file length is zero.
|
||||
|
|
@ -306,7 +301,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
|
||||
if (!isRangeRequest)
|
||||
{
|
||||
return (range: null, rangeLength: 0, serveBody: true);
|
||||
return (range: null, rangeLength: 0, serveBody);
|
||||
}
|
||||
|
||||
// Requested range is not satisfiable
|
||||
|
|
@ -330,7 +325,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
// Overwrite the Content-Length header for valid range requests with the range length.
|
||||
var rangeLength = SetContentLength(response, range);
|
||||
|
||||
return (range, rangeLength, serveBody: true);
|
||||
return (range, rangeLength, serveBody);
|
||||
}
|
||||
|
||||
private static long SetContentLength(HttpResponse response, RangeItemHeaderValue range)
|
||||
|
|
|
|||
|
|
@ -7,13 +7,11 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -189,11 +187,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FileFromDisk_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
|
||||
[Theory]
|
||||
[InlineData("GET", "This is a sample text file")]
|
||||
[InlineData("HEAD", "")]
|
||||
public async Task FileFromDisk_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored(string httpMethod, string expectedBody)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromDiskWithFileName");
|
||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/DownloadFiles/DownloadFromDiskWithFileName");
|
||||
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
|
||||
|
||||
// Act
|
||||
|
|
@ -204,7 +204,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
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);
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -246,6 +246,44 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("This is a sample text file", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", HttpStatusCode.OK, 26)]
|
||||
[InlineData("bytes = 0-6", HttpStatusCode.PartialContent, 7)]
|
||||
[InlineData("bytes = 17-25", HttpStatusCode.PartialContent, 9)]
|
||||
[InlineData("bytes = 0-50", HttpStatusCode.PartialContent, 26)]
|
||||
[InlineData("0-6", HttpStatusCode.OK, 26)]
|
||||
[InlineData("bytes = ", HttpStatusCode.OK, 26)]
|
||||
[InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK, 26)]
|
||||
[InlineData("bytes = 35-36", HttpStatusCode.RequestedRangeNotSatisfiable, 26)]
|
||||
[InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable, 26)]
|
||||
public async Task FileFromDisk_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest_WithLastModifiedAndEtag(string rangeString, HttpStatusCode httpStatusCode, int expectedContentLength)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, "http://localhost/DownloadFiles/DownloadFromDiskWithFileName_WithLastModifiedAndEtag");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(httpStatusCode, 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(string.Empty, body);
|
||||
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
Assert.Equal(expectedContentLength, contentLength);
|
||||
|
||||
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 FileFromStream_ReturnsFile()
|
||||
{
|
||||
|
|
@ -347,11 +385,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FileFromStream_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
|
||||
[Theory]
|
||||
[InlineData("GET", "This is sample text from a stream")]
|
||||
[InlineData("HEAD", "")]
|
||||
public async Task FileFromStream_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored(string httpMethod, string expectedBody)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromStreamWithFileName");
|
||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/DownloadFiles/DownloadFromStreamWithFileName");
|
||||
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
|
||||
|
||||
// Act
|
||||
|
|
@ -362,7 +402,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
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);
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -402,6 +442,44 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("This is sample text from a stream", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", HttpStatusCode.OK, 33)]
|
||||
[InlineData("bytes = 0-6", HttpStatusCode.PartialContent, 7)]
|
||||
[InlineData("bytes = 17-25", HttpStatusCode.PartialContent, 9)]
|
||||
[InlineData("bytes = 0-50", HttpStatusCode.PartialContent, 33)]
|
||||
[InlineData("0-6", HttpStatusCode.OK, 33)]
|
||||
[InlineData("bytes = ", HttpStatusCode.OK, 33)]
|
||||
[InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK, 33)]
|
||||
[InlineData("bytes = 35-36", HttpStatusCode.RequestedRangeNotSatisfiable, 33)]
|
||||
[InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable, 33)]
|
||||
public async Task FileFromStream_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode, int expectedContentLength)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, "http://localhost/DownloadFiles/DownloadFromStreamWithFileName_WithEtag");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(httpStatusCode, 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(string.Empty, body);
|
||||
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
Assert.Equal(expectedContentLength, contentLength);
|
||||
|
||||
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 FileFromBinaryData_ReturnsFile()
|
||||
{
|
||||
|
|
@ -506,11 +584,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FileFromBinaryData_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
|
||||
[Theory]
|
||||
[InlineData("GET", "This is a sample text from a binary array")]
|
||||
[InlineData("HEAD", "")]
|
||||
public async Task FileFromBinaryData_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored(string httpMethod, string expectedBody)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName");
|
||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName");
|
||||
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
|
||||
|
||||
// Act
|
||||
|
|
@ -521,7 +601,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
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);
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -563,6 +643,44 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("This is a sample text from a binary array", body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", HttpStatusCode.OK, 41)]
|
||||
[InlineData("bytes = 0-6", HttpStatusCode.PartialContent, 7)]
|
||||
[InlineData("bytes = 17-25", HttpStatusCode.PartialContent, 9)]
|
||||
[InlineData("bytes = 0-50", HttpStatusCode.PartialContent, 41)]
|
||||
[InlineData("0-6", HttpStatusCode.OK, 41)]
|
||||
[InlineData("bytes = ", HttpStatusCode.OK, 41)]
|
||||
[InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK, 41)]
|
||||
[InlineData("bytes = 45-46", HttpStatusCode.RequestedRangeNotSatisfiable, 41)]
|
||||
[InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable, 41)]
|
||||
public async Task FileFromBinaryData_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode, int expectedContentLength)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, "http://localhost/DownloadFiles/DownloadFromBinaryDataWithFileName_WithEtag");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(httpStatusCode, 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(string.Empty, body);
|
||||
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
Assert.Equal(expectedContentLength, contentLength);
|
||||
|
||||
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()
|
||||
{
|
||||
|
|
@ -612,11 +730,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored()
|
||||
[Theory]
|
||||
[InlineData("GET", "Sample text file as embedded resource.")]
|
||||
[InlineData("HEAD", "")]
|
||||
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeProcessingNotEnabled_RangeRequestedIgnored(string httpMethod, string expectedBody)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://localhost/EmbeddedFiles/DownloadFileWithFileName_RangeProcessingNotEnabled");
|
||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(httpMethod), "http://localhost/EmbeddedFiles/DownloadFileWithFileName_RangeProcessingNotEnabled");
|
||||
httpRequestMessage.Headers.Range = new RangeHeaderValue(0, 6);
|
||||
|
||||
// Act
|
||||
|
|
@ -627,7 +747,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
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);
|
||||
Assert.Equal(expectedBody, body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -721,5 +841,43 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.NotNull(contentDisposition);
|
||||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", HttpStatusCode.OK, 38)]
|
||||
[InlineData("bytes = 0-6", HttpStatusCode.PartialContent, 7)]
|
||||
[InlineData("bytes = 17-25", HttpStatusCode.PartialContent, 9)]
|
||||
[InlineData("bytes = 0-50", HttpStatusCode.PartialContent, 38)]
|
||||
[InlineData("0-6", HttpStatusCode.OK, 38)]
|
||||
[InlineData("bytes = ", HttpStatusCode.OK, 38)]
|
||||
[InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK, 38)]
|
||||
[InlineData("bytes = 45-46", HttpStatusCode.RequestedRangeNotSatisfiable, 38)]
|
||||
[InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable, 38)]
|
||||
public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode, int expectedContentLength)
|
||||
{
|
||||
// Arrange
|
||||
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, "http://localhost/EmbeddedFiles/DownloadFileWithFileName");
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation("Range", rangeString);
|
||||
httpRequestMessage.Headers.IfRange = new RangeConditionHeaderValue(new EntityTagHeaderValue("\"Etag\""));
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(httpRequestMessage);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(httpStatusCode, 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(string.Empty, body);
|
||||
|
||||
var contentLength = response.Content.Headers.ContentLength;
|
||||
Assert.Equal(expectedContentLength, contentLength);
|
||||
|
||||
var contentDisposition = response.Content.Headers.ContentDisposition.ToString();
|
||||
Assert.NotNull(contentDisposition);
|
||||
Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue