1010using Microsoft . AspNetCore . Http . Features ;
1111using Microsoft . AspNetCore . Http . Headers ;
1212using Microsoft . AspNetCore . ResponseCaching . Internal ;
13+ using Microsoft . Extensions . ObjectPool ;
1314using Microsoft . Extensions . Primitives ;
1415using Microsoft . Net . Http . Headers ;
1516
1617namespace Microsoft . AspNetCore . ResponseCaching
1718{
18- public class ResponseCachingContext
19+ internal class ResponseCachingContext
1920 {
2021 private static readonly CacheControlHeaderValue EmptyCacheControl = new CacheControlHeaderValue ( ) ;
22+
23+ private readonly HttpContext _httpContext ;
24+ private readonly IResponseCache _cache ;
25+ private readonly ISystemClock _clock ;
26+ private readonly ObjectPool < StringBuilder > _builderPool ;
27+ private readonly IResponseCachingCacheabilityValidator _cacheabilityValidator ;
28+ private readonly IResponseCachingCacheKeySuffixProvider _cacheKeySuffixProvider ;
29+
2130 private string _cacheKey ;
2231 private ResponseType ? _responseType ;
2332 private RequestHeaders _requestHeaders ;
@@ -29,12 +38,13 @@ public class ResponseCachingContext
2938 private TimeSpan _cachedResponseValidFor ;
3039 internal DateTimeOffset _responseTime ;
3140
32- public ResponseCachingContext (
41+ internal ResponseCachingContext (
3342 HttpContext httpContext ,
3443 IResponseCache cache ,
44+ ObjectPool < StringBuilder > builderPool ,
3545 IResponseCachingCacheabilityValidator cacheabilityValidator ,
3646 IResponseCachingCacheKeySuffixProvider cacheKeySuffixProvider )
37- : this ( httpContext , cache , new SystemClock ( ) , cacheabilityValidator , cacheKeySuffixProvider )
47+ : this ( httpContext , cache , new SystemClock ( ) , builderPool , cacheabilityValidator , cacheKeySuffixProvider )
3848 {
3949 }
4050
@@ -43,14 +53,16 @@ internal ResponseCachingContext(
4353 HttpContext httpContext ,
4454 IResponseCache cache ,
4555 ISystemClock clock ,
56+ ObjectPool < StringBuilder > builderPool ,
4657 IResponseCachingCacheabilityValidator cacheabilityValidator ,
4758 IResponseCachingCacheKeySuffixProvider cacheKeySuffixProvider )
4859 {
49- HttpContext = httpContext ;
50- Cache = cache ;
51- Clock = clock ;
52- CacheabilityValidator = cacheabilityValidator ;
53- CacheKeySuffixProvider = cacheKeySuffixProvider ;
60+ _httpContext = httpContext ;
61+ _cache = cache ;
62+ _clock = clock ;
63+ _builderPool = builderPool ;
64+ _cacheabilityValidator = cacheabilityValidator ;
65+ _cacheKeySuffixProvider = cacheKeySuffixProvider ;
5466 }
5567
5668 internal bool CacheResponse
@@ -70,16 +82,6 @@ internal bool CacheResponse
7082
7183 internal bool ResponseStarted { get ; set ; }
7284
73- private HttpContext HttpContext { get ; }
74-
75- private IResponseCache Cache { get ; }
76-
77- private ISystemClock Clock { get ; }
78-
79- private IResponseCachingCacheabilityValidator CacheabilityValidator { get ; }
80-
81- private IResponseCachingCacheKeySuffixProvider CacheKeySuffixProvider { get ; }
82-
8385 private Stream OriginalResponseStream { get ; set ; }
8486
8587 private ResponseCacheStream ResponseCacheStream { get ; set ; }
@@ -92,7 +94,7 @@ private RequestHeaders RequestHeaders
9294 {
9395 if ( _requestHeaders == null )
9496 {
95- _requestHeaders = HttpContext . Request . GetTypedHeaders ( ) ;
97+ _requestHeaders = _httpContext . Request . GetTypedHeaders ( ) ;
9698 }
9799 return _requestHeaders ;
98100 }
@@ -104,7 +106,7 @@ private ResponseHeaders ResponseHeaders
104106 {
105107 if ( _responseHeaders == null )
106108 {
107- _responseHeaders = HttpContext . Response . GetTypedHeaders ( ) ;
109+ _responseHeaders = _httpContext . Response . GetTypedHeaders ( ) ;
108110 }
109111 return _responseHeaders ;
110112 }
@@ -143,49 +145,58 @@ internal string CreateCacheKey()
143145
144146 internal string CreateCacheKey ( CachedVaryBy varyBy )
145147 {
146- var request = HttpContext . Request ;
147- var builder = new StringBuilder ( )
148- . Append ( request . Method . ToUpperInvariant ( ) )
149- . Append ( ";" )
150- . Append ( request . Path . Value . ToUpperInvariant ( ) ) ;
148+ var request = _httpContext . Request ;
149+ var builder = _builderPool . Get ( ) ;
151150
152- if ( varyBy ? . Headers . Count > 0 )
151+ try
153152 {
154- // TODO: resolve key format and delimiters
155- foreach ( var header in varyBy . Headers )
156- {
157- // TODO: Normalization of order, case?
158- var value = HttpContext . Request . Headers [ header ] ;
153+ builder
154+ . Append ( request . Method . ToUpperInvariant ( ) )
155+ . Append ( ";" )
156+ . Append ( request . Path . Value . ToUpperInvariant ( ) ) ;
159157
160- // TODO: How to handle null/empty string?
161- if ( StringValues . IsNullOrEmpty ( value ) )
158+ if ( varyBy ? . Headers . Count > 0 )
159+ {
160+ // TODO: resolve key format and delimiters
161+ foreach ( var header in varyBy . Headers )
162162 {
163- value = "null" ;
163+ // TODO: Normalization of order, case?
164+ var value = _httpContext . Request . Headers [ header ] ;
165+
166+ // TODO: How to handle null/empty string?
167+ if ( StringValues . IsNullOrEmpty ( value ) )
168+ {
169+ value = "null" ;
170+ }
171+
172+ builder . Append ( ";" )
173+ . Append ( header )
174+ . Append ( "=" )
175+ . Append ( value ) ;
164176 }
177+ }
178+ // TODO: Parse querystring params
165179
180+ // Append custom cache key segment
181+ var customKey = _cacheKeySuffixProvider . CreateCustomKeySuffix ( _httpContext ) ;
182+ if ( ! string . IsNullOrEmpty ( customKey ) )
183+ {
166184 builder . Append ( ";" )
167- . Append ( header )
168- . Append ( "=" )
169- . Append ( value ) ;
185+ . Append ( customKey ) ;
170186 }
171- }
172- // TODO: Parse querystring params
173187
174- // Append custom cache key segment
175- var customKey = CacheKeySuffixProvider . CreateCustomKeySuffix ( HttpContext ) ;
176- if ( ! string . IsNullOrEmpty ( customKey ) )
188+ return builder . ToString ( ) ;
189+ }
190+ finally
177191 {
178- builder . Append ( ";" )
179- . Append ( customKey ) ;
192+ _builderPool . Return ( builder ) ;
180193 }
181-
182- return builder . ToString ( ) ;
183194 }
184195
185196 internal bool RequestIsCacheable ( )
186197 {
187198 // Use optional override if specified by user
188- switch ( CacheabilityValidator . RequestIsCacheableOverride ( HttpContext ) )
199+ switch ( _cacheabilityValidator . RequestIsCacheableOverride ( _httpContext ) )
189200 {
190201 case OverrideResult . UseDefaultLogic :
191202 break ;
@@ -194,12 +205,12 @@ internal bool RequestIsCacheable()
194205 case OverrideResult . Cache :
195206 return true ;
196207 default :
197- throw new NotSupportedException ( $ "Unrecognized result from { nameof ( CacheabilityValidator . RequestIsCacheableOverride ) } .") ;
208+ throw new NotSupportedException ( $ "Unrecognized result from { nameof ( _cacheabilityValidator . RequestIsCacheableOverride ) } .") ;
198209 }
199210
200211 // Verify the method
201212 // TODO: RFC lists POST as a cacheable method when explicit freshness information is provided, but this is not widely implemented. Will revisit.
202- var request = HttpContext . Request ;
213+ var request = _httpContext . Request ;
203214 if ( string . Equals ( "GET" , request . Method , StringComparison . OrdinalIgnoreCase ) )
204215 {
205216 _responseType = ResponseType . FullReponse ;
@@ -249,7 +260,7 @@ internal bool RequestIsCacheable()
249260 internal bool ResponseIsCacheable ( )
250261 {
251262 // Use optional override if specified by user
252- switch ( CacheabilityValidator . ResponseIsCacheableOverride ( HttpContext ) )
263+ switch ( _cacheabilityValidator . ResponseIsCacheableOverride ( _httpContext ) )
253264 {
254265 case OverrideResult . UseDefaultLogic :
255266 break ;
@@ -258,7 +269,7 @@ internal bool ResponseIsCacheable()
258269 case OverrideResult . Cache :
259270 return true ;
260271 default :
261- throw new NotSupportedException ( $ "Unrecognized result from { nameof ( CacheabilityValidator . ResponseIsCacheableOverride ) } .") ;
272+ throw new NotSupportedException ( $ "Unrecognized result from { nameof ( _cacheabilityValidator . ResponseIsCacheableOverride ) } .") ;
262273 }
263274
264275 // Only cache pages explicitly marked with public
@@ -281,7 +292,7 @@ internal bool ResponseIsCacheable()
281292 return false ;
282293 }
283294
284- var response = HttpContext . Response ;
295+ var response = _httpContext . Response ;
285296
286297 // Do not cache responses varying by *
287298 if ( string . Equals ( response . Headers [ HeaderNames . Vary ] , "*" , StringComparison . OrdinalIgnoreCase ) )
@@ -359,28 +370,28 @@ internal bool EntryIsFresh(ResponseHeaders responseHeaders, TimeSpan age, bool v
359370 internal async Task < bool > TryServeFromCacheAsync ( )
360371 {
361372 _cacheKey = CreateCacheKey ( ) ;
362- var cacheEntry = Cache . Get ( _cacheKey ) ;
373+ var cacheEntry = _cache . Get ( _cacheKey ) ;
363374 var responseServed = false ;
364375
365376 if ( cacheEntry is CachedVaryBy )
366377 {
367378 // Request contains VaryBy rules, recompute key and try again
368379 _cacheKey = CreateCacheKey ( cacheEntry as CachedVaryBy ) ;
369- cacheEntry = Cache . Get ( _cacheKey ) ;
380+ cacheEntry = _cache . Get ( _cacheKey ) ;
370381 }
371382
372383 if ( cacheEntry is CachedResponse )
373384 {
374385 var cachedResponse = cacheEntry as CachedResponse ;
375386 var cachedResponseHeaders = new ResponseHeaders ( cachedResponse . Headers ) ;
376387
377- _responseTime = Clock . UtcNow ;
388+ _responseTime = _clock . UtcNow ;
378389 var age = _responseTime - cachedResponse . Created ;
379390 age = age > TimeSpan . Zero ? age : TimeSpan . Zero ;
380391
381392 if ( EntryIsFresh ( cachedResponseHeaders , age , verifyAgainstRequest : true ) )
382393 {
383- var response = HttpContext . Response ;
394+ var response = _httpContext . Response ;
384395 // Copy the cached status code and response headers
385396 response . StatusCode = cachedResponse . StatusCode ;
386397 foreach ( var header in cachedResponse . Headers )
@@ -425,7 +436,7 @@ internal async Task<bool> TryServeFromCacheAsync()
425436
426437 if ( ! responseServed && RequestCacheControl . OnlyIfCached )
427438 {
428- HttpContext . Response . StatusCode = StatusCodes . Status504GatewayTimeout ;
439+ _httpContext . Response . StatusCode = StatusCodes . Status504GatewayTimeout ;
429440
430441 responseServed = true ;
431442 }
@@ -438,7 +449,7 @@ internal void FinalizeCachingHeaders()
438449 if ( CacheResponse )
439450 {
440451 // Create the cache entry now
441- var response = HttpContext . Response ;
452+ var response = _httpContext . Response ;
442453 var varyHeaderValue = response . Headers [ HeaderNames . Vary ] ;
443454 _cachedResponseValidFor = ResponseCacheControl . SharedMaxAge
444455 ?? ResponseCacheControl . MaxAge
@@ -457,7 +468,7 @@ internal void FinalizeCachingHeaders()
457468 } ;
458469
459470 // TODO: Overwrite?
460- Cache . Set ( _cacheKey , cachedVaryBy , _cachedResponseValidFor ) ;
471+ _cache . Set ( _cacheKey , cachedVaryBy , _cachedResponseValidFor ) ;
461472 _cacheKey = CreateCacheKey ( cachedVaryBy ) ;
462473 }
463474
@@ -471,7 +482,7 @@ internal void FinalizeCachingHeaders()
471482 _cachedResponse = new CachedResponse
472483 {
473484 Created = ResponseHeaders . Date . Value ,
474- StatusCode = HttpContext . Response . StatusCode
485+ StatusCode = _httpContext . Response . StatusCode
475486 } ;
476487
477488 foreach ( var header in ResponseHeaders . Headers )
@@ -495,7 +506,7 @@ internal void FinalizeCachingBody()
495506 {
496507 _cachedResponse . Body = ResponseCacheStream . BufferedStream . ToArray ( ) ;
497508
498- Cache . Set ( _cacheKey , _cachedResponse , _cachedResponseValidFor ) ;
509+ _cache . Set ( _cacheKey , _cachedResponse , _cachedResponseValidFor ) ;
499510 }
500511 }
501512
@@ -504,7 +515,7 @@ internal void OnResponseStarting()
504515 if ( ! ResponseStarted )
505516 {
506517 ResponseStarted = true ;
507- _responseTime = Clock . UtcNow ;
518+ _responseTime = _clock . UtcNow ;
508519
509520 FinalizeCachingHeaders ( ) ;
510521 }
@@ -515,25 +526,25 @@ internal void ShimResponseStream()
515526 // TODO: Consider caching large responses on disk and serving them from there.
516527
517528 // Shim response stream
518- OriginalResponseStream = HttpContext . Response . Body ;
529+ OriginalResponseStream = _httpContext . Response . Body ;
519530 ResponseCacheStream = new ResponseCacheStream ( OriginalResponseStream ) ;
520- HttpContext . Response . Body = ResponseCacheStream ;
531+ _httpContext . Response . Body = ResponseCacheStream ;
521532
522533 // Shim IHttpSendFileFeature
523- OriginalSendFileFeature = HttpContext . Features . Get < IHttpSendFileFeature > ( ) ;
534+ OriginalSendFileFeature = _httpContext . Features . Get < IHttpSendFileFeature > ( ) ;
524535 if ( OriginalSendFileFeature != null )
525536 {
526- HttpContext . Features . Set < IHttpSendFileFeature > ( new SendFileFeatureWrapper ( OriginalSendFileFeature , ResponseCacheStream ) ) ;
537+ _httpContext . Features . Set < IHttpSendFileFeature > ( new SendFileFeatureWrapper ( OriginalSendFileFeature , ResponseCacheStream ) ) ;
527538 }
528539 }
529540
530541 internal void UnshimResponseStream ( )
531542 {
532543 // Unshim response stream
533- HttpContext . Response . Body = OriginalResponseStream ;
544+ _httpContext . Response . Body = OriginalResponseStream ;
534545
535546 // Unshim IHttpSendFileFeature
536- HttpContext . Features . Set ( OriginalSendFileFeature ) ;
547+ _httpContext . Features . Set ( OriginalSendFileFeature ) ;
537548 }
538549
539550 private enum ResponseType
0 commit comments