Skip to content
This repository was archived by the owner on Sep 9, 2022. It is now read-only.

Commit 717f386

Browse files
committed
Simplified usage, and added support for setting a minimum content size for compressing.
1 parent de55daa commit 717f386

20 files changed

+848
-239
lines changed

README.md

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ Drop-in module for ASP.Net WebAPI that enables `GZip` and `Deflate` support
99
You need to add the compression handler as the last applied message handler on outgoing requests, and the first one on incoming requests.
1010
To do that, just add the following line to your `App_Start\WebApiConfig.cs` file after adding all your other message handlers:
1111
```csharp
12-
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new CompressionHandler(new GZipCompressor(), new DeflateCompressor()));
12+
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));
1313
```
14-
This will insert the `CompressionHandler` to the request pipeline as the first on incoming requests, and the last on outgoing requests.
14+
This will insert the `ServerCompressionHandler` to the request pipeline as the first on incoming requests, and the last on outgoing requests.
1515

1616
### Client side
1717

@@ -20,26 +20,31 @@ If you are doing your requests with `JavaScript` you probably don't have to do d
2020
Just make sure the `gzip` and `deflate` values are included in the `Accept-Encoding` header. (Most browsers do this by default)
2121

2222
#### C\#
23-
You need to apply the following code when creating your `HttpClient`, depending on the request type.
24-
25-
**GET requests**
23+
You need to apply the following code when creating your `HttpClient`.
2624
```csharp
27-
var client =
28-
new HttpClient(
29-
new DecompressionHandler(new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor()));
25+
var client = new HttpClient(new ClientompressionHandler(new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor()));
3026

3127
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
3228
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
3329
```
3430

