diff --git a/src/Microsoft.AspNetCore.ResponseCompression/Microsoft.AspNetCore.ResponseCompression.csproj b/src/Microsoft.AspNetCore.ResponseCompression/Microsoft.AspNetCore.ResponseCompression.csproj
index d39f7db89a..7f594f1c30 100644
--- a/src/Microsoft.AspNetCore.ResponseCompression/Microsoft.AspNetCore.ResponseCompression.csproj
+++ b/src/Microsoft.AspNetCore.ResponseCompression/Microsoft.AspNetCore.ResponseCompression.csproj
@@ -9,6 +9,7 @@
+
diff --git a/src/Microsoft.AspNetCore.ResponseCompression/ResponseCompressionProvider.cs b/src/Microsoft.AspNetCore.ResponseCompression/ResponseCompressionProvider.cs
index d459c13ff3..b63ff7380e 100644
--- a/src/Microsoft.AspNetCore.ResponseCompression/ResponseCompressionProvider.cs
+++ b/src/Microsoft.AspNetCore.ResponseCompression/ResponseCompressionProvider.cs
@@ -3,8 +3,12 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.ResponseCompression.Internal;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
@@ -18,6 +22,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
private readonly HashSet _mimeTypes;
private readonly HashSet _excludedMimeTypes;
private readonly bool _enableForHttps;
+ private readonly ILogger _logger;
///
/// If no compression providers are specified then GZip is used by default.
@@ -67,6 +72,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
{
mimeTypes = ResponseCompressionDefaults.MimeTypes;
}
+
_mimeTypes = new HashSet(mimeTypes, StringComparer.OrdinalIgnoreCase);
_excludedMimeTypes = new HashSet(
@@ -75,6 +81,8 @@ namespace Microsoft.AspNetCore.ResponseCompression
);
_enableForHttps = responseCompressionOptions.EnableForHttps;
+
+ _logger = services.GetRequiredService>();
}
///
@@ -83,76 +91,86 @@ namespace Microsoft.AspNetCore.ResponseCompression
// e.g. Accept-Encoding: gzip, deflate, sdch
var accept = context.Request.Headers[HeaderNames.AcceptEncoding];
+ // Note this is already checked in CheckRequestAcceptsCompression which _should_ prevent any of these other methods from being called.
if (StringValues.IsNullOrEmpty(accept))
{
+ Debug.Assert(false, "Duplicate check failed.");
+ _logger.NoAcceptEncoding();
return null;
}
- if (StringWithQualityHeaderValue.TryParseList(accept, out var encodings))
+ if (!StringWithQualityHeaderValue.TryParseList(accept, out var encodings) || !encodings.Any())
{
- if (encodings.Count == 0)
+ _logger.NoAcceptEncoding();
+ return null;
+ }
+
+ var candidates = new HashSet();
+
+ foreach (var encoding in encodings)
+ {
+ var encodingName = encoding.Value;
+ var quality = encoding.Quality.GetValueOrDefault(1);
+
+ if (quality < double.Epsilon)
{
- return null;
+ continue;
}
- var candidates = new HashSet();
-
- foreach (var encoding in encodings)
+ for (int i = 0; i < _providers.Length; i++)
{
- var encodingName = encoding.Value;
- var quality = encoding.Quality.GetValueOrDefault(1);
+ var provider = _providers[i];
- if (quality < double.Epsilon)
+ if (StringSegment.Equals(provider.EncodingName, encodingName, StringComparison.OrdinalIgnoreCase))
{
- continue;
+ candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider));
}
+ }
+ // Uncommon but valid options
+ if (StringSegment.Equals("*", encodingName, StringComparison.Ordinal))
+ {
for (int i = 0; i < _providers.Length; i++)
{
var provider = _providers[i];
- if (StringSegment.Equals(provider.EncodingName, encodingName, StringComparison.OrdinalIgnoreCase))
- {
- candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider));
- }
+ // Any provider is a candidate.
+ candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider));
}
- // Uncommon but valid options
- if (StringSegment.Equals("*", encodingName, StringComparison.Ordinal))
- {
- for (int i = 0; i < _providers.Length; i++)
- {
- var provider = _providers[i];
-
- // Any provider is a candidate.
- candidates.Add(new ProviderCandidate(provider.EncodingName, quality, i, provider));
- }
-
- break;
- }
-
- if (StringSegment.Equals("identity", encodingName, StringComparison.OrdinalIgnoreCase))
- {
- // We add 'identity' to the list of "candidates" with a very low priority and no provider.
- // This will allow it to be ordered based on its quality (and priority) later in the method.
- candidates.Add(new ProviderCandidate(encodingName.Value, quality, priority: int.MaxValue, provider: null));
- }
+ break;
}
- if (candidates.Count <= 1)
+ if (StringSegment.Equals("identity", encodingName, StringComparison.OrdinalIgnoreCase))
{
- return candidates.ElementAtOrDefault(0).Provider;
+ // We add 'identity' to the list of "candidates" with a very low priority and no provider.
+ // This will allow it to be ordered based on its quality (and priority) later in the method.
+ candidates.Add(new ProviderCandidate(encodingName.Value, quality, priority: int.MaxValue, provider: null));
}
-
- var accepted = candidates
- .OrderByDescending(x => x.Quality)
- .ThenBy(x => x.Priority)
- .First();
-
- return accepted.Provider;
}
- return null;
+ ICompressionProvider selectedProvider = null;
+ if (candidates.Count <= 1)
+ {
+ selectedProvider = candidates.FirstOrDefault().Provider;
+ }
+ else
+ {
+ selectedProvider = candidates
+ .OrderByDescending(x => x.Quality)
+ .ThenBy(x => x.Priority)
+ .First().Provider;
+ }
+
+ if (selectedProvider == null)
+ {
+ // "identity" would match as a candidate but not have a provider implementation
+ _logger.NoCompressionProvider();
+ return null;
+ }
+
+ _logger.CompressingWith(selectedProvider.EncodingName);
+ return selectedProvider;
}
///
@@ -160,11 +178,13 @@ namespace Microsoft.AspNetCore.ResponseCompression
{
if (context.Response.Headers.ContainsKey(HeaderNames.ContentRange))
{
+ _logger.NoCompressionDueToHeader(HeaderNames.ContentRange);
return false;
}
if (context.Response.Headers.ContainsKey(HeaderNames.ContentEncoding))
{
+ _logger.NoCompressionDueToHeader(HeaderNames.ContentEncoding);
return false;
}
@@ -172,6 +192,7 @@ namespace Microsoft.AspNetCore.ResponseCompression
if (string.IsNullOrEmpty(mimeType))
{
+ _logger.NoCompressionForContentType(mimeType);
return false;
}
@@ -183,9 +204,18 @@ namespace Microsoft.AspNetCore.ResponseCompression
mimeType = mimeType.Trim();
}
- return ShouldCompressExact(mimeType) //check exact match type/subtype
+ var shouldCompress = ShouldCompressExact(mimeType) //check exact match type/subtype
?? ShouldCompressPartial(mimeType) //check partial match type/*
?? _mimeTypes.Contains("*/*"); //check wildcard */*
+
+ if (shouldCompress)
+ {
+ _logger.ShouldCompressResponse(); // Trace, there will be more logs
+ return true;
+ }
+
+ _logger.NoCompressionForContentType(mimeType);
+ return false;
}
///
@@ -193,9 +223,18 @@ namespace Microsoft.AspNetCore.ResponseCompression
{
if (context.Request.IsHttps && !_enableForHttps)
{
+ _logger.NoCompressionForHttps();
return false;
}
- return !string.IsNullOrEmpty(context.Request.Headers[HeaderNames.AcceptEncoding]);
+
+ if (string.IsNullOrEmpty(context.Request.Headers[HeaderNames.AcceptEncoding]))
+ {
+ _logger.NoAcceptEncoding();
+ return false;
+ }
+
+ _logger.RequestAcceptsCompression(); // Trace, there will be more logs
+ return true;
}
private bool? ShouldCompressExact(string mimeType)
diff --git a/src/Microsoft.AspNetCore.ResponseCompression/internal/ResponseCompressionLoggingExtensions.cs b/src/Microsoft.AspNetCore.ResponseCompression/internal/ResponseCompressionLoggingExtensions.cs
new file mode 100644
index 0000000000..75f3d07fd3
--- /dev/null
+++ b/src/Microsoft.AspNetCore.ResponseCompression/internal/ResponseCompressionLoggingExtensions.cs
@@ -0,0 +1,72 @@
+// 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 Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.ResponseCompression.Internal
+{
+ internal static class ResponseCompressionLoggingExtensions
+ {
+ private static readonly Action _noAcceptEncoding;
+ private static readonly Action _noCompressionForHttps;
+ private static readonly Action _requestAcceptsCompression;
+ private static readonly Action _noCompressionDueToHeader;
+ private static readonly Action _noCompressionForContentType;
+ private static readonly Action _shouldCompressResponse;
+ private static readonly Action _noCompressionProvider;
+ private static readonly Action _compressWith;
+
+ static ResponseCompressionLoggingExtensions()
+ {
+ _noAcceptEncoding = LoggerMessage.Define(LogLevel.Debug, 1, "No response compression available, the Accept-Encoding header is missing or invalid.");
+ _noCompressionForHttps = LoggerMessage.Define(LogLevel.Debug, 2, "No response compression available for HTTPS requests. See ResponseCompressionOptions.EnableForHttps.");
+ _requestAcceptsCompression = LoggerMessage.Define(LogLevel.Trace, 3, "This request accepts compression.");
+ _noCompressionDueToHeader = LoggerMessage.Define(LogLevel.Debug, 4, "Response compression disabled due to the {header} header.");
+ _noCompressionForContentType = LoggerMessage.Define(LogLevel.Debug, 5, "Response compression is not enabled for the Content-Type '{header}'.");
+ _shouldCompressResponse = LoggerMessage.Define(LogLevel.Trace, 6, "Response compression is available for this Content-Type.");
+ _noCompressionProvider = LoggerMessage.Define(LogLevel.Debug, 7, "No matching response compression provider found.");
+ _compressWith = LoggerMessage.Define(LogLevel.Debug, 8, "The response will be compressed with '{provider}'.");
+ }
+
+ public static void NoAcceptEncoding(this ILogger logger)
+ {
+ _noAcceptEncoding(logger, null);
+ }
+
+ public static void NoCompressionForHttps(this ILogger logger)
+ {
+ _noCompressionForHttps(logger, null);
+ }
+
+ public static void RequestAcceptsCompression(this ILogger logger)
+ {
+ _requestAcceptsCompression(logger, null);
+ }
+
+ public static void NoCompressionDueToHeader(this ILogger logger, string header)
+ {
+ _noCompressionDueToHeader(logger, header, null);
+ }
+
+ public static void NoCompressionForContentType(this ILogger logger, string header)
+ {
+ _noCompressionForContentType(logger, header, null);
+ }
+
+ public static void ShouldCompressResponse(this ILogger logger)
+ {
+ _shouldCompressResponse(logger, null);
+ }
+
+ public static void NoCompressionProvider(this ILogger logger)
+ {
+ _noCompressionProvider(logger, null);
+ }
+
+ public static void CompressingWith(this ILogger logger, string provider)
+ {
+ _compressWith(logger, provider, null);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.ResponseCompression.Tests/Microsoft.AspNetCore.ResponseCompression.Tests.csproj b/test/Microsoft.AspNetCore.ResponseCompression.Tests/Microsoft.AspNetCore.ResponseCompression.Tests.csproj
index 902449b486..b979c66a00 100644
--- a/test/Microsoft.AspNetCore.ResponseCompression.Tests/Microsoft.AspNetCore.ResponseCompression.Tests.csproj
+++ b/test/Microsoft.AspNetCore.ResponseCompression.Tests/Microsoft.AspNetCore.ResponseCompression.Tests.csproj
@@ -14,6 +14,7 @@
+
diff --git a/test/Microsoft.AspNetCore.ResponseCompression.Tests/ResponseCompressionMiddlewareTest.cs b/test/Microsoft.AspNetCore.ResponseCompression.Tests/ResponseCompressionMiddlewareTest.cs
index 08fa0b283e..8cf9d3bf1e 100644
--- a/test/Microsoft.AspNetCore.ResponseCompression.Tests/ResponseCompressionMiddlewareTest.cs
+++ b/test/Microsoft.AspNetCore.ResponseCompression.Tests/ResponseCompressionMiddlewareTest.cs
@@ -15,6 +15,8 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
using Xunit;
@@ -55,28 +57,31 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[Fact]
public async Task Request_NoAcceptEncoding_Uncompressed()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: null, responseType: TextPlain);
+ 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.");
}
[Fact]
public async Task Request_AcceptGzipDeflate_CompressedGzip()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "gzip", "deflate" }, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "gzip", "deflate" }, responseType: TextPlain);
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
}
[Fact]
public async Task Request_AcceptBrotli_CompressedBrotli()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "br" }, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "br" }, responseType: TextPlain);
#if NET461
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
#elif NETCOREAPP2_2
CheckResponseCompressed(response, expectedBodyLength: 20, expectedEncoding: "br");
+ AssertCompressedWithLog(logMessages, "br");
#else
#error Target frameworks need to be updated.
#endif
@@ -87,12 +92,14 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[InlineData("br", "gzip")]
public async Task Request_AcceptMixed_CompressedBrotli(string encoding1, string encoding2)
{
- var response = await InvokeMiddleware(100, new[] { encoding1, encoding2 }, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, new[] { encoding1, encoding2 }, responseType: TextPlain);
#if NET461
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
#elif NETCOREAPP2_2
CheckResponseCompressed(response, expectedBodyLength: 20, expectedEncoding: "br");
+ AssertCompressedWithLog(logMessages, "br");
#else
#error Target frameworks need to be updated.
#endif
@@ -110,9 +117,10 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
options.Providers.Add();
}
- var response = await InvokeMiddleware(100, new[] { encoding1, encoding2 }, responseType: TextPlain, configure: Configure);
+ var (response, logMessages) = await InvokeMiddleware(100, new[] { encoding1, encoding2 }, responseType: TextPlain, configure: Configure);
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
}
#elif NET461
#else
@@ -122,9 +130,13 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[Fact]
public async Task Request_AcceptUnknown_NotCompressed()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "unknown" }, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "unknown" }, responseType: TextPlain);
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
+ Assert.Equal(3, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Trace, "Response compression is available for this Content-Type.");
+ AssertLog(logMessages.Skip(2).First(), LogLevel.Debug, "No matching response compression provider found.");
}
[Theory]
@@ -134,31 +146,10 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[InlineData("text/plain ; charset=ISO-8859-4")]
public async Task ContentType_WithCharset_Compress(string contentType)
{
- var builder = new WebHostBuilder()
- .ConfigureServices(services =>
- {
- services.AddResponseCompression();
- })
- .Configure(app =>
- {
- app.UseResponseCompression();
- app.Run(context =>
- {
- context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
- context.Response.ContentType = contentType;
- return context.Response.WriteAsync(new string('a', 100));
- });
- });
-
- var server = new TestServer(builder);
- var client = server.CreateClient();
-
- var request = new HttpRequestMessage(HttpMethod.Get, "");
- request.Headers.AcceptEncoding.ParseAdd("gzip");
-
- var response = await client.SendAsync(request);
+ var (response, logMessages) = await InvokeMiddleware(uncompressedBodyLength: 100, requestAcceptEncodings: new[] { "gzip" }, contentType);
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
}
[Fact]
@@ -197,31 +188,13 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[InlineData("text/plain2")]
public async Task MimeTypes_OtherContentTypes_NoMatch(string contentType)
{
- var builder = new WebHostBuilder()
- .ConfigureServices(services =>
- {
- services.AddResponseCompression();
- })
- .Configure(app =>
- {
- app.UseResponseCompression();
- app.Run(context =>
- {
- context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
- context.Response.ContentType = contentType;
- return context.Response.WriteAsync(new string('a', 100));
- });
- });
-
- var server = new TestServer(builder);
- var client = server.CreateClient();
-
- var request = new HttpRequestMessage(HttpMethod.Get, "");
- request.Headers.AcceptEncoding.ParseAdd("gzip");
-
- var response = await client.SendAsync(request);
+ var (response, logMessages) = await InvokeMiddleware(uncompressedBodyLength: 100, requestAcceptEncodings: new[] { "gzip" }, contentType);
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false);
+ Assert.Equal(2, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ var expected = string.IsNullOrEmpty(contentType) ? "(null)" : contentType;
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Debug, $"Response compression is not enabled for the Content-Type '{expected}'.");
}
[Theory]
@@ -287,88 +260,42 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
public async Task MimeTypes_IncludedAndExcluded(
string[] mimeTypes,
string[] excludedMimeTypes,
- string mimeType,
+ string contentType,
bool compress
)
{
- var builder = new WebHostBuilder()
- .ConfigureServices(
- services =>
- services.AddResponseCompression(
- options =>
- {
- options.MimeTypes = mimeTypes;
- options.ExcludedMimeTypes = excludedMimeTypes;
- }
- )
- )
- .Configure(
- app =>
- {
- app.UseResponseCompression();
- app.Run(
- context =>
- {
- context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
- context.Response.ContentType = mimeType;
- return context.Response.WriteAsync(new string('a', 100));
- }
- );
- }
- );
-
- var server = new TestServer(builder);
- var client = server.CreateClient();
-
- var request = new HttpRequestMessage(HttpMethod.Get, "");
- request.Headers.AcceptEncoding.ParseAdd("gzip");
-
- var response = await client.SendAsync(request);
+ var (response, logMessages) = await InvokeMiddleware(uncompressedBodyLength: 100, requestAcceptEncodings: new[] { "gzip" }, contentType,
+ configure: options =>
+ {
+ options.MimeTypes = mimeTypes;
+ options.ExcludedMimeTypes = excludedMimeTypes;
+ });
if (compress)
{
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
}
else
{
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false);
+ Assert.Equal(2, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Debug, $"Response compression is not enabled for the Content-Type '{contentType}'.");
}
}
[Fact]
public async Task NoIncludedMimeTypes_UseDefaults()
{
- var builder = new WebHostBuilder()
- .ConfigureServices(
- services =>
- services.AddResponseCompression(
- options => options.ExcludedMimeTypes = new[] { "text/*" }
- )
- )
- .Configure(
- app =>
- {
- app.UseResponseCompression();
- app.Run(
- context =>
- {
- context.Response.Headers[HeaderNames.ContentMD5] = "MD5";
- context.Response.ContentType = TextPlain;
- return context.Response.WriteAsync(new string('a', 100));
- }
- );
- }
- );
-
- var server = new TestServer(builder);
- var client = server.CreateClient();
-
- var request = new HttpRequestMessage(HttpMethod.Get, "");
- request.Headers.AcceptEncoding.ParseAdd("gzip");
-
- var response = await client.SendAsync(request);
+ var (response, logMessages) = await InvokeMiddleware(uncompressedBodyLength: 100, requestAcceptEncodings: new[] { "gzip" }, TextPlain,
+ configure: options =>
+ {
+ options.ExcludedMimeTypes = new[] { "text/*" };
+ });
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
}
[Theory]
@@ -410,12 +337,14 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[Fact]
public async Task Request_AcceptStar_Compressed()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "*" }, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "*" }, responseType: TextPlain);
#if NET461
CheckResponseCompressed(response, expectedBodyLength: 24, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
#elif NETCOREAPP2_2
CheckResponseCompressed(response, expectedBodyLength: 20, expectedEncoding: "br");
+ AssertCompressedWithLog(logMessages, "br");
#else
#error Target frameworks need to be updated.
#endif
@@ -424,9 +353,13 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[Fact]
public async Task Request_AcceptIdentity_NotCompressed()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "identity" }, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "identity" }, responseType: TextPlain);
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: true);
+ Assert.Equal(3, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Trace, "Response compression is available for this Content-Type.");
+ AssertLog(logMessages.Skip(2).First(), LogLevel.Debug, "No matching response compression provider found.");
}
[Theory]
@@ -435,37 +368,48 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[InlineData(new[] { "identity;q=0.5", "gzip" }, 24)]
public async Task Request_AcceptWithHigherCompressionQuality_Compressed(string[] acceptEncodings, int expectedBodyLength)
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
CheckResponseCompressed(response, expectedBodyLength: expectedBodyLength, expectedEncoding: "gzip");
+ AssertCompressedWithLog(logMessages, "gzip");
}
[Theory]
[InlineData(new[] { "gzip;q=0.5", "identity;q=0.8" }, 100)]
public async Task Request_AcceptWithhigherIdentityQuality_NotCompressed(string[] acceptEncodings, int expectedBodyLength)
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: acceptEncodings, responseType: TextPlain);
CheckResponseNotCompressed(response, expectedBodyLength: expectedBodyLength, sendVaryHeader: true);
+ Assert.Equal(3, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Trace, "Response compression is available for this Content-Type.");
+ AssertLog(logMessages.Skip(2).First(), LogLevel.Debug, "No matching response compression provider found.");
}
[Fact]
public async Task Response_UnknownMimeType_NotCompressed()
{
- var response = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "gzip" }, responseType: "text/custom");
+ var (response, logMessages) = await InvokeMiddleware(100, requestAcceptEncodings: new[] { "gzip" }, responseType: "text/custom");
CheckResponseNotCompressed(response, expectedBodyLength: 100, sendVaryHeader: false);
+ Assert.Equal(2, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Debug, "Response compression is not enabled for the Content-Type 'text/custom'.");
}
[Fact]
public async Task Response_WithContentRange_NotCompressed()
{
- var response = await InvokeMiddleware(50, requestAcceptEncodings: new[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
+ var (response, logMessages) = await InvokeMiddleware(50, requestAcceptEncodings: new[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
{
r.Headers[HeaderNames.ContentRange] = "1-2/*";
});
CheckResponseNotCompressed(response, expectedBodyLength: 50, sendVaryHeader: false);
+ Assert.Equal(2, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Debug, "Response compression disabled due to the Content-Range header.");
}
@@ -474,7 +418,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
{
var otherContentEncoding = "something";
- var response = await InvokeMiddleware(50, requestAcceptEncodings: new[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
+ var (response, logMessages) = await InvokeMiddleware(50, requestAcceptEncodings: new[] { "gzip" }, responseType: TextPlain, addResponseAction: (r) =>
{
r.Headers[HeaderNames.ContentEncoding] = otherContentEncoding;
});
@@ -482,6 +426,9 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
Assert.True(response.Content.Headers.ContentEncoding.Contains(otherContentEncoding));
Assert.False(response.Content.Headers.ContentEncoding.Contains("gzip"));
Assert.Equal(50, response.Content.Headers.ContentLength);
+ Assert.Equal(2, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Debug, "Response compression disabled due to the Content-Encoding header.");
}
[Theory]
@@ -489,9 +436,15 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
[InlineData(true, 24)]
public async Task Request_Https_CompressedIfEnabled(bool enableHttps, int expectedLength)
{
+ var sink = new TestSink(
+ TestSink.EnableWithTypeName,
+ TestSink.EnableWithTypeName);
+ var loggerFactory = new TestLoggerFactory(sink, enabled: true);
+
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
+ services.AddSingleton(loggerFactory);
services.AddResponseCompression(options =>
{
options.EnableForHttps = enableHttps;
@@ -521,6 +474,16 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
var response = await client.SendAsync(request);
Assert.Equal(expectedLength, response.Content.ReadAsByteArrayAsync().Result.Length);
+
+ var logMessages = sink.Writes.ToList();
+ if (enableHttps)
+ {
+ AssertCompressedWithLog(logMessages, "gzip");
+ }
+ else
+ {
+ AssertLog(logMessages.Single(), LogLevel.Debug, "No response compression available for HTTPS requests. See ResponseCompressionOptions.EnableForHttps.");
+ }
}
[Theory]
@@ -985,17 +948,23 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
Assert.False(fakeSendFile.Invoked);
}
- private Task InvokeMiddleware(
+ private async Task<(HttpResponseMessage, List)> InvokeMiddleware(
int uncompressedBodyLength,
string[] requestAcceptEncodings,
string responseType,
Action addResponseAction = null,
Action configure = null)
{
+ var sink = new TestSink(
+ TestSink.EnableWithTypeName,
+ TestSink.EnableWithTypeName);
+ var loggerFactory = new TestLoggerFactory(sink, enabled: true);
+
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddResponseCompression(configure ?? (_ => { }));
+ services.AddSingleton(loggerFactory);
})
.Configure(app =>
{
@@ -1019,7 +988,7 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
request.Headers.AcceptEncoding.Add(System.Net.Http.Headers.StringWithQualityHeaderValue.Parse(requestAcceptEncodings[i]));
}
- return client.SendAsync(request);
+ return (await client.SendAsync(request), sink.Writes.ToList());
}
private void CheckResponseCompressed(HttpResponseMessage response, int expectedBodyLength, string expectedEncoding)
@@ -1063,6 +1032,20 @@ namespace Microsoft.AspNetCore.ResponseCompression.Tests
Assert.Equal(expectedBodyLength, response.Content.Headers.ContentLength);
}
+ private void AssertLog(WriteContext log, LogLevel level, string message)
+ {
+ Assert.Equal(level, log.LogLevel);
+ Assert.Equal(message, log.State.ToString());
+ }
+
+ private void AssertCompressedWithLog(List logMessages, string provider)
+ {
+ Assert.Equal(3, logMessages.Count);
+ AssertLog(logMessages.First(), LogLevel.Trace, "This request accepts compression.");
+ AssertLog(logMessages.Skip(1).First(), LogLevel.Trace, "Response compression is available for this Content-Type.");
+ AssertLog(logMessages.Skip(2).First(), LogLevel.Debug, $"The response will be compressed with '{provider}'.");
+ }
+
private class FakeSendFileFeature : IHttpSendFileFeature
{
private readonly Stream _innerBody;