Skip to content
This repository was archived by the owner on Nov 22, 2018. It is now read-only.

Commit 8c5a5f7

Browse files
committed
Use ObjectPool for StringBuilder
1 parent fb724a7 commit 8c5a5f7

File tree

3 files changed

+98
-73
lines changed

3 files changed

+98
-73
lines changed

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs

Lines changed: 79 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@
1010
using Microsoft.AspNetCore.Http.Features;
1111
using Microsoft.AspNetCore.Http.Headers;
1212
using Microsoft.AspNetCore.ResponseCaching.Internal;
13+
using Microsoft.Extensions.ObjectPool;
1314
using Microsoft.Extensions.Primitives;
1415
using Microsoft.Net.Http.Headers;
1516

1617
namespace 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

Comments
 (0)