3
3
4
4
using System ;
5
5
using System . Collections . Generic ;
6
+ using System . Diagnostics ;
6
7
using System . Linq ;
7
8
using Microsoft . AspNetCore . Http ;
9
+ using Microsoft . AspNetCore . ResponseCompression . Internal ;
10
+ using Microsoft . Extensions . DependencyInjection ;
11
+ using Microsoft . Extensions . Logging ;
8
12
using Microsoft . Extensions . Options ;
9
13
using Microsoft . Extensions . Primitives ;
10
14
using Microsoft . Net . Http . Headers ;
@@ -18,6 +22,7 @@ public class ResponseCompressionProvider : IResponseCompressionProvider
18
22
private readonly HashSet < string > _mimeTypes ;
19
23
private readonly HashSet < string > _excludedMimeTypes ;
20
24
private readonly bool _enableForHttps ;
25
+ private readonly ILogger _logger ;
21
26
22
27
/// <summary>
23
28
/// If no compression providers are specified then GZip is used by default.
@@ -67,6 +72,7 @@ public ResponseCompressionProvider(IServiceProvider services, IOptions<ResponseC
67
72
{
68
73
mimeTypes = ResponseCompressionDefaults . MimeTypes ;
69
74
}
75
+
70
76
_mimeTypes = new HashSet < string > ( mimeTypes , StringComparer . OrdinalIgnoreCase ) ;
71
77
72
78
_excludedMimeTypes = new HashSet < string > (
@@ -75,6 +81,8 @@ public ResponseCompressionProvider(IServiceProvider services, IOptions<ResponseC
75
81
) ;
76
82
77
83
_enableForHttps = responseCompressionOptions . EnableForHttps ;
84
+
85
+ _logger = services . GetRequiredService < ILogger < ResponseCompressionProvider > > ( ) ;
78
86
}
79
87
80
88
/// <inheritdoc />
@@ -83,95 +91,108 @@ public virtual ICompressionProvider GetCompressionProvider(HttpContext context)
83
91
// e.g. Accept-Encoding: gzip, deflate, sdch
84
92
var accept = context . Request . Headers [ HeaderNames . AcceptEncoding ] ;
85
93
94
+ // Note this is already checked in CheckRequestAcceptsCompression which _should_ prevent any of these other methods from being called.
86
95
if ( StringValues . IsNullOrEmpty ( accept ) )
87
96
{
97
+ Debug . Assert ( false , "Duplicate check failed." ) ;
98
+ _logger . NoAcceptEncoding ( ) ;
88
99
return null ;
89
100
}
90
101
91
- if ( StringWithQualityHeaderValue . TryParseList ( accept , out var encodings ) )
102
+ if ( ! StringWithQualityHeaderValue . TryParseList ( accept , out var encodings ) || ! encodings . Any ( ) )
92
103
{
93
- if ( encodings . Count == 0 )
104
+ _logger . NoAcceptEncoding ( ) ;
105
+ return null ;
106
+ }
107
+
108
+ var candidates = new HashSet < ProviderCandidate > ( ) ;
109
+
110
+ foreach ( var encoding in encodings )
111
+ {
112
+ var encodingName = encoding . Value ;
113
+ var quality = encoding . Quality . GetValueOrDefault ( 1 ) ;
114
+
115
+ if ( quality < double . Epsilon )
94
116
{
95
- return null ;
117
+ continue ;
96
118
}
97
119
98
- var candidates = new HashSet < ProviderCandidate > ( ) ;
99
-
100
- foreach ( var encoding in encodings )
120
+ for ( int i = 0 ; i < _providers . Length ; i ++ )
101
121
{
102
- var encodingName = encoding . Value ;
103
- var quality = encoding . Quality . GetValueOrDefault ( 1 ) ;
122
+ var provider = _providers [ i ] ;
104
123
105
- if ( quality < double . Epsilon )
124
+ if ( StringSegment . Equals ( provider . EncodingName , encodingName , StringComparison . OrdinalIgnoreCase ) )
106
125
{
107
- continue ;
126
+ candidates . Add ( new ProviderCandidate ( provider . EncodingName , quality , i , provider ) ) ;
108
127
}
128
+ }
109
129
130
+ // Uncommon but valid options
131
+ if ( StringSegment . Equals ( "*" , encodingName , StringComparison . Ordinal ) )
132
+ {
110
133
for ( int i = 0 ; i < _providers . Length ; i ++ )
111
134
{
112
135
var provider = _providers [ i ] ;
113
136
114
- if ( StringSegment . Equals ( provider . EncodingName , encodingName , StringComparison . OrdinalIgnoreCase ) )
115
- {
116
- candidates . Add ( new ProviderCandidate ( provider . EncodingName , quality , i , provider ) ) ;
117
- }
137
+ // Any provider is a candidate.
138
+ candidates . Add ( new ProviderCandidate ( provider . EncodingName , quality , i , provider ) ) ;
118
139
}
119
140
120
- // Uncommon but valid options
121
- if ( StringSegment . Equals ( "*" , encodingName , StringComparison . Ordinal ) )
122
- {
123
- for ( int i = 0 ; i < _providers . Length ; i ++ )
124
- {
125
- var provider = _providers [ i ] ;
126
-
127
- // Any provider is a candidate.
128
- candidates . Add ( new ProviderCandidate ( provider . EncodingName , quality , i , provider ) ) ;
129
- }
130
-
131
- break ;
132
- }
133
-
134
- if ( StringSegment . Equals ( "identity" , encodingName , StringComparison . OrdinalIgnoreCase ) )
135
- {
136
- // We add 'identity' to the list of "candidates" with a very low priority and no provider.
137
- // This will allow it to be ordered based on its quality (and priority) later in the method.
138
- candidates . Add ( new ProviderCandidate ( encodingName . Value , quality , priority : int . MaxValue , provider : null ) ) ;
139
- }
141
+ break ;
140
142
}
141
143
142
- if ( candidates . Count <= 1 )
144
+ if ( StringSegment . Equals ( "identity" , encodingName , StringComparison . OrdinalIgnoreCase ) )
143
145
{
144
- return candidates . ElementAtOrDefault ( 0 ) . Provider ;
146
+ // We add 'identity' to the list of "candidates" with a very low priority and no provider.
147
+ // This will allow it to be ordered based on its quality (and priority) later in the method.
148
+ candidates . Add ( new ProviderCandidate ( encodingName . Value , quality , priority : int . MaxValue , provider : null ) ) ;
145
149
}
150
+ }
146
151
147
- var accepted = candidates
152
+ ICompressionProvider selectedProvider = null ;
153
+ if ( candidates . Count <= 1 )
154
+ {
155
+ selectedProvider = candidates . FirstOrDefault ( ) . Provider ;
156
+ }
157
+ else
158
+ {
159
+ selectedProvider = candidates
148
160
. OrderByDescending ( x => x . Quality )
149
161
. ThenBy ( x => x . Priority )
150
- . First ( ) ;
162
+ . First ( ) . Provider ;
163
+ }
151
164
152
- return accepted . Provider ;
165
+ if ( selectedProvider == null )
166
+ {
167
+ // "identity" would match as a candidate but not have a provider implementation
168
+ _logger . NoCompressionProvider ( ) ;
169
+ return null ;
153
170
}
154
171
155
- return null ;
172
+ _logger . CompressingWith ( selectedProvider . EncodingName ) ;
173
+ return selectedProvider ;
156
174
}
157
175
158
176
/// <inheritdoc />
159
177
public virtual bool ShouldCompressResponse ( HttpContext context )
160
178
{
161
179
if ( context . Response . Headers . ContainsKey ( HeaderNames . ContentRange ) )
162
180
{
181
+ _logger . NoCompressionDueToHeader ( HeaderNames . ContentRange ) ;
163
182
return false ;
164
183
}
165
184
166
185
if ( context . Response . Headers . ContainsKey ( HeaderNames . ContentEncoding ) )
167
186
{
187
+ _logger . NoCompressionDueToHeader ( HeaderNames . ContentEncoding ) ;
168
188
return false ;
169
189
}
170
190
171
191
var mimeType = context . Response . ContentType ;
172
192
173
193
if ( string . IsNullOrEmpty ( mimeType ) )
174
194
{
195
+ _logger . NoCompressionForContentType ( mimeType ) ;
175
196
return false ;
176
197
}
177
198
@@ -183,19 +204,37 @@ public virtual bool ShouldCompressResponse(HttpContext context)
183
204
mimeType = mimeType . Trim ( ) ;
184
205
}
185
206
186
- return ShouldCompressExact ( mimeType ) //check exact match type/subtype
207
+ var shouldCompress = ShouldCompressExact ( mimeType ) //check exact match type/subtype
187
208
?? ShouldCompressPartial ( mimeType ) //check partial match type/*
188
209
?? _mimeTypes . Contains ( "*/*" ) ; //check wildcard */*
210
+
211
+ if ( shouldCompress )
212
+ {
213
+ _logger . ShouldCompressResponse ( ) ; // Trace, there will be more logs
214
+ return true ;
215
+ }
216
+
217
+ _logger . NoCompressionForContentType ( mimeType ) ;
218
+ return false ;
189
219
}
190
220
191
221
/// <inheritdoc />
192
222
public bool CheckRequestAcceptsCompression ( HttpContext context )
193
223
{
194
224
if ( context . Request . IsHttps && ! _enableForHttps )
195
225
{
226
+ _logger . NoCompressionForHttps ( ) ;
196
227
return false ;
197
228
}
198
- return ! string . IsNullOrEmpty ( context . Request . Headers [ HeaderNames . AcceptEncoding ] ) ;
229
+
230
+ if ( string . IsNullOrEmpty ( context . Request . Headers [ HeaderNames . AcceptEncoding ] ) )
231
+ {
232
+ _logger . NoAcceptEncoding ( ) ;
233
+ return false ;
234
+ }
235
+
236
+ _logger . RequestAcceptsCompression ( ) ; // Trace, there will be more logs
237
+ return true ;
199
238
}
200
239
201
240
private bool ? ShouldCompressExact ( string mimeType )
0 commit comments