Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Ross (ASP.NET) <chrross@microsoft.com>2020-07-17 00:30:13 +0300
committerChris Ross (ASP.NET) <chrross@microsoft.com>2020-07-17 00:30:13 +0300
commitbccd1ea5e8d4dbd4f1367946b22ea60efffd7b50 (patch)
treed5be6dbe2c03a885d65b1f88f62b3f1d52188964
parentcb4e0cef8b84e7224ab77ff3c6a85edd23005fc2 (diff)
Merged PR 9014: [2.1] Cancel SendFile copy loops
There are two places in 2.1 where SendFileAsync falls back to a copy loop. These loops should short circuit when the client disconnects, or else the server will sit there and burn resources reading the whole file from disk. Fix: If you passed in your own active CT we'll use it. Otherwise we'll use the RequestAborted token.
-rw-r--r--eng/PatchConfig.props2
-rw-r--r--src/Http/Http.Extensions/src/SendFileResponseExtensions.cs71
-rw-r--r--src/Http/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj4
-rw-r--r--src/Http/Http.Extensions/test/SendFileResponseExtensionsTests.cs42
-rw-r--r--src/Http/Http.Extensions/test/testfile1kb.txt1
-rw-r--r--src/Http/HttpAbstractions.sln30
-rw-r--r--src/Middleware/Middleware.sln15
-rw-r--r--src/Middleware/ResponseCompression/src/BodyWrapperStream.cs50
-rw-r--r--src/Middleware/ResponseCompression/test/BodyWrapperStreamTests.cs18
9 files changed, 200 insertions, 33 deletions
diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props
index 3eb4d105ba..6f17fb8dec 100644
--- a/eng/PatchConfig.props
+++ b/eng/PatchConfig.props
@@ -68,6 +68,8 @@ Later on, this will be checked using this condition:
</PropertyGroup>
<PropertyGroup Condition=" '$(VersionPrefix)' == '2.1.21' ">
<PackagesInPatch>
+ Microsoft.AspNetCore.Http.Extensions;
+ Microsoft.AspNetCore.ResponseCompression;
</PackagesInPatch>
</PropertyGroup>
</Project>
diff --git a/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs b/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs
index 74c0422ef4..1ff159c7c9 100644
--- a/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs
+++ b/src/Http/Http.Extensions/src/SendFileResponseExtensions.cs
@@ -107,41 +107,28 @@ namespace Microsoft.AspNetCore.Http
private static async Task SendFileAsyncCore(HttpResponse response, IFileInfo file, long offset, long? count, CancellationToken cancellationToken)
{
- if (string.IsNullOrEmpty(file.PhysicalPath))
+ if (!string.IsNullOrEmpty(file.PhysicalPath))
{
- CheckRange(offset, count, file.Length);
-
- using (var fileContent = file.CreateReadStream())
- {
- if (offset > 0)
- {
- fileContent.Seek(offset, SeekOrigin.Begin);
- }
- await StreamCopyOperation.CopyToAsync(fileContent, response.Body, count, cancellationToken);
- }
+ await response.SendFileAsync(file.PhysicalPath, offset, count, cancellationToken);
+ return;
}
- else
+
+ CheckRange(offset, count, file.Length);
+ using (var fileContent = file.CreateReadStream())
{
- await response.SendFileAsync(file.PhysicalPath, offset, count, cancellationToken);
+ await SendStreamAsync(fileContent, response, offset, count, cancellationToken);
}
}
- private static Task SendFileAsyncCore(HttpResponse response, string fileName, long offset, long? count, CancellationToken cancellationToken = default)
+ private static async Task SendFileAsyncCore(HttpResponse response, string fileName, long offset, long? count, CancellationToken cancellationToken = default)
{
var sendFile = response.HttpContext.Features.Get<IHttpSendFileFeature>();
- if (sendFile == null)
+ if (sendFile != null)
{
- return SendFileAsyncCore(response.Body, fileName, offset, count, cancellationToken);
+ await sendFile.SendFileAsync(fileName, offset, count, cancellationToken);
+ return;
}
- return sendFile.SendFileAsync(fileName, offset, count, cancellationToken);
- }
-
- // Not safe for overlapped writes.
- private static async Task SendFileAsyncCore(Stream outputStream, string fileName, long offset, long? count, CancellationToken cancel = default)
- {
- cancel.ThrowIfCancellationRequested();
-
var fileInfo = new FileInfo(fileName);
CheckRange(offset, count, fileInfo.Length);
@@ -156,13 +143,43 @@ namespace Microsoft.AspNetCore.Http
using (fileStream)
{
+ await SendStreamAsync(fileStream, response, offset, count, cancellationToken);
+ }
+ }
+
+ private static Task SendStreamAsync(Stream source, HttpResponse response, long offset, long? count, CancellationToken cancellationToken)
+ {
+ if (!cancellationToken.CanBeCanceled)
+ {
+ return SendStreamQuietAsync(source, response, offset, count, response.HttpContext.RequestAborted);
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ if (offset > 0)
+ {
+ source.Seek(offset, SeekOrigin.Begin);
+ }
+
+ return StreamCopyOperation.CopyToAsync(source, response.Body, count, cancellationToken);
+ }
+
+ private static async Task SendStreamQuietAsync(Stream source, HttpResponse response, long offset, long? count, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+
+ try
+ {
if (offset > 0)
{
- fileStream.Seek(offset, SeekOrigin.Begin);
+ source.Seek(offset, SeekOrigin.Begin);
}
- await StreamCopyOperation.CopyToAsync(fileStream, outputStream, count, cancel);
+ await StreamCopyOperation.CopyToAsync(source, response.Body, count, cancellationToken);
}
+ catch (OperationCanceledException) { }
}
private static void CheckRange(long offset, long? count, long fileLength)
@@ -178,4 +195,4 @@ namespace Microsoft.AspNetCore.Http
}
}
}
-} \ No newline at end of file
+}
diff --git a/src/Http/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj b/src/Http/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj
index aa69c02d8a..d13e755526 100644
--- a/src/Http/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj
+++ b/src/Http/Http.Extensions/test/Microsoft.AspNetCore.Http.Extensions.Tests.csproj
@@ -5,6 +5,10 @@
</PropertyGroup>
<ItemGroup>
+ <Content Include="testfile1kb.txt" CopyToOutputDirectory="PreserveNewest" />
+ </ItemGroup>
+
+ <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http" />
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
diff --git a/src/Http/Http.Extensions/test/SendFileResponseExtensionsTests.cs b/src/Http/Http.Extensions/test/SendFileResponseExtensionsTests.cs
index f4c7c0f2a9..a763317cea 100644
--- a/src/Http/Http.Extensions/test/SendFileResponseExtensionsTests.cs
+++ b/src/Http/Http.Extensions/test/SendFileResponseExtensionsTests.cs
@@ -1,5 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information.
+using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -49,5 +50,46 @@ namespace Microsoft.AspNetCore.Http.Extensions.Tests
return Task.FromResult(0);
}
}
+
+ [Fact]
+ public async Task SendFile_FallsBackToBodyStream()
+ {
+ var body = new MemoryStream();
+ var context = new DefaultHttpContext();
+ var response = context.Response;
+ response.Body = body;
+
+ await response.SendFileAsync("testfile1kb.txt", 1, 3, CancellationToken.None);
+
+ Assert.Equal(3, body.Length);
+ }
+
+ [Fact]
+ public async Task SendFile_ThrowsWhenCanceled()
+ {
+ var body = new MemoryStream();
+ var context = new DefaultHttpContext();
+ var response = context.Response;
+ response.Body = body;
+
+ await Assert.ThrowsAsync<OperationCanceledException>(
+ () => response.SendFileAsync("testfile1kb.txt", 1, 3, new CancellationToken(canceled: true)));
+
+ Assert.Equal(0, body.Length);
+ }
+
+ [Fact]
+ public async Task SendFile_AbortsSilentlyWhenRequestCanceled()
+ {
+ var body = new MemoryStream();
+ var context = new DefaultHttpContext();
+ context.RequestAborted = new CancellationToken(canceled: true);
+ var response = context.Response;
+ response.Body = body;
+
+ await response.SendFileAsync("testfile1kb.txt", 1, 3, CancellationToken.None);
+
+ Assert.Equal(0, body.Length);
+ }
}
}
diff --git a/src/Http/Http.Extensions/test/testfile1kb.txt b/src/Http/Http.Extensions/test/testfile1kb.txt
new file mode 100644
index 0000000000..24baa4c608
--- /dev/null
+++ b/src/Http/Http.Extensions/test/testfile1kb.txt
@@ -0,0 +1 @@
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ No newline at end of file
diff --git a/src/Http/HttpAbstractions.sln b/src/Http/HttpAbstractions.sln
index 17e89b8dea..6e2a5eabbf 100644
--- a/src/Http/HttpAbstractions.sln
+++ b/src/Http/HttpAbstractions.sln
@@ -81,6 +81,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RoutingSample.Web", "Routin
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{793FFE24-138A-4C3D-81AB-18D625E36230}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel", "..\Servers\Kestrel\Kestrel\src\Microsoft.AspNetCore.Server.Kestrel.csproj", "{C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IISIntegration", "..\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj", "{11E37916-24DF-48A3-AFC9-9E9BB76588E4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -403,6 +407,30 @@ Global
{F4F5D8AF-FBD1-463F-9473-B63AA820A6C4}.Release|x64.Build.0 = Release|Any CPU
{F4F5D8AF-FBD1-463F-9473-B63AA820A6C4}.Release|x86.ActiveCfg = Release|Any CPU
{F4F5D8AF-FBD1-463F-9473-B63AA820A6C4}.Release|x86.Build.0 = Release|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Debug|x64.Build.0 = Debug|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Debug|x86.Build.0 = Debug|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Release|x64.ActiveCfg = Release|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Release|x64.Build.0 = Release|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Release|x86.ActiveCfg = Release|Any CPU
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566}.Release|x86.Build.0 = Release|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Debug|x64.Build.0 = Debug|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Debug|x86.Build.0 = Debug|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Release|x64.ActiveCfg = Release|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Release|x64.Build.0 = Release|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Release|x86.ActiveCfg = Release|Any CPU
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -435,6 +463,8 @@ Global
{E4AC79A3-625B-421B-9F91-EFCBD9BEB37F} = {24D19E8E-25FD-4C0B-8865-697878B67BE0}
{BF8DC0FF-96F9-4705-8CFA-F42BE989AB6A} = {793FFE24-138A-4C3D-81AB-18D625E36230}
{F4F5D8AF-FBD1-463F-9473-B63AA820A6C4} = {14A7B3DE-46C8-4245-B0BD-9AFF3795C163}
+ {C2608BEB-0C4C-4EDB-A0E4-E29AE59B4566} = {793FFE24-138A-4C3D-81AB-18D625E36230}
+ {11E37916-24DF-48A3-AFC9-9E9BB76588E4} = {793FFE24-138A-4C3D-81AB-18D625E36230}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {85B5E151-2E9D-419C-83DD-0DDCF446C83A}
diff --git a/src/Middleware/Middleware.sln b/src/Middleware/Middleware.sln
index e861bd87a8..111573a9c9 100644
--- a/src/Middleware/Middleware.sln
+++ b/src/Middleware/Middleware.sln
@@ -191,6 +191,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_dependencies", "_dependencies", "{ACA6DDB9-7592-47CE-A740-D15BF307E9E0}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IISIntegration", "..\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj", "{F05215CF-F754-47BF-ACED-259C53C8B223}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1005,6 +1007,18 @@ Global
{260E77CB-800F-4A13-BE92-9CAA097705C2}.Release|x64.Build.0 = Release|Any CPU
{260E77CB-800F-4A13-BE92-9CAA097705C2}.Release|x86.ActiveCfg = Release|Any CPU
{260E77CB-800F-4A13-BE92-9CAA097705C2}.Release|x86.Build.0 = Release|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Debug|x64.Build.0 = Debug|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Debug|x86.Build.0 = Debug|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Release|x64.ActiveCfg = Release|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Release|x64.Build.0 = Release|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Release|x86.ActiveCfg = Release|Any CPU
+ {F05215CF-F754-47BF-ACED-259C53C8B223}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1086,6 +1100,7 @@ Global
{0186A5D0-6D05-4C19-BB81-E49A51745FFF} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{17B7BFF6-4E72-410C-B690-02741505500A} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{260E77CB-800F-4A13-BE92-9CAA097705C2} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
+ {F05215CF-F754-47BF-ACED-259C53C8B223} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {83786312-A93B-4BB4-AB06-7C6913A59AFA}
diff --git a/src/Middleware/ResponseCompression/src/BodyWrapperStream.cs b/src/Middleware/ResponseCompression/src/BodyWrapperStream.cs
index d315d82dfa..ee84b49338 100644
--- a/src/Middleware/ResponseCompression/src/BodyWrapperStream.cs
+++ b/src/Middleware/ResponseCompression/src/BodyWrapperStream.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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;
@@ -286,10 +286,8 @@ namespace Microsoft.AspNetCore.ResponseCompression
return _innerSendFileFeature.SendFileAsync(path, offset, count, cancellation);
}
- private async Task InnerSendFileAsync(string path, long offset, long? count, CancellationToken cancellation)
+ private Task InnerSendFileAsync(string path, long offset, long? count, CancellationToken cancellation)
{
- cancellation.ThrowIfCancellationRequested();
-
var fileInfo = new FileInfo(path);
if (offset < 0 || offset > fileInfo.Length)
{
@@ -311,9 +309,25 @@ namespace Microsoft.AspNetCore.ResponseCompression
bufferSize: bufferSize,
options: FileOptions.Asynchronous | FileOptions.SequentialScan);
+ if (cancellation.CanBeCanceled)
+ {
+ return InnerSendFileLoudAsync(fileStream, offset, count, cancellation);
+ }
+
+ return InnerSendFileQuietAsync(fileStream, offset, count, _context.RequestAborted);
+ }
+
+ private async Task InnerSendFileLoudAsync(Stream fileStream, long offset, long? count, CancellationToken cancellation)
+ {
using (fileStream)
{
- fileStream.Seek(offset, SeekOrigin.Begin);
+ cancellation.ThrowIfCancellationRequested();
+
+ if (offset > 0)
+ {
+ fileStream.Seek(offset, SeekOrigin.Begin);
+ }
+
await StreamCopyOperation.CopyToAsync(fileStream, _compressionStream, count, cancellation);
if (_autoFlush)
@@ -322,5 +336,31 @@ namespace Microsoft.AspNetCore.ResponseCompression
}
}
}
+
+ private async Task InnerSendFileQuietAsync(Stream fileStream, long offset, long? count, CancellationToken cancellation)
+ {
+ try
+ {
+ if (!cancellation.IsCancellationRequested)
+ {
+ if (offset > 0)
+ {
+ fileStream.Seek(offset, SeekOrigin.Begin);
+ }
+
+ await StreamCopyOperation.CopyToAsync(fileStream, _compressionStream, count, cancellation);
+
+ if (_autoFlush)
+ {
+ await _compressionStream.FlushAsync(cancellation);
+ }
+ }
+ }
+ catch (OperationCanceledException) { }
+ finally
+ {
+ fileStream.Dispose();
+ }
+ }
}
}
diff --git a/src/Middleware/ResponseCompression/test/BodyWrapperStreamTests.cs b/src/Middleware/ResponseCompression/test/BodyWrapperStreamTests.cs
index 4ffd70b745..15351cd428 100644
--- a/src/Middleware/ResponseCompression/test/BodyWrapperStreamTests.cs
+++ b/src/Middleware/ResponseCompression/test/BodyWrapperStreamTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// 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;
@@ -90,6 +90,22 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
Assert.Equal(File.ReadAllBytes(path), memoryStream.ToArray());
}
+ [Fact]
+ public async Task SendFileAsync_SkipsSilently_WhenRequestAborted()
+ {
+ var memoryStream = new MemoryStream();
+
+ var context = new DefaultHttpContext();
+ context.RequestAborted = new CancellationToken(canceled: true);
+ var stream = new BodyWrapperStream(context, memoryStream, new MockResponseCompressionProvider(true), null, null);
+
+ var path = "testfile1kb.txt";
+ await stream.SendFileAsync(path, 0, null, CancellationToken.None);
+ stream.Flush();
+
+ Assert.Equal(0, memoryStream.Length);
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]