-
Notifications
You must be signed in to change notification settings - Fork 84
Conversation
Hi @Yves57, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! The agreement was validated by .NET Foundation and real humans are currently evaluating your PR. TTYL, DNFBOT; |
|
||
using (var uncompressedStream = new MemoryStream()) | ||
{ | ||
context.Response.Body = uncompressedStream; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fully buffering all responses is not viable. We'll need to wrap the Response.Body stream and do the compression check on the first write.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any special test to add, or I can assume that in any cases the MIME type is available when the first byte is written in the body Stream? In understand that the header is written before the body, but I want to be sure that there are not special cases.
else | ||
{ | ||
context.Response.Headers[HeaderNames.ContentEncoding] = compressionProvider.EncodingName; | ||
context.Response.Headers[HeaderNames.ContentMD5] = StringValues.Empty; // Reset the MD5 because the content changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Headers.Remove...
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Extensions.Primitives; | ||
using Microsoft.Net.Http.Headers; | ||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put System.* ahead of other namespaces.
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath> | ||
</PropertyGroup> | ||
<PropertyGroup Label="Configuration"> | ||
<RootNamespace>C:\ws\github\BasicMiddleware\samples\RewriteSample\Properties\AssemblyInfo.csResponseCompressionSample</RootNamespace> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove?
@Yves57 Can you get some basic performance data when this middleware is active and doing its job and when it's not since it always wraps the stream. |
_level = level; | ||
} | ||
|
||
public string EncodingName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: follow form of property can make the code more condense and readable
public string EncodingName { get; } = "gzip";
I'd like to see a flag, which is set by default, so compression doesn't happen if the request protocol is HTTPS to combat variations on Beast, Crime et al. |
@DamianEdwards @muratg Are we actually planning to accept/ship this as an official package or should this be moved to contrib? |
If you're shipping it then I definitely want my flag :D |
I'm more than happy to accept this from a function/product point of view (modulo usual API, security, perf, engineering reviews, etc.) |
For the HTTPS flag, is |
@davidfowl I have used the
The results on my computer:
I will push the perf test source code in my next PR (I can remove it later if you want). |
How does it perform with Kestrel? |
Other test:
On my computer (so all in local) with 1 connection:
On my computer (so all in local) with 4 connections:
So the stream wrapper seems to be more or less "free" in term of performance, even if I'm a little bit surprised by the small difference between compression/none with 4 connections. |
- New 'EnableHttps' flag - Small source code feedbacks
I'm going to be building a Request Decompression middleware (I work for a company that sends large JSON logs from Android apps in rural Africa where GPRS internet connectivity is low and compression would make a massive difference). Request Decompression is essentially going to be a mirror image of Response Compression (Decompress, instead of Compress). Is there any appetite for extending this PR or starting a new PR based on this one? IMHO, I'm really surprised that Request Decompression is not a thing in .NET or IIS and even more surprised that I've never wondered why it was missing. No easy built in option for HttpClient either. |
MIME TypesThe MIME types have no default value, I propose the following list which only includes standard MIME types where possible and no common MIME type synonyms like
You'd probably also want to use the above list for Request Compression, although the option to be different should still be there. Minimum Size OptionIIS and NGINX provide an options which lets you specify a minimum size before compression is applied. This option should be added to the NGINX also provides other options which I don't think are as important but should be considered:
|
@davidfowl @Tratcher I think the most important information in that
I think a good optimization would be to add a method |
I had to resort using the following MIME type statement instead of just
|
@dodyg Are you sure that you have the latest version of the PR? The extension method with only MIME types does not exist anymore. And I just tried your sample without any problem. |
] | ||
}, | ||
"dependencies": { | ||
"Microsoft.AspNetCore.Http": "1.1.0-*", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Middleware shouldn't need to reference Http directly. What are you using from here that isn't in abstractions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, "Http.Abstractions" is enough. I fix it.
@Yves57 ah my fault. It works now.
|
Just for reference, compression via middleware is available currently in WebMarkupMin.AspNetCore1 - I haven't compared the implementations, but there may be something of interest pertinent to this PR? |
"commandName": "IISExpress", | ||
"launchBrowser": true, | ||
"environmentVariables": { | ||
"ASPNET_ENV": "Development" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ASPNETCORE_ENVIRONMENT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will fix, but just for information "ResponseBufferingSample" has the same error 😄 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to fix the ResponseBufferingSample too :-)
"web": { | ||
"commandName": "web", | ||
"environmentVariables": { | ||
"Hosting:Environment": "Development" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ASPNETCORE_ENVIRONMENT
IsMimeTypeCompressable(_response.ContentType); // MIME type in the authorized list | ||
} | ||
|
||
private bool IsMimeTypeCompressable(string mimeType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be extracted to a generic ShouldCompressResponse callback on Options for more customizable control. Then we'd have an implementation based on mime types.
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.AspNetCore.ResponseCompression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IApplicationBuilder extensions go into the Microsoft.AspNetCore.Builder namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes we'll move the Options there too, but we're less consistent about that.
/// 'False' to enable compression only on HTTP request. Enable compression on HTTPS request | ||
/// may lead to security problems. | ||
/// </summary> | ||
public bool EnableHttps { get; set; } = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blowdart should this option even be available?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No it shouldn't. Remove.
], | ||
"xmlDoc": true | ||
}, | ||
"description": "ASP.NET Core basic middleware for HTTP Response compression.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove basic
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyConfiguration("")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert.Equal(expectedLength, httpContext.Response.Body.Length); | ||
} | ||
|
||
private async Task<HttpResponse> InvokeMiddleware(int uncompressedBodyLength, string requestAcceptEncoding, string responseType, Action<HttpResponse> addResponseAction = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use TestServer to arrange this. See https://github.com/aspnet/BasicMiddleware/blob/dev/test/Microsoft.AspNetCore.HttpOverrides.Tests/ForwardedHeadersMiddlewareTest.cs#L22
|
||
public override Task FlushAsync(CancellationToken cancellationToken) | ||
{ | ||
if (_compressionStream != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to call OnWrite() if the user is just trying to flush the headers
|
||
public override void Flush() | ||
{ | ||
if (_compressionStream != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnWrite();
- Replace MIME types filter by a more generic delegate
/// 'False' to enable compression only on HTTP requests. Enable compression on HTTPS requests | ||
/// may lead to security problems. | ||
/// </summary> | ||
public bool EnableHttps { get; set; } = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this offline. It can stay, but will remain off by default. bing, google, msn, and wikipedia all enable this, so we can't outright prohibit it.
@RehanSaeed there are a few different issues with request compression. A) There's no opportunity for the client and server to negotiate the compression protocol, the client needs to know in advance what the server supports. B) the server may not need to decompress the body for a given operation. E.g. for your compressed log files, what if the web server only wanted to save them to disk in their compressed format. Let's not try to put these two behaviors into one middleware. However, there may be some re-usable components between the two. You can check with @DamianEdwards, but I don't anticipate accepting a contribution for request decompression. https://github.com/aspnet-contrib may be a better place for it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the update, it looks pretty good. The options still need some refinements, but I'll merge it and take over from here.
Rebased, squashed, and merged. |
A first PR proposition about #34
Included:
Pending:
MemoryStream
management.MinimumSize
option?