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

Commit b57dcbe

Browse files
committed
Feedback
1 parent 36916d4 commit b57dcbe

File tree

5 files changed

+186
-23
lines changed

5 files changed

+186
-23
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
7+
{
8+
/// <summary>
9+
/// Abstracts the system clock to facilitate testing.
10+
/// </summary>
11+
internal interface ISystemClock
12+
{
13+
/// <summary>
14+
/// Retrieves the current system time in UTC.
15+
/// </summary>
16+
DateTimeOffset UtcNow { get; }
17+
}
18+
}

src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCacheStream.cs

Lines changed: 119 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.IO;
56
using System.Threading;
67
using System.Threading.Tasks;
@@ -18,6 +19,8 @@ public ResponseCacheStream(Stream innerStream)
1819

1920
public MemoryStream BufferedStream { get; } = new MemoryStream();
2021

22+
public bool BufferingEnabled { get; set; } = true;
23+
2124
public override bool CanRead => _innerStream.CanRead;
2225

2326
public override bool CanSeek => _innerStream.CanSeek;
@@ -32,16 +35,45 @@ public override long Position
3235
set { _innerStream.Position = value; }
3336
}
3437

38+
public void DisableBuffering()
39+
{
40+
BufferingEnabled = false;
41+
BufferedStream.Dispose();
42+
}
43+
3544
public override void SetLength(long value)
3645
{
37-
BufferedStream.SetLength(value);
38-
_innerStream.SetLength(value);
46+
try
47+
{
48+
_innerStream.SetLength(value);
49+
if (BufferingEnabled)
50+
{
51+
BufferedStream.SetLength(value);
52+
}
53+
}
54+
catch
55+
{
56+
DisableBuffering();
57+
throw;
58+
}
3959
}
4060

4161
public override long Seek(long offset, SeekOrigin origin)
4262
{
43-
BufferedStream.Seek(offset, origin);
44-
return _innerStream.Seek(offset, origin);
63+
try
64+
{
65+
var position = _innerStream.Seek(offset, origin);
66+
if (BufferingEnabled)
67+
{
68+
BufferedStream.Seek(offset, origin);
69+
}
70+
return position;
71+
}
72+
catch
73+
{
74+
DisableBuffering();
75+
throw;
76+
}
4577
}
4678

4779
public override void Flush()
@@ -56,20 +88,97 @@ public override int Read(byte[] buffer, int offset, int count)
5688

5789
public override void Write(byte[] buffer, int offset, int count)
5890
{
59-
BufferedStream.Write(buffer, offset, count);
60-
_innerStream.Write(buffer, offset, count);
91+
try
92+
{
93+
_innerStream.Write(buffer, offset, count);
94+
if (BufferingEnabled)
95+
{
96+
BufferedStream.Write(buffer, offset, count);
97+
}
98+
}
99+
catch
100+
{
101+
DisableBuffering();
102+
throw;
103+
}
61104
}
62105

63106
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
64107
{
65-
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
66-
await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
108+
try
109+
{
110+
await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
111+
if (BufferingEnabled)
112+
{
113+
await BufferedStream.WriteAsync(buffer, offset, count, cancellationToken);
114+
}
115+
}
116+
catch
117+
{
118+
DisableBuffering();
119+
throw;
120+
}
67121
}
68122

69123
public override void WriteByte(byte value)
70124
{
71-
BufferedStream.WriteByte(value);
72-
_innerStream.WriteByte(value);
125+
try
126+
{
127+
_innerStream.WriteByte(value);
128+
if (BufferingEnabled)
129+
{
130+
BufferedStream.WriteByte(value);
131+
}
132+
}
133+
catch
134+
{
135+
DisableBuffering();
136+
throw;
137+
}
138+
}
139+
140+
#if NETSTANDARD1_3
141+
public IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
142+
#else
143+
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
144+
#endif
145+
{
146+
return ToIAsyncResult(WriteAsync(buffer, offset, count), callback, state);
147+
}
148+
#if NETSTANDARD1_3
149+
public void EndWrite(IAsyncResult asyncResult)
150+
#else
151+
public override void EndWrite(IAsyncResult asyncResult)
152+
#endif
153+
{
154+
if (asyncResult == null)
155+
{
156+
throw new ArgumentNullException(nameof(asyncResult));
157+
}
158+
((Task)asyncResult).GetAwaiter().GetResult();
159+
}
160+
161+
private static IAsyncResult ToIAsyncResult(Task task, AsyncCallback callback, object state)
162+
{
163+
var tcs = new TaskCompletionSource<int>(state);
164+
task.ContinueWith(t =>
165+
{
166+
if (t.IsFaulted)
167+
{
168+
tcs.TrySetException(t.Exception.InnerExceptions);
169+
}
170+
else if (t.IsCanceled)
171+
{
172+
tcs.TrySetCanceled();
173+
}
174+
else
175+
{
176+
tcs.TrySetResult(0);
177+
}
178+
179+
callback?.Invoke(tcs.Task);
180+
}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
181+
return tcs.Task;
73182
}
74183
}
75184
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
6+
namespace Microsoft.AspNetCore.ResponseCaching.Internal
7+
{
8+
/// <summary>
9+
/// Provides access to the normal system clock.
10+
/// </summary>
11+
internal class SystemClock : ISystemClock
12+
{
13+
/// <summary>
14+
/// Retrieves the current system time in UTC.
15+
/// </summary>
16+
public DateTimeOffset UtcNow
17+
{
18+
get
19+
{
20+
return DateTimeOffset.UtcNow;
21+
}
22+
}
23+
}
24+
}

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingContext.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,28 @@ public class ResponseCachingContext
3030
internal DateTimeOffset _responseTime;
3131

