From 02ce07cce38f0f3b41df14a0db4a07f7d3d57b73 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Wed, 10 Jan 2018 14:54:56 -0800 Subject: [PATCH 1/2] Do not serve response body for HEAD requests --- .../Infrastructure/FileResultExecutorBase.cs | 5 +- .../FileResultTest.cs | 2 - .../FileResultTests.cs | 182 ++++++++++++++++-- 3 files changed, 167 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs index 19e5b8d6c7..d5aba07f2e 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs @@ -295,6 +295,7 @@ private static PreconditionState GetMaxPreconditionState(params PreconditionStat { 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 +307,7 @@ private static PreconditionState GetMaxPreconditionState(params PreconditionStat if (!isRangeRequest) { - return (range: null, rangeLength: 0, serveBody: true); + return (range: null, rangeLength: 0, serveBody); } // Requested range is not satisfiable @@ -330,7 +331,7 @@ private static PreconditionState GetMaxPreconditionState(params PreconditionStat // 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) diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs index 366380abd5..c169414a63 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/FileResultTest.cs @@ -7,13 +7,11 @@ 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; diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs index e5733a558e..6781883e65 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs @@ -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 @@ public async Task FileFromDisk_ReturnsFileWithFileName() 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 @@ public async Task FileFromDisk_ReturnsFileWithFileName_RangeProcessingNotEnabled 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,41 @@ public async Task FileFromDisk_ReturnsFileWithFileName_IfRangeHeaderInvalid_Rang Assert.Equal("This is a sample text file", body); } + [Theory] + [InlineData("", HttpStatusCode.OK)] + [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] + [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] + [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] + [InlineData("0-6", HttpStatusCode.OK)] + [InlineData("bytes = ", HttpStatusCode.OK)] + [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] + [InlineData("bytes = 35-36", HttpStatusCode.RequestedRangeNotSatisfiable)] + [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] + public async Task FileFromDisk_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest_WithLastModifiedAndEtag(string rangeString, HttpStatusCode httpStatusCode) + { + // 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 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 +382,13 @@ public async Task FileFromStream_ReturnsFileWithFileName() 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 +399,7 @@ public async Task FileFromStream_ReturnsFileWithFileName_RangeProcessingNotEnabl 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 +439,41 @@ public async Task FileFromStream_ReturnsFileWithFileName_IfRangeHeaderInvalid_Ra Assert.Equal("This is sample text from a stream", body); } + [Theory] + [InlineData("", HttpStatusCode.OK)] + [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] + [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] + [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] + [InlineData("0-6", HttpStatusCode.OK)] + [InlineData("bytes = ", HttpStatusCode.OK)] + [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] + [InlineData("bytes = 35-36", HttpStatusCode.RequestedRangeNotSatisfiable)] + [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] + public async Task FileFromStream_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode) + { + // 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 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 +578,13 @@ public async Task FileFromBinaryData_ReturnsFileWithFileName() 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 +595,7 @@ public async Task FileFromBinaryData_ReturnsFileWithFileName_RangeProcessingNotE 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 +637,41 @@ public async Task FileFromBinaryData_ReturnsFileWithFileName_IfRangeHeaderInvali Assert.Equal("This is a sample text from a binary array", body); } + [Theory] + [InlineData("", HttpStatusCode.OK)] + [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] + [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] + [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] + [InlineData("0-6", HttpStatusCode.OK)] + [InlineData("bytes = ", HttpStatusCode.OK)] + [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] + [InlineData("bytes = 45-46", HttpStatusCode.RequestedRangeNotSatisfiable)] + [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] + public async Task FileFromBinaryData_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode) + { + // 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 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 +721,13 @@ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeRequest 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 +738,7 @@ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeProcess 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 +832,40 @@ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeRequest Assert.NotNull(contentDisposition); Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition); } + + [Theory] + [InlineData("", HttpStatusCode.OK)] + [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] + [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] + [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] + [InlineData("0-6", HttpStatusCode.OK)] + [InlineData("bytes = ", HttpStatusCode.OK)] + [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] + [InlineData("bytes = 45-46", HttpStatusCode.RequestedRangeNotSatisfiable)] + [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] + public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode) + { + // 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 contentDisposition = response.Content.Headers.ContentDisposition.ToString(); + Assert.NotNull(contentDisposition); + Assert.Equal("attachment; filename=downloadName.txt; filename*=UTF-8''downloadName.txt", contentDisposition); + } } } From 5675822dcf214a47ddf33f4d185c7daa07686cf1 Mon Sep 17 00:00:00 2001 From: Jass Bagga Date: Wed, 10 Jan 2018 16:49:22 -0800 Subject: [PATCH 2/2] Add check for Content-Length in the tests --- .../Infrastructure/FileResultExecutorBase.cs | 16 +--- .../FileResultTests.cs | 92 +++++++++++-------- 2 files changed, 57 insertions(+), 51 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs index d5aba07f2e..c632b2f150 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Infrastructure/FileResultExecutorBase.cs @@ -64,20 +64,16 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody) 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 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody) // 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 @@ protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody) } } - return (range: null, rangeLength: 0, serveBody); + return (range: null, rangeLength: 0, serveBody: !HttpMethods.IsHead(request.Method)); } private static void SetContentType(ActionContext context, FileResult result) diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs index 6781883e65..150e490239 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/FileResultTests.cs @@ -247,16 +247,16 @@ public async Task FileFromDisk_ReturnsFileWithFileName_IfRangeHeaderInvalid_Rang } [Theory] - [InlineData("", HttpStatusCode.OK)] - [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] - [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] - [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] - [InlineData("0-6", HttpStatusCode.OK)] - [InlineData("bytes = ", HttpStatusCode.OK)] - [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] - [InlineData("bytes = 35-36", HttpStatusCode.RequestedRangeNotSatisfiable)] - [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] - public async Task FileFromDisk_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest_WithLastModifiedAndEtag(string rangeString, HttpStatusCode httpStatusCode) + [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"); @@ -276,6 +276,9 @@ public async Task FileFromDisk_ReturnsFileWithFileName_DoesNotServeBody_ForHeadR 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); @@ -440,16 +443,16 @@ public async Task FileFromStream_ReturnsFileWithFileName_IfRangeHeaderInvalid_Ra } [Theory] - [InlineData("", HttpStatusCode.OK)] - [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] - [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] - [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] - [InlineData("0-6", HttpStatusCode.OK)] - [InlineData("bytes = ", HttpStatusCode.OK)] - [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] - [InlineData("bytes = 35-36", HttpStatusCode.RequestedRangeNotSatisfiable)] - [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] - public async Task FileFromStream_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode) + [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"); @@ -469,6 +472,9 @@ public async Task FileFromStream_ReturnsFileWithFileName_DoesNotServeBody_ForHea 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); @@ -638,16 +644,16 @@ public async Task FileFromBinaryData_ReturnsFileWithFileName_IfRangeHeaderInvali } [Theory] - [InlineData("", HttpStatusCode.OK)] - [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] - [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] - [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] - [InlineData("0-6", HttpStatusCode.OK)] - [InlineData("bytes = ", HttpStatusCode.OK)] - [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] - [InlineData("bytes = 45-46", HttpStatusCode.RequestedRangeNotSatisfiable)] - [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] - public async Task FileFromBinaryData_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode) + [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"); @@ -667,6 +673,9 @@ public async Task FileFromBinaryData_ReturnsFileWithFileName_DoesNotServeBody_Fo 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); @@ -834,16 +843,16 @@ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_RangeRequest } [Theory] - [InlineData("", HttpStatusCode.OK)] - [InlineData("bytes = 0-6", HttpStatusCode.PartialContent)] - [InlineData("bytes = 17-25", HttpStatusCode.PartialContent)] - [InlineData("bytes = 0-50", HttpStatusCode.PartialContent)] - [InlineData("0-6", HttpStatusCode.OK)] - [InlineData("bytes = ", HttpStatusCode.OK)] - [InlineData("bytes = 1-4, 5-11", HttpStatusCode.OK)] - [InlineData("bytes = 45-46", HttpStatusCode.RequestedRangeNotSatisfiable)] - [InlineData("bytes = -0", HttpStatusCode.RequestedRangeNotSatisfiable)] - public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_DoesNotServeBody_ForHeadRequest(string rangeString, HttpStatusCode httpStatusCode) + [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"); @@ -863,6 +872,9 @@ public async Task FileFromEmbeddedResources_ReturnsFileWithFileName_DoesNotServe 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);