35-
**POST, PUT, DELETE requests (anything with a request body)**
36-
```csharp
37-
var client =
38-
new HttpClient(
39-
new CompressionHandler(new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor()));
31+
Thats it! You should now immediately start experiencing smaller payloads when doing GET, POST, PUT, etc.
4032

41-
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
42-
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
33+
## Advanced use
34+
### Don't compress requests/responses that are smaller than a specified value
35+
By default, both `ServerCompressionHandler` and `ClientCompressionHandler` compress everything.
36+
However, this can be overriden by inserting a threshold as the first parameter like this:
37+
```csharp
38+
var serverCompressionHandler = new ServerCompressionHandler(4096, new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor());
39+
var clientCompressionHandler = new ClientCompressionHandler(4096, new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor());
4340
```
44-
45-
Thats it! You should now immediately start experiencing smaller payloads when doing GET, POST, PUT, etc.
41+
The above code will skip compression for any request/response that is smaller the `4096 bytes` / `4 kB`.
42+
43+
## Version history
44+
### 1.1 (current)
45+
* Simplified usage
46+
* Added support for setting a minimum content size for compressing
47+
48+
### 1.0
49+
* First release, basic compression of server responses and client requests
50+
* Did not support compressing POSTs and PUTs
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<?xml version="1.0" encoding="utf-8"?><Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><DontShowAgainInSolution>false</DontShowAgainInSolution></Configuration>

src/Microsoft.AspNet.WebApi.MessageHandlers.Compression.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetPackage", "NuGetPackag
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.WebApi.MessageHandlers.Compression", "Microsoft.AspNet.WebApi.MessageHandlers.Compression\Microsoft.AspNet.WebApi.MessageHandlers.Compression.csproj", "{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}"
99
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{7952E97C-2C7E-424B-A912-ED875576CA92}"
11+
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1214
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
2123
{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
2224
{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
2325
{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Release|Any CPU.Build.0 = Release|Any CPU
26+
{7952E97C-2C7E-424B-A912-ED875576CA92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27+
{7952E97C-2C7E-424B-A912-ED875576CA92}.Debug|Any CPU.Build.0 = Debug|Any CPU
28+
{7952E97C-2C7E-424B-A912-ED875576CA92}.Release|Any CPU.ActiveCfg = Release|Any CPU
29+
{7952E97C-2C7E-424B-A912-ED875576CA92}.Release|Any CPU.Build.0 = Release|Any CPU
2430
EndGlobalSection
2531
GlobalSection(SolutionProperties) = preSolution
2632
HideSolutionNode = FALSE
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
namespace Microsoft.AspNet.WebApi.MessageHandlers.Compression
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Net.Http;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
using Microsoft.AspNet.WebApi.MessageHandlers.Compression.Interfaces;
11+
using Microsoft.AspNet.WebApi.MessageHandlers.Compression.Models;
12+
13+
/// <summary>
14+
/// Message handler for handling gzip/deflate requests/responses on a <see cref="HttpClient"/>.
15+
/// </summary>
16+
public class ClientCompressionHandler : DelegatingHandler
17+
{
18+
/// <summary>
19+
/// The content size threshold before compressing.
20+
/// </summary>
21+
private readonly int contentSizeThreshold;
22+
23+
/// <summary>
24+
/// The HTTP content operations
25+
/// </summary>
26+
private readonly HttpContentOperations httpContentOperations;
27+
28+
/// <summary>
29+
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
30+
/// </summary>
31+
/// <param name="compressors">The compressors.</param>
32+
public ClientCompressionHandler(params ICompressor[] compressors)
33+
{
34+
this.Compressors = compressors;
35+
this.httpContentOperations = new HttpContentOperations();
36+
}
37+
38+
/// <summary>
39+
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
40+
/// </summary>
41+
/// <param name="contentSizeThreshold">The content size threshold before compressing.</param>
42+
/// <param name="compressors">The compressors.</param>
43+
public ClientCompressionHandler(int contentSizeThreshold, params ICompressor[] compressors)
44+
: this(compressors)
45+
{
46+
this.contentSizeThreshold = contentSizeThreshold;
47+
}
48+
49+
/// <summary>
50+
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
51+
/// </summary>
52+
/// <param name="innerHandler">The inner handler.</param>
53+
/// <param name="compressors">The compressors.</param>
54+
public ClientCompressionHandler(HttpMessageHandler innerHandler, params ICompressor[] compressors)
55+
: this(compressors)
56+
{
57+
this.InnerHandler = innerHandler;
58+
}
59+
60+
/// <summary>
61+
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
62+
/// </summary>
63+
/// <param name="innerHandler">The inner handler.</param>
64+
/// <param name="contentSizeThreshold">The content size threshold before compressing.</param>
65+
/// <param name="compressors">The compressors.</param>
66+
public ClientCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, params ICompressor[] compressors)
67+
: this(contentSizeThreshold, compressors)
68+
{
69+
this.InnerHandler = innerHandler;
70+
}
71+
72+
/// <summary>
73+
/// Gets the compressors.
74+
/// </summary>
75+
/// <value>The compressors.</value>
76+
public ICollection<ICompressor> Compressors { get; private set; }
77+
78+
/// <summary>
79+
/// send as an asynchronous operation.
80+
/// </summary>
81+
/// <param name="request">The HTTP request message to send to the server.</param>
82+
/// <param name="cancellationToken">A cancellation token to cancel operation.</param>
83+
/// <returns>Returns <see cref="T:System.Threading.Tasks.Task`1" />. The task object representing the asynchronous operation.</returns>
84+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
85+
{
86+
// Compress uncompressed requests from the client to the server
87+
if (request.Content != null && request.Headers.AcceptEncoding.Any())
88+
{
89+
await this.CompressRequest(request);
90+
}
91+
92+
var response = await base.SendAsync(request, cancellationToken);
93+
94+
// Decompress compressed responses to the client from the server
95+
if (response.Content != null && response.Content.Headers.ContentEncoding.Any())
96+
{
97+
await this.DecompressResponse(response);
98+
}
99+
100+
return response;
101+
}
102+
103+
/// <summary>
104+
/// Compresses the content.
105+
/// </summary>
106+
/// <param name="request">The request.</param>
107+
/// <returns>An async void.</returns>
108+
private async Task CompressRequest(HttpRequestMessage request)
109+
{
110+
// As per RFC2616.14.3:
111+
// Ignores encodings with quality == 0
112+
// If multiple content-codings are acceptable, then the acceptable content-coding with the highest non-zero qvalue is preferred.
113+
var compressor = (from encoding in request.Headers.AcceptEncoding
114+
let quality = encoding.Quality ?? 1.0
115+
where quality > 0
116+
join c in this.Compressors on encoding.Value.ToLowerInvariant() equals
117+
c.EncodingType.ToLowerInvariant()
118+
orderby quality descending
119+
select c).FirstOrDefault();
120+
121+
if (compressor != null)
122+
{
123+
// Only compress response if size is larger than threshold (if set)
124+
if (this.contentSizeThreshold == 0)
125+
{
126+
request.Content = new CompressedContent(request.Content, compressor);
127+
}
128+
else if (this.contentSizeThreshold > 0 && request.Content.Headers.ContentLength >= this.contentSizeThreshold)
129+
{
130+
request.Content = new CompressedContent(request.Content, compressor);
131+
}
132+
}
133+
}
134+
135+
/// <summary>
136+
/// Decompresses the response.
137+
/// </summary>
138+
/// <param name="response">The response.</param>
139+
/// <returns>An async void.</returns>
140+
private async Task DecompressResponse(HttpResponseMessage response)
141+
{
142+
var encoding = response.Content.Headers.ContentEncoding.FirstOrDefault();
143+
144+
if (encoding != null)
145+
{
146+
var compressor = this.Compressors.FirstOrDefault(c => c.EncodingType.Equals(encoding, StringComparison.OrdinalIgnoreCase));
147+
148+
if (compressor != null)
149+
{
150+
response.Content = await this.httpContentOperations.DecompressContent(response.Content, compressor);
151+
}
152+
}
153+
}
154+
}
155+
}

src/Microsoft.AspNet.WebApi.MessageHandlers.Compression/CompressionHandler.cs

Lines changed: 0 additions & 77 deletions
This file was deleted.

0 commit comments

Comments
 (0)