Use object indirection in HttpContextAccessor (#1066)

This commit is contained in:
Ben Adams 2018-11-16 06:34:44 +00:00 committed by David Fowler
parent ea1ee2b68c
commit 49d785c934
4 changed files with 21 additions and 22 deletions

View File

@ -7,21 +7,35 @@ namespace Microsoft.AspNetCore.Http
{
public class HttpContextAccessor : IHttpContextAccessor
{
private static AsyncLocal<(string traceIdentifier, HttpContext context)> _httpContextCurrent = new AsyncLocal<(string traceIdentifier, HttpContext context)>();
private static AsyncLocal<HttpContextHolder> _httpContextCurrent = new AsyncLocal<HttpContextHolder>();
public HttpContext HttpContext
{
get
{
var value = _httpContextCurrent.Value;
// Only return the context if the stored request id matches the stored trace identifier
// context.TraceIdentifier is cleared by HttpContextFactory.Dispose.
return value.traceIdentifier == value.context?.TraceIdentifier ? value.context : null;
return _httpContextCurrent.Value?.Context;
}
set
{
_httpContextCurrent.Value = (value?.TraceIdentifier, value);
var holder = _httpContextCurrent.Value;
if (holder != null)
{
// Clear current HttpContext trapped in the AsyncLocals, as its done.
holder.Context = null;
}
if (value != null)
{
// Use an object indirection to hold the HttpContext in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
_httpContextCurrent.Value = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
}

View File

@ -53,10 +53,6 @@ namespace Microsoft.AspNetCore.Http
{
_httpContextAccessor.HttpContext = null;
}
// Null out the TraceIdentifier here as a sign that this request is done,
// the HttpContextAccessor implementation relies on this to detect that the request is over
httpContext.TraceIdentifier = null;
}
}
}

View File

@ -44,7 +44,6 @@ namespace Microsoft.AspNetCore.Http
var accessor = new HttpContextAccessor();
var context = new DefaultHttpContext();
context.TraceIdentifier = "1";
accessor.HttpContext = context;
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -76,7 +75,6 @@ namespace Microsoft.AspNetCore.Http
// Null out the accessor
accessor.HttpContext = null;
context.TraceIdentifier = null;
waitForNullTcs.SetResult(null);
@ -86,12 +84,11 @@ namespace Microsoft.AspNetCore.Http
}
[Fact]
public async Task HttpContextAccessor_GettingHttpContextReturnsNullHttpContextIfDifferentTraceIdentifier()
public async Task HttpContextAccessor_GettingHttpContextReturnsNullHttpContextIfChanged()
{
var accessor = new HttpContextAccessor();
var context = new DefaultHttpContext();
context.TraceIdentifier = "1";
accessor.HttpContext = context;
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -121,12 +118,8 @@ namespace Microsoft.AspNetCore.Http
await checkAsyncFlowTcs.Task;
// Reset the trace identifier on the first request
context.TraceIdentifier = null;
// Set a new http context
var context2 = new DefaultHttpContext();
context2.TraceIdentifier = "2";
accessor.HttpContext = context2;
waitForNullTcs.SetResult(null);
@ -142,7 +135,6 @@ namespace Microsoft.AspNetCore.Http
var accessor = new HttpContextAccessor();
var context = new DefaultHttpContext();
context.TraceIdentifier = "1";
accessor.HttpContext = context;
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -172,7 +164,6 @@ namespace Microsoft.AspNetCore.Http
var accessor = new HttpContextAccessor();
var context = new DefaultHttpContext();
context.TraceIdentifier = "1";
accessor.HttpContext = context;
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);

View File

@ -34,7 +34,6 @@ namespace Microsoft.AspNetCore.Http
// Act
var context = contextFactory.Create(new FeatureCollection());
var traceIdentifier = context.TraceIdentifier;
// Assert
Assert.Same(context, accessor.HttpContext);
@ -42,7 +41,6 @@ namespace Microsoft.AspNetCore.Http
contextFactory.Dispose(context);
Assert.Null(accessor.HttpContext);
Assert.NotEqual(traceIdentifier, context.TraceIdentifier);
}
[Fact]