Use object indirection in HttpContextAccessor (#1066)
This commit is contained in:
parent
ea1ee2b68c
commit
49d785c934
|
|
@ -7,21 +7,35 @@ namespace Microsoft.AspNetCore.Http
|
||||||
{
|
{
|
||||||
public class HttpContextAccessor : IHttpContextAccessor
|
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
|
public HttpContext HttpContext
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var value = _httpContextCurrent.Value;
|
return _httpContextCurrent.Value?.Context;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
set
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
{
|
{
|
||||||
_httpContextAccessor.HttpContext = null;
|
_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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
var accessor = new HttpContextAccessor();
|
var accessor = new HttpContextAccessor();
|
||||||
|
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.TraceIdentifier = "1";
|
|
||||||
accessor.HttpContext = context;
|
accessor.HttpContext = context;
|
||||||
|
|
||||||
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -76,7 +75,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
|
|
||||||
// Null out the accessor
|
// Null out the accessor
|
||||||
accessor.HttpContext = null;
|
accessor.HttpContext = null;
|
||||||
context.TraceIdentifier = null;
|
|
||||||
|
|
||||||
waitForNullTcs.SetResult(null);
|
waitForNullTcs.SetResult(null);
|
||||||
|
|
||||||
|
|
@ -86,12 +84,11 @@ namespace Microsoft.AspNetCore.Http
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HttpContextAccessor_GettingHttpContextReturnsNullHttpContextIfDifferentTraceIdentifier()
|
public async Task HttpContextAccessor_GettingHttpContextReturnsNullHttpContextIfChanged()
|
||||||
{
|
{
|
||||||
var accessor = new HttpContextAccessor();
|
var accessor = new HttpContextAccessor();
|
||||||
|
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.TraceIdentifier = "1";
|
|
||||||
accessor.HttpContext = context;
|
accessor.HttpContext = context;
|
||||||
|
|
||||||
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -121,12 +118,8 @@ namespace Microsoft.AspNetCore.Http
|
||||||
|
|
||||||
await checkAsyncFlowTcs.Task;
|
await checkAsyncFlowTcs.Task;
|
||||||
|
|
||||||
// Reset the trace identifier on the first request
|
|
||||||
context.TraceIdentifier = null;
|
|
||||||
|
|
||||||
// Set a new http context
|
// Set a new http context
|
||||||
var context2 = new DefaultHttpContext();
|
var context2 = new DefaultHttpContext();
|
||||||
context2.TraceIdentifier = "2";
|
|
||||||
accessor.HttpContext = context2;
|
accessor.HttpContext = context2;
|
||||||
|
|
||||||
waitForNullTcs.SetResult(null);
|
waitForNullTcs.SetResult(null);
|
||||||
|
|
@ -142,7 +135,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
var accessor = new HttpContextAccessor();
|
var accessor = new HttpContextAccessor();
|
||||||
|
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.TraceIdentifier = "1";
|
|
||||||
accessor.HttpContext = context;
|
accessor.HttpContext = context;
|
||||||
|
|
||||||
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -172,7 +164,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
var accessor = new HttpContextAccessor();
|
var accessor = new HttpContextAccessor();
|
||||||
|
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
context.TraceIdentifier = "1";
|
|
||||||
accessor.HttpContext = context;
|
accessor.HttpContext = context;
|
||||||
|
|
||||||
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var checkAsyncFlowTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var context = contextFactory.Create(new FeatureCollection());
|
var context = contextFactory.Create(new FeatureCollection());
|
||||||
var traceIdentifier = context.TraceIdentifier;
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(context, accessor.HttpContext);
|
Assert.Same(context, accessor.HttpContext);
|
||||||
|
|
@ -42,7 +41,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
contextFactory.Dispose(context);
|
contextFactory.Dispose(context);
|
||||||
|
|
||||||
Assert.Null(accessor.HttpContext);
|
Assert.Null(accessor.HttpContext);
|
||||||
Assert.NotEqual(traceIdentifier, context.TraceIdentifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue