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

Commit 993206f

Browse files
committed
Use ObjectPool for StringBuilder
1 parent fb724a7 commit 993206f

File tree

3 files changed

+97
-71
lines changed

3 files changed

+97
-71
lines changed

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs

Lines changed: 80 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@
1212
using Microsoft.AspNetCore.ResponseCaching.Internal;
1313
using Microsoft.Extensions.Primitives;
1414
using Microsoft.Net.Http.Headers;
15+
using Microsoft.Extensions.ObjectPool;
1516

1617
namespace Microsoft.AspNetCore.ResponseCaching
1718
{
1819
public 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;
@@ -32,9 +41,10 @@ public class ResponseCachingContext
3241
public 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,61 @@ 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() ?? new StringBuilder();
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+
if (_builderPool != null)
193+
{
194+
_builderPool.Return(builder);
195+
}
180196
}
181-
182-
return builder.ToString();
183197
}
184198

185199
internal bool RequestIsCacheable()
186200
{
187201
// Use optional override if specified by user
188-
switch(CacheabilityValidator.RequestIsCacheableOverride(HttpContext))
202+
switch(_cacheabilityValidator.RequestIsCacheableOverride(_httpContext))
189203
{
190204
case OverrideResult.UseDefaultLogic:
191205
break;
@@ -194,12 +208,12 @@ internal bool RequestIsCacheable()
194208
case OverrideResult.Cache:
195209
return true;
196210
default:
197-
throw new NotSupportedException($"Unrecognized result from {nameof(CacheabilityValidator.RequestIsCacheableOverride)}.");
211+
throw new NotSupportedException($"Unrecognized result from {nameof(_cacheabilityValidator.RequestIsCacheableOverride)}.");
198212
}
199213

200214
// Verify the method
201215
// 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;
216+
var request = _httpContext.Request;
203217
if (string.Equals("GET", request.Method, StringComparison.OrdinalIgnoreCase))
204218
{
205219
_responseType = ResponseType.FullReponse;
@@ -249,7 +263,7 @@ internal bool RequestIsCacheable()
249263
internal bool ResponseIsCacheable()
250264
{
251265
// Use optional override if specified by user
252-
switch (CacheabilityValidator.ResponseIsCacheableOverride(HttpContext))
266+
switch (_cacheabilityValidator.ResponseIsCacheableOverride(_httpContext))
253267
{
254268
case OverrideResult.UseDefaultLogic:
255269
break;
@@ -258,7 +272,7 @@ internal bool ResponseIsCacheable()
258272
case OverrideResult.Cache:
259273
return true;
260274
default:
261-
throw new NotSupportedException($"Unrecognized result from {nameof(CacheabilityValidator.ResponseIsCacheableOverride)}.");
275+
throw new NotSupportedException($"Unrecognized result from {nameof(_cacheabilityValidator.ResponseIsCacheableOverride)}.");
262276
}
263277

264278
// Only cache pages explicitly marked with public
@@ -281,7 +295,7 @@ internal bool ResponseIsCacheable()
281295
return false;
282296
}
283297

284-
var response = HttpContext.Response;
298+
var response = _httpContext.Response;
285299

286300
// Do not cache responses varying by *
287301
if (string.Equals(response.Headers[HeaderNames.Vary], "*", StringComparison.OrdinalIgnoreCase))
@@ -359,28 +373,28 @@ internal bool EntryIsFresh(ResponseHeaders responseHeaders, TimeSpan age, bool v
359373
internal async Task<bool> TryServeFromCacheAsync()
360374
{
361375
_cacheKey = CreateCacheKey();
362-
var cacheEntry = Cache.Get(_cacheKey);
376+
var cacheEntry = _cache.Get(_cacheKey);
363377
var responseServed = false;
364378

365379
if (cacheEntry is CachedVaryBy)
366380
{
367381
// Request contains VaryBy rules, recompute key and try again
368382
_cacheKey = CreateCacheKey(cacheEntry as CachedVaryBy);
369-
cacheEntry = Cache.Get(_cacheKey);
383+
cacheEntry = _cache.Get(_cacheKey);
370384
}
371385

372386
if (cacheEntry is CachedResponse)
373387
{
374388
var cachedResponse = cacheEntry as CachedResponse;
375389
var cachedResponseHeaders = new ResponseHeaders(cachedResponse.Headers);
376390

377-
_responseTime = Clock.UtcNow;
391+
_responseTime = _clock.UtcNow;
378392
var age = _responseTime - cachedResponse.Created;
379393
age = age > TimeSpan.Zero ? age : TimeSpan.Zero;
380394

381395
if (EntryIsFresh(cachedResponseHeaders, age, verifyAgainstRequest: true))
382396
{
383-
var response = HttpContext.Response;
397+
var response = _httpContext.Response;
384398
// Copy the cached status code and response headers
385399
response.StatusCode = cachedResponse.StatusCode;
386400
foreach (var header in cachedResponse.Headers)
@@ -425,7 +439,7 @@ internal async Task<bool> TryServeFromCacheAsync()
425439

426440
if (!responseServed && RequestCacheControl.OnlyIfCached)
427441
{
428-
HttpContext.Response.StatusCode = StatusCodes.Status504GatewayTimeout;
442+
_httpContext.Response.StatusCode = StatusCodes.Status504GatewayTimeout;
429443

430444
responseServed = true;
431445
}
@@ -438,7 +452,7 @@ internal void FinalizeCachingHeaders()
438452
if (CacheResponse)
439453
{
440454
// Create the cache entry now
441-
var response = HttpContext.Response;
455+
var response = _httpContext.Response;
442456
var varyHeaderValue = response.Headers[HeaderNames.Vary];
443457
_cachedResponseValidFor = ResponseCacheControl.SharedMaxAge
444458
?? ResponseCacheControl.MaxAge
@@ -457,7 +471,7 @@ internal void FinalizeCachingHeaders()
457471
};
458472

459473
// TODO: Overwrite?
460-
Cache.Set(_cacheKey, cachedVaryBy, _cachedResponseValidFor);
474+
_cache.Set(_cacheKey, cachedVaryBy, _cachedResponseValidFor);
461475
_cacheKey = CreateCacheKey(cachedVaryBy);
462476
}
463477

@@ -471,7 +485,7 @@ internal void FinalizeCachingHeaders()
471485
_cachedResponse = new CachedResponse
472486
{
473487
Created = ResponseHeaders.Date.Value,
474-
StatusCode = HttpContext.Response.StatusCode
488+
StatusCode = _httpContext.Response.StatusCode
475489
};
476490

477491
foreach (var header in ResponseHeaders.Headers)
@@ -495,7 +509,7 @@ internal void FinalizeCachingBody()
495509
{
496510
_cachedResponse.Body = ResponseCacheStream.BufferedStream.ToArray();
497511

498-
Cache.Set(_cacheKey, _cachedResponse, _cachedResponseValidFor);
512+
_cache.Set(_cacheKey, _cachedResponse, _cachedResponseValidFor);
499513
}
500514
}
501515

@@ -504,7 +518,7 @@ internal void OnResponseStarting()
504518
if (!ResponseStarted)
505519
{
506520
ResponseStarted = true;
507-
_responseTime = Clock.UtcNow;
521+
_responseTime = _clock.UtcNow;
508522

509523
FinalizeCachingHeaders();
510524
}
@@ -515,25 +529,25 @@ internal void ShimResponseStream()
515529
// TODO: Consider caching large responses on disk and serving them from there.
516530

517531
// Shim response stream
518-
OriginalResponseStream = HttpContext.Response.Body;
532+
OriginalResponseStream = _httpContext.Response.Body;
519533
ResponseCacheStream = new ResponseCacheStream(OriginalResponseStream);
520-
HttpContext.Response.Body = ResponseCacheStream;
534+
_httpContext.Response.Body = ResponseCacheStream;
521535

522536
// Shim IHttpSendFileFeature
523-
OriginalSendFileFeature = HttpContext.Features.Get<IHttpSendFileFeature>();
537+
OriginalSendFileFeature = _httpContext.Features.Get<IHttpSendFileFeature>();
524538
if (OriginalSendFileFeature != null)
525539
{
526-
HttpContext.Features.Set<IHttpSendFileFeature>(new SendFileFeatureWrapper(OriginalSendFileFeature, ResponseCacheStream));
540+
_httpContext.Features.Set<IHttpSendFileFeature>(new SendFileFeatureWrapper(OriginalSendFileFeature, ResponseCacheStream));
527541
}
528542
}
529543

530544
internal void UnshimResponseStream()
531545
{
532546
// Unshim response stream
533-
HttpContext.Response.Body = OriginalResponseStream;
547+
_httpContext.Response.Body = OriginalResponseStream;
534548

535549
// Unshim IHttpSendFileFeature
536-
HttpContext.Features.Set(OriginalSendFileFeature);
550+
_httpContext.Features.Set(OriginalSendFileFeature);
537551
}
538552

539553
private enum ResponseType

0 commit comments

Comments
 (0)