3232
public ResponseCachingContext(HttpContext httpContext, IResponseCache cache)
33+
: this(httpContext, cache, new SystemClock())
34+
{
35+
}
36+
37+
internal ResponseCachingContext(HttpContext httpContext, IResponseCache cache, ISystemClock clock)
3338
{
3439
if (cache == null)
3540
{
3641
throw new ArgumentNullException(nameof(cache));
3742
}
38-
3943
if (httpContext == null)
4044
{
4145
throw new ArgumentNullException(nameof(httpContext));
4246
}
47+
if (clock == null)
48+
{
49+
throw new ArgumentNullException(nameof(clock));
50+
}
4351

4452
HttpContext = httpContext;
4553
Cache = cache;
54+
Clock = clock;
4655
}
4756

4857
internal bool CacheResponse
@@ -54,14 +63,16 @@ internal bool CacheResponse
5463
// TODO: apparent age vs corrected age value
5564
var responseAge = _responseTime - ResponseHeaders.Date ?? TimeSpan.Zero;
5665

57-
_cacheResponse = ResponseStarted && ResponseIsCacheable() && EntryIsFresh(ResponseHeaders, responseAge, verifyAgainstRequest: false);
66+
_cacheResponse = ResponseIsCacheable() && EntryIsFresh(ResponseHeaders, responseAge, verifyAgainstRequest: false);
5867
}
5968
return _cacheResponse.Value;
6069
}
6170
}
6271

6372
internal bool ResponseStarted { get; set; }
6473

74+
private ISystemClock Clock { get; }
75+
6576
private HttpContext HttpContext { get; }
6677

6778
private IResponseCache Cache { get; }
@@ -341,7 +352,7 @@ internal async Task<bool> TryServeFromCacheAsync()
341352
var cachedResponse = cacheEntry as CachedResponse;
342353
var cachedResponseHeaders = new ResponseHeaders(cachedResponse.Headers);
343354

344-
_responseTime = DateTimeOffset.UtcNow;
355+
_responseTime = Clock.UtcNow;
345356
var age = _responseTime - cachedResponse.Created;
346357
age = age > TimeSpan.Zero ? age : TimeSpan.Zero;
347358

@@ -440,22 +451,22 @@ internal void FinalizeCachingHeaders()
440451

441452
foreach (var header in ResponseHeaders.Headers)
442453
{
443-
if (header.Key != HeaderNames.Age
444-
&& header.Key != HeaderNames.SetCookie)
454+
if (!string.Equals(header.Key, HeaderNames.Age, StringComparison.OrdinalIgnoreCase)
455+
&& !string.Equals(header.Key, HeaderNames.SetCookie, StringComparison.OrdinalIgnoreCase))
445456
{
446457
_cachedResponse.Headers.Add(header);
447458
}
448459
}
449460
}
450461
else
451462
{
452-
UnhookResponseStream();
463+
ResponseCacheStream.DisableBuffering();
453464
}
454465
}
455466

456467
internal void FinalizeCachingBody()
457468
{
458-
if (CacheResponse)
469+
if (CacheResponse && ResponseCacheStream.BufferingEnabled)
459470
{
460471
_cachedResponse.Body = ResponseCacheStream.BufferedStream.ToArray();
461472

@@ -469,7 +480,7 @@ internal void OnResponseStarting()
469480
if (!ResponseStarted)
470481
{
471482
ResponseStarted = true;
472-
_responseTime = DateTimeOffset.UtcNow;
483+
_responseTime = Clock.UtcNow;
473484

474485
FinalizeCachingHeaders();
475486
}

src/Microsoft.AspNetCore.ResponseCaching/ResponseCachingMiddleware.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ namespace Microsoft.AspNetCore.ResponseCaching
1010
// http://tools.ietf.org/html/rfc7234
1111
public class ResponseCachingMiddleware
1212
{
13-
private readonly RequestDelegate _next;
14-
private readonly IResponseCache _cache;
1513
private static readonly Func<object, Task> OnStartingCallback = state =>
1614
{
1715
((ResponseCachingContext)state).OnResponseStarting();
1816
return Task.FromResult(0);
1917
};
2018

19+
private readonly RequestDelegate _next;
20+
private readonly IResponseCache _cache;
21+
2122
public ResponseCachingMiddleware(RequestDelegate next, IResponseCache cache)
2223
{
2324
if (cache == null)
@@ -47,11 +48,11 @@ public async Task Invoke(HttpContext context)
4748
return;
4849
}
4950

51+
// Hook up to listen to the response stream
52+
cachingContext.HookResponseStream();
53+
5054
try
5155
{
52-
// Hook up to listen to the response stream
53-
cachingContext.HookResponseStream();
54-
5556
// Subscribe to OnStarting event
5657
context.Response.OnStarting(OnStartingCallback, cachingContext);
5758

0 commit comments

Comments
 (0)