diff --git a/src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs b/src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs index 2eefdb6482b8..671c81231043 100644 --- a/src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs +++ b/src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs @@ -196,15 +196,18 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella } /// - /// Checks if the response should be compressed and sets the response headers. + /// Examines the response on first write to see if compression should be used and if true sets the Vary Accept-Encoding header. /// - /// The compression provider to use if compression is enabled, otherwise null. - private ICompressionProvider? InitializeCompressionHeaders() + /// current response compression provider + /// The . + /// if the response should be compressed, otherwise . + internal static bool ShouldCompressResponseCommon(IResponseCompressionProvider provider, HttpContext context) { - if (_provider.ShouldCompressResponse(_context)) + var result = provider.ShouldCompressResponse(context); + + if (result) { - var headers = _context.Response.Headers; - // If the MIME type indicates that the response could be compressed, caches will need to vary by the Accept-Encoding header + var headers = context.Response.Headers; var varyValues = headers.GetCommaSeparatedValues(HeaderNames.Vary); var varyByAcceptEncoding = false; @@ -219,10 +222,22 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella if (!varyByAcceptEncoding) { - // Can't use += as StringValues does not override operator+ - // and the implict conversions will cause an incorrect string concat https://github.com/dotnet/runtime/issues/52507 headers.Vary = StringValues.Concat(headers.Vary, HeaderNames.AcceptEncoding); } + } + + return result; + } + + /// + /// Checks if the response should be compressed and sets the response headers. + /// + /// The compression provider to use if compression is enabled, otherwise null. + private ICompressionProvider? InitializeCompressionHeaders() + { + if (ShouldCompressResponseCommon(_provider, _context)) + { + var headers = _context.Response.Headers; var compressionProvider = ResolveCompressionProvider(); if (compressionProvider != null) diff --git a/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs b/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs index 22a9677d6e10..519c508f5187 100644 --- a/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs +++ b/src/Middleware/ResponseCompression/src/ResponseCompressionMiddleware.cs @@ -38,11 +38,19 @@ public Task Invoke(HttpContext context) { if (!_provider.CheckRequestAcceptsCompression(context)) { + var originalResponseFeature = context.Features.GetRequiredFeature(); + originalResponseFeature.OnStarting(OnStartingResponseHandler, context); return _next(context); } return InvokeCore(context); } + private async Task OnStartingResponseHandler(object state) + { + HttpContext context = (HttpContext)state; + ResponseCompressionBody.ShouldCompressResponseCommon(_provider, context); + } + private async Task InvokeCore(HttpContext context) { var originalBodyFeature = context.Features.Get(); diff --git a/src/Middleware/ResponseCompression/test/ResponseCompressionMiddlewareTest.cs b/src/Middleware/ResponseCompression/test/ResponseCompressionMiddlewareTest.cs index 4f1d1def09fc..156ad4ae22a3 100644 --- a/src/Middleware/ResponseCompression/test/ResponseCompressionMiddlewareTest.cs +++ b/src/Middleware/ResponseCompression/test/ResponseCompressionMiddlewareTest.cs @@ -41,12 +41,14 @@ public void Options_HttpsDisabledByDefault() } [Fact] - public async Task Request_NoAcceptEncoding_Uncompressed() + public async Task Request_NoAcceptEncoding_Uncompressed_WithVaryHeader() { var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: null, responseType: TextPlain); - CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false); - AssertLog(logMessages.Single(), LogLevel.Debug, "No response compression available, the Accept-Encoding header is missing or invalid."); + CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true); + Assert.Equal(2, logMessages.Count); + AssertLog(logMessages.First(), LogLevel.Debug, "No response compression available, the Accept-Encoding header is missing or invalid."); + AssertLog(logMessages.Skip(1).First(), LogLevel.Trace, "Response compression is available for this Content-Type."); } [Fact] @@ -132,12 +134,14 @@ public async Task Request_AcceptUnknown_NotCompressed() } [Fact] - public async Task RequestHead_NoAcceptEncoding_Uncompressed() + public async Task RequestHead_NoAcceptEncoding_Uncompressed_WithVaryHeader() { var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: null, responseType: TextPlain, httpMethod: HttpMethods.Head); - CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false); - AssertLog(logMessages.Single(), LogLevel.Debug, "No response compression available, the Accept-Encoding header is missing or invalid."); + CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true); + Assert.Equal(2, logMessages.Count); + AssertLog(logMessages.First(), LogLevel.Debug, "No response compression available, the Accept-Encoding header is missing or invalid."); + AssertLog(logMessages.Skip(1).First(), LogLevel.Trace, "Response compression is available for this Content-Type."); } [Fact]