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

Commit

Permalink
Simplified usage, and added support for setting a minimum content siz…
Browse files Browse the repository at this point in the history
…e for compressing.
  • Loading branch information
azzlack committed Jul 10, 2014
1 parent de55daa commit 717f386
Show file tree
Hide file tree
Showing 20 changed files with 848 additions and 239 deletions.
39 changes: 22 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Drop-in module for ASP.Net WebAPI that enables `GZip` and `Deflate` support
You need to add the compression handler as the last applied message handler on outgoing requests, and the first one on incoming requests.
To do that, just add the following line to your `App_Start\WebApiConfig.cs` file after adding all your other message handlers:
```csharp
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new CompressionHandler(new GZipCompressor(), new DeflateCompressor()));
GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));
```
This will insert the `CompressionHandler` to the request pipeline as the first on incoming requests, and the last on outgoing requests.
This will insert the `ServerCompressionHandler` to the request pipeline as the first on incoming requests, and the last on outgoing requests.

### Client side

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

#### C\#
You need to apply the following code when creating your `HttpClient`, depending on the request type.

**GET requests**
You need to apply the following code when creating your `HttpClient`.
```csharp
var client =
new HttpClient(
new DecompressionHandler(new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor()));
var client = new HttpClient(new ClientompressionHandler(new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor()));

client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
```

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

client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
## Advanced use
### Don't compress requests/responses that are smaller than a specified value
By default, both `ServerCompressionHandler` and `ClientCompressionHandler` compress everything.
However, this can be overriden by inserting a threshold as the first parameter like this:
```csharp
var serverCompressionHandler = new ServerCompressionHandler(4096, new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor());
var clientCompressionHandler = new ClientCompressionHandler(4096, new HttpClientHandler(), new GZipCompressor(), new DeflateCompressor());
```

Thats it! You should now immediately start experiencing smaller payloads when doing GET, POST, PUT, etc.
The above code will skip compression for any request/response that is smaller the `4096 bytes` / `4 kB`.

## Version history
### 1.1 (current)
* Simplified usage
* Added support for setting a minimum content size for compressing

### 1.0
* First release, basic compression of server responses and client requests
* Did not support compressing POSTs and PUTs
Original file line number Diff line number Diff line change
@@ -0,0 +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>
6 changes: 6 additions & 0 deletions src/Microsoft.AspNet.WebApi.MessageHandlers.Compression.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetPackage", "NuGetPackag
EndProject
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}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{7952E97C-2C7E-424B-A912-ED875576CA92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -21,6 +23,10 @@ Global
{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{56E19FF7-9AC7-48C5-9AC2-105F55AA16E1}.Release|Any CPU.Build.0 = Release|Any CPU
{7952E97C-2C7E-424B-A912-ED875576CA92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7952E97C-2C7E-424B-A912-ED875576CA92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7952E97C-2C7E-424B-A912-ED875576CA92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7952E97C-2C7E-424B-A912-ED875576CA92}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
namespace Microsoft.AspNet.WebApi.MessageHandlers.Compression
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNet.WebApi.MessageHandlers.Compression.Interfaces;
using Microsoft.AspNet.WebApi.MessageHandlers.Compression.Models;

/// <summary>
/// Message handler for handling gzip/deflate requests/responses on a <see cref="HttpClient"/>.
/// </summary>
public class ClientCompressionHandler : DelegatingHandler
{
/// <summary>
/// The content size threshold before compressing.
/// </summary>
private readonly int contentSizeThreshold;

/// <summary>
/// The HTTP content operations
/// </summary>
private readonly HttpContentOperations httpContentOperations;

/// <summary>
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
/// </summary>
/// <param name="compressors">The compressors.</param>
public ClientCompressionHandler(params ICompressor[] compressors)
{
this.Compressors = compressors;
this.httpContentOperations = new HttpContentOperations();
}

/// <summary>
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
/// </summary>
/// <param name="contentSizeThreshold">The content size threshold before compressing.</param>
/// <param name="compressors">The compressors.</param>
public ClientCompressionHandler(int contentSizeThreshold, params ICompressor[] compressors)
: this(compressors)
{
this.contentSizeThreshold = contentSizeThreshold;
}

/// <summary>
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
/// </summary>
/// <param name="innerHandler">The inner handler.</param>
/// <param name="compressors">The compressors.</param>
public ClientCompressionHandler(HttpMessageHandler innerHandler, params ICompressor[] compressors)
: this(compressors)
{
this.InnerHandler = innerHandler;
}

/// <summary>
/// Initializes a new instance of the <see cref="ClientCompressionHandler" /> class.
/// </summary>
/// <param name="innerHandler">The inner handler.</param>
/// <param name="contentSizeThreshold">The content size threshold before compressing.</param>
/// <param name="compressors">The compressors.</param>
public ClientCompressionHandler(HttpMessageHandler innerHandler, int contentSizeThreshold, params ICompressor[] compressors)
: this(contentSizeThreshold, compressors)
{
this.InnerHandler = innerHandler;
}

/// <summary>
/// Gets the compressors.
/// </summary>
/// <value>The compressors.</value>
public ICollection<ICompressor> Compressors { get; private set; }

/// <summary>
/// send as an asynchronous operation.
/// </summary>
/// <param name="request">The HTTP request message to send to the server.</param>
/// <param name="cancellationToken">A cancellation token to cancel operation.</param>
/// <returns>Returns <see cref="T:System.Threading.Tasks.Task`1" />. The task object representing the asynchronous operation.</returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Compress uncompressed requests from the client to the server
if (request.Content != null && request.Headers.AcceptEncoding.Any())
{
await this.CompressRequest(request);
}

var response = await base.SendAsync(request, cancellationToken);

// Decompress compressed responses to the client from the server
if (response.Content != null && response.Content.Headers.ContentEncoding.Any())
{
await this.DecompressResponse(response);
}

return response;
}

/// <summary>
/// Compresses the content.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>An async void.</returns>
private async Task CompressRequest(HttpRequestMessage request)
{
// As per RFC2616.14.3:
// Ignores encodings with quality == 0
// If multiple content-codings are acceptable, then the acceptable content-coding with the highest non-zero qvalue is preferred.
var compressor = (from encoding in request.Headers.AcceptEncoding
let quality = encoding.Quality ?? 1.0
where quality > 0
join c in this.Compressors on encoding.Value.ToLowerInvariant() equals
c.EncodingType.ToLowerInvariant()
orderby quality descending
select c).FirstOrDefault();

if (compressor != null)
{
// Only compress response if size is larger than threshold (if set)
if (this.contentSizeThreshold == 0)
{
request.Content = new CompressedContent(request.Content, compressor);
}
else if (this.contentSizeThreshold > 0 && request.Content.Headers.ContentLength >= this.contentSizeThreshold)
{
request.Content = new CompressedContent(request.Content, compressor);
}
}
}

/// <summary>
/// Decompresses the response.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>An async void.</returns>
private async Task DecompressResponse(HttpResponseMessage response)
{
var encoding = response.Content.Headers.ContentEncoding.FirstOrDefault();

if (encoding != null)
{
var compressor = this.Compressors.FirstOrDefault(c => c.EncodingType.Equals(encoding, StringComparison.OrdinalIgnoreCase));

if (compressor != null)
{
response.Content = await this.httpContentOperations.DecompressContent(response.Content, compressor);
}
}
}
}
}

This file was deleted.

Loading

0 comments on commit 717f386

Please sign in to comment.