Fix endpoint routing statefulness.

- In the case that other middleware change the path of an `HttpContext` and cause middleware to re-invoke we used to short-circuit on second time through the middleware pipeline, now we allow routing to occur.
- Added unit tests to validate the clearing of state.

#11233
This commit is contained in:
N. Taylor Mullen 2019-07-18 16:38:15 -07:00
parent ec81582dff
commit 9a6881b0e8
2 changed files with 42 additions and 6 deletions

View File

@ -51,6 +51,8 @@ namespace Microsoft.AspNetCore.Routing
if (endpoint != null)
{
Log.MatchSkipped(_logger, endpoint);
// Someone else set the endpoint, we'll let them handle the unsetting.
return _next(httpContext);
}
@ -87,7 +89,7 @@ namespace Microsoft.AspNetCore.Routing
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Task SetRoutingAndContinue(HttpContext httpContext)
private async Task SetRoutingAndContinue(HttpContext httpContext)
{
// If there was no mutation of the endpoint then log failure
var endpoint = httpContext.GetEndpoint();
@ -107,7 +109,16 @@ namespace Microsoft.AspNetCore.Routing
Log.MatchSuccess(_logger, endpoint);
}
return _next(httpContext);
try
{
await _next(httpContext);
}
finally
{
// We unset the endpoint after calling through to the next middleware. This enables any future calls into
// endpoint routing don't no-op from there already being an endpoint set.
httpContext.SetEndpoint(endpoint: null);
}
}
// Initialization is async to avoid blocking threads while reflection and things

View File

@ -21,7 +21,30 @@ namespace Microsoft.AspNetCore.Routing
public class EndpointRoutingMiddlewareTest
{
[Fact]
public async Task Invoke_OnCall_SetsEndpointFeature()
public async Task Invoke_OnException_ResetsEndpoint()
{
// Arrange
var httpContext = CreateHttpContext();
var middleware = CreateMiddleware(next: context => throw new Exception());
// Act
try
{
await middleware.Invoke(httpContext);
}
catch
{
// Do nothing, we expect the test to throw.
}
// Assert
var endpoint = httpContext.GetEndpoint();
Assert.Null(endpoint);
}
[Fact]
public async Task Invoke_OnCall_SetsEndpointFeatureAndResetsEndpoint()
{
// Arrange
var httpContext = CreateHttpContext();
@ -34,14 +57,16 @@ namespace Microsoft.AspNetCore.Routing
// Assert
var endpointFeature = httpContext.Features.Get<IEndpointFeature>();
Assert.NotNull(endpointFeature);
Assert.Null(endpointFeature.Endpoint);
}
[Fact]
public async Task Invoke_SkipsRouting_IfEndpointSet()
public async Task Invoke_SkipsRoutingAndMaintainsEndpoint_IfEndpointSet()
{
// Arrange
var httpContext = CreateHttpContext();
httpContext.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(), "myapp"));
var expectedEndpoint = new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(), "myapp");
httpContext.SetEndpoint(expectedEndpoint);
var middleware = CreateMiddleware();
@ -50,7 +75,7 @@ namespace Microsoft.AspNetCore.Routing
// Assert
var endpoint = httpContext.GetEndpoint();
Assert.NotNull(endpoint);
Assert.Same(expectedEndpoint, endpoint);
Assert.Equal("myapp", endpoint.DisplayName);
}