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+ }
0 commit comments