Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/Middleware/ResponseCompression/src/ResponseCompressionBody.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ internal sealed class ResponseCompressionBody : Stream, IHttpResponseBodyFeature
private bool _providerCreated;
private bool _autoFlush;
private bool _complete;
private readonly bool _allowCompression;

internal ResponseCompressionBody(HttpContext context, IResponseCompressionProvider provider,
IHttpResponseBodyFeature innerBodyFeature)
IHttpResponseBodyFeature innerBodyFeature, bool allowCompression)
{
_context = context;
_provider = provider;
_innerBodyFeature = innerBodyFeature;
_innerStream = innerBodyFeature.Stream;
_allowCompression = allowCompression;
}

internal async Task FinishCompressionAsync()
Expand Down Expand Up @@ -224,17 +226,22 @@ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, Cancella
headers.Vary = StringValues.Concat(headers.Vary, HeaderNames.AcceptEncoding);
}

var compressionProvider = ResolveCompressionProvider();
if (compressionProvider != null)
// Only attempt compression when the request includes Accept-Encoding.
// The Vary header is always added above for correct caching behavior.
if (_allowCompression)
{
// 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.ContentEncoding = StringValues.Concat(headers.ContentEncoding, compressionProvider.EncodingName);
headers.ContentMD5 = default; // Reset the MD5 because the content changed.
headers.ContentLength = default;
}
var compressionProvider = ResolveCompressionProvider();
if (compressionProvider != null)
{
// 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.ContentEncoding = StringValues.Concat(headers.ContentEncoding, compressionProvider.EncodingName);
headers.ContentMD5 = default; // Reset the MD5 because the content changed.
headers.ContentLength = default;
}

return compressionProvider;
return compressionProvider;
}
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,17 @@ public ResponseCompressionMiddleware(RequestDelegate next, IResponseCompressionP
/// <returns>A task that represents the execution of this middleware.</returns>
public Task Invoke(HttpContext context)
{
if (!_provider.CheckRequestAcceptsCompression(context))
{
return _next(context);
}
return InvokeCore(context);
return InvokeCore(context, _provider.CheckRequestAcceptsCompression(context));
}

private async Task InvokeCore(HttpContext context)
private async Task InvokeCore(HttpContext context, bool allowCompression)
{
var originalBodyFeature = context.Features.Get<IHttpResponseBodyFeature>();
var originalCompressionFeature = context.Features.Get<IHttpsCompressionFeature>();

Debug.Assert(originalBodyFeature != null);

var compressionBody = new ResponseCompressionBody(context, _provider, originalBodyFeature);
var compressionBody = new ResponseCompressionBody(context, _provider, originalBodyFeature, allowCompression);
context.Features.Set<IHttpResponseBodyFeature>(compressionBody);
context.Features.Set<IHttpsCompressionFeature>(compressionBody);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public void OnWrite_AppendsAcceptEncodingToVaryHeader_IfNotPresent(string provid
{
var httpContext = new DefaultHttpContext();
httpContext.Response.Headers.Vary = providedVaryHeader;
var stream = new ResponseCompressionBody(httpContext, new MockResponseCompressionProvider(flushable: true), new StreamResponseBodyFeature(new MemoryStream()));
var stream = new ResponseCompressionBody(httpContext, new MockResponseCompressionProvider(flushable: true), new StreamResponseBodyFeature(new MemoryStream()), allowCompression: true);

stream.Write(new byte[] { }, 0, 0);

Expand All @@ -34,7 +34,7 @@ public void Write_IsPassedToUnderlyingStream_WhenDisableResponseBuffering(bool f
var buffer = new byte[] { 1 };

var memoryStream = new MemoryStream();
var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(flushable), new StreamResponseBodyFeature(memoryStream));
var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(flushable), new StreamResponseBodyFeature(memoryStream), allowCompression: true);

stream.DisableBuffering();
stream.Write(buffer, 0, buffer.Length);
Expand All @@ -50,7 +50,7 @@ public async Task WriteAsync_IsPassedToUnderlyingStream_WhenDisableResponseBuffe
var buffer = new byte[] { 1 };

var memoryStream = new MemoryStream();
var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(flushable), new StreamResponseBodyFeature(memoryStream));
var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(flushable), new StreamResponseBodyFeature(memoryStream), allowCompression: true);

stream.DisableBuffering();
await stream.WriteAsync(buffer, 0, buffer.Length);
Expand All @@ -63,7 +63,7 @@ public async Task SendFileAsync_IsPassedToUnderlyingStream_WhenDisableResponseBu
{
var memoryStream = new MemoryStream();

var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(true), new StreamResponseBodyFeature(memoryStream));
var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(true), new StreamResponseBodyFeature(memoryStream), allowCompression: true);

stream.DisableBuffering();

Expand All @@ -82,7 +82,7 @@ public void BeginWrite_IsPassedToUnderlyingStream_WhenDisableResponseBuffering(b

var memoryStream = new MemoryStream();

var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(flushable), new StreamResponseBodyFeature(memoryStream));
var stream = new ResponseCompressionBody(new DefaultHttpContext(), new MockResponseCompressionProvider(flushable), new StreamResponseBodyFeature(memoryStream), allowCompression: true);

stream.DisableBuffering();
stream.BeginWrite(buffer, 0, buffer.Length, (o) => { }, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ public async Task Request_NoAcceptEncoding_Uncompressed()
{
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.Contains(logMessages, lm => lm.LogLevel == LogLevel.Debug && lm.State.ToString().Contains("Accept-Encoding header is missing or invalid"));
}

[Fact]
Expand Down Expand Up @@ -136,10 +136,9 @@ public async Task RequestHead_NoAcceptEncoding_Uncompressed()
{
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.Contains(logMessages, lm => lm.LogLevel == LogLevel.Debug && lm.State.ToString().Contains("Accept-Encoding header is missing or invalid"));
}

[Fact]
public async Task RequestHead_AcceptGzipDeflate_CompressedGzip()
{
Expand Down
Loading