Revert "Make endpoint routing allocation free in common scenarios"

This reverts commit 264ae1acd1.
This commit is contained in:
David Fowler 2019-04-26 02:10:25 -07:00
parent 264ae1acd1
commit b2457d25f7
31 changed files with 240 additions and 212 deletions

View File

@ -131,9 +131,10 @@ namespace Microsoft.AspNetCore.Routing
protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext( protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext(
object ambientValues = null) object ambientValues = null)
{ {
var feature = new EndpointSelectorContext { RouteValues = new RouteValueDictionary(ambientValues) };
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
var feature = new EndpointSelectorContext(context) { RouteValues = new RouteValueDictionary(ambientValues) }; context.Features.Set<IEndpointFeature>(feature);
context.Features.Set<IRouteValuesFeature>(feature);
return (context, feature.RouteValues); return (context, feature.RouteValues);
} }

View File

@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
private Matcher _route; private Matcher _route;
private Matcher _tree; private Matcher _tree;
private EndpointSelectorContext _feature;
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
@ -33,6 +35,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
_dfa = SetupMatcher(CreateDfaMatcherBuilder()); _dfa = SetupMatcher(CreateDfaMatcherBuilder());
_route = SetupMatcher(new RouteMatcherBuilder()); _route = SetupMatcher(new RouteMatcherBuilder());
_tree = SetupMatcher(new TreeRouterMatcherBuilder()); _tree = SetupMatcher(new TreeRouterMatcherBuilder());
_feature = new EndpointSelectorContext();
} }
private Matcher SetupMatcher(MatcherBuilder builder) private Matcher SetupMatcher(MatcherBuilder builder)
@ -44,8 +48,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public async Task Baseline() public async Task Baseline()
{ {
var feature = _feature;
var httpContext = Requests[0]; var httpContext = Requests[0];
var feature = new EndpointSelectorContext(httpContext);
await _baseline.MatchAsync(httpContext, feature); await _baseline.MatchAsync(httpContext, feature);
Validate(httpContext, Endpoints[0], feature.Endpoint); Validate(httpContext, Endpoints[0], feature.Endpoint);
@ -54,8 +58,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
[Benchmark] [Benchmark]
public async Task Dfa() public async Task Dfa()
{ {
var feature = _feature;
var httpContext = Requests[0]; var httpContext = Requests[0];
var feature = new EndpointSelectorContext(httpContext);
await _dfa.MatchAsync(httpContext, feature); await _dfa.MatchAsync(httpContext, feature);
Validate(httpContext, Endpoints[0], feature.Endpoint); Validate(httpContext, Endpoints[0], feature.Endpoint);
@ -64,11 +68,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
[Benchmark] [Benchmark]
public async Task LegacyTreeRouter() public async Task LegacyTreeRouter()
{ {
var httpContext = Requests[0]; var feature = _feature;
var feature = new EndpointSelectorContext(httpContext);
//// This is required to make the legacy router implementation work with global routing. var httpContext = Requests[0];
//httpContext.Features.Set<IEndpointFeature>(feature);
// This is required to make the legacy router implementation work with global routing.
httpContext.Features.Set<IEndpointFeature>(feature);
await _tree.MatchAsync(httpContext, feature); await _tree.MatchAsync(httpContext, feature);
Validate(httpContext, Endpoints[0], feature.Endpoint); Validate(httpContext, Endpoints[0], feature.Endpoint);
@ -77,14 +82,14 @@ namespace Microsoft.AspNetCore.Routing.Matching
[Benchmark] [Benchmark]
public async Task LegacyRouter() public async Task LegacyRouter()
{ {
var feature = _feature;
var httpContext = Requests[0]; var httpContext = Requests[0];
var feature = new EndpointSelectorContext(httpContext);
// This is required to make the legacy router implementation work with global routing. // This is required to make the legacy router implementation work with global routing.
//httpContext.Features.Set<IEndpointFeature>(feature); httpContext.Features.Set<IEndpointFeature>(feature);
await _route.MatchAsync(httpContext, feature); await _route.MatchAsync(httpContext, feature);
Validate(httpContext, Endpoints[0], feature.Endpoint); Validate(httpContext, Endpoints[0], feature.Endpoint);
} }
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -30,6 +30,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var path = httpContext.Request.Path.Value; var path = httpContext.Request.Path.Value;
if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase)) if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase))
{ {

View File

@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Routing
public Task Invoke(HttpContext httpContext) public Task Invoke(HttpContext httpContext)
{ {
var feature = new EndpointSelectorContext(httpContext); var feature = new EndpointSelectorContext();
// There's an inherent race condition between waiting for init and accessing the matcher // There's an inherent race condition between waiting for init and accessing the matcher
// this is OK because once `_matcher` is initialized, it will not be set to null again. // this is OK because once `_matcher` is initialized, it will not be set to null again.
@ -97,6 +97,8 @@ namespace Microsoft.AspNetCore.Routing
{ {
// Set the endpoint feature only on success. This means we won't overwrite any // Set the endpoint feature only on success. This means we won't overwrite any
// existing state for related features unless we did something. // existing state for related features unless we did something.
SetFeatures(httpContext, feature);
Log.MatchSuccess(_logger, feature); Log.MatchSuccess(_logger, feature);
} }
else else
@ -107,6 +109,15 @@ namespace Microsoft.AspNetCore.Routing
return _next(httpContext); return _next(httpContext);
} }
private static void SetFeatures(HttpContext httpContext, EndpointSelectorContext context)
{
// For back-compat EndpointSelectorContext implements IEndpointFeature,
// IRouteValuesFeature and IRoutingFeature
httpContext.Features.Set<IRoutingFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
httpContext.Features.Set<IEndpointFeature>(context);
}
// Initialization is async to avoid blocking threads while reflection and things // Initialization is async to avoid blocking threads while reflection and things
// of that nature take place. // of that nature take place.
// //

View File

@ -3,37 +3,20 @@
using System; using System;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Endpoints; using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
public struct EndpointSelectorContext public sealed class EndpointSelectorContext : IEndpointFeature, IRouteValuesFeature, IRoutingFeature
{ {
private HttpContext _httpContext; private RouteData _routeData;
private RouteValueDictionary _routeValues;
public EndpointSelectorContext(HttpContext httpContext)
{
_httpContext = httpContext;
}
/// <summary> /// <summary>
/// Gets or sets the selected <see cref="Http.Endpoint"/> for the current /// Gets or sets the selected <see cref="Http.Endpoint"/> for the current
/// request. /// request.
/// </summary> /// </summary>
public Endpoint Endpoint public Endpoint Endpoint { get; set; }
{
get
{
return _httpContext.GetEndpoint();
}
set
{
if (value != null)
{
_httpContext.SetEndpoint(value);
}
}
}
/// <summary> /// <summary>
/// Gets or sets the <see cref="RouteValueDictionary"/> associated with the currrent /// Gets or sets the <see cref="RouteValueDictionary"/> associated with the currrent
@ -41,14 +24,47 @@ namespace Microsoft.AspNetCore.Routing
/// </summary> /// </summary>
public RouteValueDictionary RouteValues public RouteValueDictionary RouteValues
{ {
get get => _routeValues ?? (_routeValues = new RouteValueDictionary());
{
return _httpContext.Request.RouteValues;
}
set set
{ {
_httpContext.Request.RouteValues = value; _routeValues = value;
// RouteData will be created next get with new Values
_routeData = null;
} }
} }
/// <summary>
/// Gets or sets the <see cref="RouteData"/> for the current request.
/// </summary>
/// <remarks>
/// The setter is not implemented. Use <see cref="RouteValues"/> to set the route values.
/// </remarks>
RouteData IRoutingFeature.RouteData
{
get
{
if (_routeData == null)
{
_routeData = _routeValues == null ? new RouteData() : new RouteData(_routeValues);
// Note: DataTokens won't update if someone else overwrites the Endpoint
// after route values has been set. This seems find since endpoints are a new
// feature and DataTokens are for back-compat.
var dataTokensMetadata = Endpoint?.Metadata.GetMetadata<IDataTokensMetadata>();
if (dataTokensMetadata != null)
{
var dataTokens = _routeData.DataTokens;
foreach (var kvp in dataTokensMetadata.DataTokens)
{
_routeData.DataTokens.Add(kvp.Key, kvp.Value);
}
}
}
return _routeData;
}
set => throw new NotSupportedException();
}
} }
} }

View File

@ -21,6 +21,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (candidateSet == null) if (candidateSet == null)
{ {
throw new ArgumentNullException(nameof(candidateSet)); throw new ArgumentNullException(nameof(candidateSet));

View File

@ -34,6 +34,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// All of the logging we do here is at level debug, so we can get away with doing a single check. // All of the logging we do here is at level debug, so we can get away with doing a single check.
var log = _logger.IsEnabled(LogLevel.Debug); var log = _logger.IsEnabled(LogLevel.Debug);

View File

@ -77,6 +77,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (candidates == null) if (candidates == null)
{ {
throw new ArgumentNullException(nameof(candidates)); throw new ArgumentNullException(nameof(candidates));

View File

@ -94,6 +94,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (candidates == null) if (candidates == null)
{ {
throw new ArgumentNullException(nameof(candidates)); throw new ArgumentNullException(nameof(candidates));

View File

@ -635,12 +635,12 @@ namespace Microsoft.AspNetCore.Routing
var linkGenerator = CreateLinkGenerator(endpointControllerAction, endpointController, endpointEmpty, endpointControllerActionParameter); var linkGenerator = CreateLinkGenerator(endpointControllerAction, endpointController, endpointEmpty, endpointControllerActionParameter);
var httpContext = CreateHttpContext(); var context = new EndpointSelectorContext()
// This sets data on the feature directly in the HttpContext
var context = new EndpointSelectorContext(httpContext)
{ {
RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", }) RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", })
}; };
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
var values = new RouteValueDictionary(); var values = new RouteValueDictionary();
for (int i = 0; i < routeNames.Length; i++) for (int i = 0; i < routeNames.Length; i++)
@ -678,11 +678,12 @@ namespace Microsoft.AspNetCore.Routing
var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin); var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin);
var httpContext = CreateHttpContext(); var context = new EndpointSelectorContext()
var context = new EndpointSelectorContext(httpContext)
{ {
RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", }) RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", })
}; };
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
var values = new RouteValueDictionary(); var values = new RouteValueDictionary();
for (int i = 0; i < routeNames.Length; i++) for (int i = 0; i < routeNames.Length; i++)
@ -720,7 +721,9 @@ namespace Microsoft.AspNetCore.Routing
var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin); var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin);
var context = new EndpointSelectorContext();
var httpContext = CreateHttpContext(); var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
var values = new RouteValueDictionary(); var values = new RouteValueDictionary();
for (int i = 0; i < routeNames.Length; i++) for (int i = 0; i < routeNames.Length; i++)

View File

@ -45,10 +45,10 @@ namespace Microsoft.AspNetCore.Routing
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.RequestServices = new ServiceProvider(); httpContext.RequestServices = new ServiceProvider();
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = null, Endpoint = null,
}; });
RequestDelegate next = (c) => RequestDelegate next = (c) =>
{ {
@ -77,10 +77,10 @@ namespace Microsoft.AspNetCore.Routing
return Task.CompletedTask; return Task.CompletedTask;
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test"), Endpoint = new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test"),
}; });
RequestDelegate next = (c) => RequestDelegate next = (c) =>
{ {
@ -108,10 +108,10 @@ namespace Microsoft.AspNetCore.Routing
RequestServices = new ServiceProvider() RequestServices = new ServiceProvider()
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"), Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"),
}; });
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, RouteOptions); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, RouteOptions);
@ -131,10 +131,10 @@ namespace Microsoft.AspNetCore.Routing
RequestServices = new ServiceProvider() RequestServices = new ServiceProvider()
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"), Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"),
}; });
httpContext.Items[EndpointMiddleware.AuthorizationMiddlewareInvokedKey] = true; httpContext.Items[EndpointMiddleware.AuthorizationMiddlewareInvokedKey] = true;
@ -155,11 +155,10 @@ namespace Microsoft.AspNetCore.Routing
RequestServices = new ServiceProvider() RequestServices = new ServiceProvider()
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"), Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"),
}; });
var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true });
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, routeOptions); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, routeOptions);
@ -179,10 +178,10 @@ namespace Microsoft.AspNetCore.Routing
RequestServices = new ServiceProvider() RequestServices = new ServiceProvider()
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test"), Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test"),
}; });
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, RouteOptions); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, RouteOptions);
@ -202,10 +201,10 @@ namespace Microsoft.AspNetCore.Routing
RequestServices = new ServiceProvider() RequestServices = new ServiceProvider()
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test"), Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<ICorsMetadata>()), "Test"),
}; });
httpContext.Items[EndpointMiddleware.CorsMiddlewareInvokedKey] = true; httpContext.Items[EndpointMiddleware.CorsMiddlewareInvokedKey] = true;
@ -226,11 +225,10 @@ namespace Microsoft.AspNetCore.Routing
RequestServices = new ServiceProvider() RequestServices = new ServiceProvider()
}; };
new EndpointSelectorContext(httpContext) httpContext.Features.Set<IEndpointFeature>(new EndpointSelectorContext()
{ {
Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"), Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"),
}; });
var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true });
var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, routeOptions); var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, _ => Task.CompletedTask, routeOptions);

View File

@ -132,8 +132,12 @@ namespace Microsoft.AspNetCore.Routing
private HttpContext CreateHttpContext() private HttpContext CreateHttpContext()
{ {
var context = new EndpointSelectorContext();
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
var context = new EndpointSelectorContext(httpContext); httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
httpContext.RequestServices = new TestServiceProvider(); httpContext.RequestServices = new TestServiceProvider();
return httpContext; return httpContext;

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq; using System.Linq;
@ -9,52 +9,51 @@ using Xunit;
namespace Microsoft.AspNetCore.Routing namespace Microsoft.AspNetCore.Routing
{ {
// The IRoutingFeature is TBD, we need a way to make it lazy.
public class EndpointSelectorContextTest public class EndpointSelectorContextTest
{ {
//[Fact] [Fact]
//public void RouteData_CanIntializeDataTokens_WithMetadata() public void RouteData_CanIntializeDataTokens_WithMetadata()
//{ {
// // Arrange // Arrange
// var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", }); var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", });
// var context = new EndpointSelectorContext() var context = new EndpointSelectorContext()
// { {
// Endpoint = new RouteEndpoint( Endpoint = new RouteEndpoint(
// TestConstants.EmptyRequestDelegate, TestConstants.EmptyRequestDelegate,
// RoutePatternFactory.Parse("/"), RoutePatternFactory.Parse("/"),
// 0, 0,
// new EndpointMetadataCollection(new DataTokensMetadata(expected)), new EndpointMetadataCollection(new DataTokensMetadata(expected)),
// "test"), "test"),
// }; };
// // Act // Act
// var routeData = ((IRoutingFeature)context).RouteData; var routeData = ((IRoutingFeature)context).RouteData;
// // Assert // Assert
// Assert.NotSame(expected, routeData.DataTokens); Assert.NotSame(expected, routeData.DataTokens);
// Assert.Equal(expected.OrderBy(kvp => kvp.Key), routeData.DataTokens.OrderBy(kvp => kvp.Key)); Assert.Equal(expected.OrderBy(kvp => kvp.Key), routeData.DataTokens.OrderBy(kvp => kvp.Key));
//} }
//[Fact] [Fact]
//public void RouteData_DataTokensIsEmpty_WithoutMetadata() public void RouteData_DataTokensIsEmpty_WithoutMetadata()
//{ {
// // Arrange // Arrange
// var context = new EndpointSelectorContext() var context = new EndpointSelectorContext()
// { {
// Endpoint = new RouteEndpoint( Endpoint = new RouteEndpoint(
// TestConstants.EmptyRequestDelegate, TestConstants.EmptyRequestDelegate,
// RoutePatternFactory.Parse("/"), RoutePatternFactory.Parse("/"),
// 0, 0,
// new EndpointMetadataCollection(), new EndpointMetadataCollection(),
// "test"), "test"),
// }; };
// // Act // Act
// var routeData = ((IRoutingFeature)context).RouteData; var routeData = ((IRoutingFeature)context).RouteData;
// // Assert // Assert
// Assert.Empty(routeData.DataTokens); Assert.Empty(routeData.DataTokens);
//} }
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq; using System.Linq;
@ -26,11 +26,12 @@ namespace Microsoft.AspNetCore.Routing
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
var httpContext = CreateHttpContext(); var context = new EndpointSelectorContext()
var context = new EndpointSelectorContext(httpContext)
{ {
RouteValues = new RouteValueDictionary(new { p = "5", }) RouteValues = new RouteValueDictionary(new { p = "5", })
}; };
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?");
var values = new { query = "some?query", }; var values = new { query = "some?query", };

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq; using System.Linq;
@ -32,11 +32,12 @@ namespace Microsoft.AspNetCore.Routing
var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2);
var httpContext = CreateHttpContext(); var context = new EndpointSelectorContext()
var context = new EndpointSelectorContext(httpContext)
{ {
RouteValues = new RouteValueDictionary(new { action = "Index", }) RouteValues = new RouteValueDictionary(new { action = "Index", })
}; };
var httpContext = CreateHttpContext();
httpContext.Features.Set<IRouteValuesFeature>(context);
httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?");
// Act // Act

View File

@ -19,11 +19,13 @@ namespace Microsoft.AspNetCore.Routing
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
var context = new EndpointSelectorContext(httpContext) var context = new EndpointSelectorContext
{ {
RouteValues = new RouteValueDictionary(ambientValues) RouteValues = new RouteValueDictionary(ambientValues)
}; };
httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
return httpContext; return httpContext;
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -27,6 +27,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var path = httpContext.Request.Path.Value; var path = httpContext.Request.Path.Value;
for (var i = 0; i < Matchers.Length; i++) for (var i = 0; i < Matchers.Length; i++)
{ {
@ -128,4 +133,4 @@ namespace Microsoft.AspNetCore.Routing.Matching
} }
} }
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -174,8 +174,10 @@ namespace Microsoft.AspNetCore.Routing.Matching
private static (HttpContext httpContext, EndpointSelectorContext context) CreateContext() private static (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
{ {
var context = new EndpointSelectorContext();
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
var context = new EndpointSelectorContext(httpContext); httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
return (httpContext, context); return (httpContext, context);
} }

View File

@ -633,7 +633,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
var (httpContext, context) = CreateContext(); var (httpContext, context) = CreateContext();
httpContext.Request.Path = "/test/17"; httpContext.Request.Path = "/test/17";
// Act // Act
await matcher.MatchAsync(httpContext, context); await matcher.MatchAsync(httpContext, context);
@ -727,8 +727,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
private (HttpContext httpContext, EndpointSelectorContext context) CreateContext() private (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
{ {
var context = new EndpointSelectorContext();
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
var context = new EndpointSelectorContext(httpContext); httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
return (httpContext, context); return (httpContext, context);
} }

View File

@ -303,7 +303,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
httpContext.Request.Path = path; httpContext.Request.Path = path;
httpContext.Request.Scheme = scheme; httpContext.Request.Scheme = scheme;
var context = new EndpointSelectorContext(httpContext); var context = new EndpointSelectorContext();
httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
return (httpContext, context); return (httpContext, context);
} }

View File

@ -352,7 +352,9 @@ namespace Microsoft.AspNetCore.Routing.Matching
httpContext.Request.Headers[AccessControlRequestMethod] = httpMethod; httpContext.Request.Headers[AccessControlRequestMethod] = httpMethod;
} }
var context = new EndpointSelectorContext(httpContext); var context = new EndpointSelectorContext();
httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
return (httpContext, context); return (httpContext, context);
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -21,10 +21,13 @@ namespace Microsoft.AspNetCore.Routing.Matching
httpContext.Request.Path = path; httpContext.Request.Path = path;
httpContext.RequestServices = CreateServices(); httpContext.RequestServices = CreateServices();
var context = new EndpointSelectorContext(httpContext) var context = new EndpointSelectorContext()
{ {
RouteValues = new RouteValueDictionary() RouteValues = new RouteValueDictionary()
}; };
httpContext.Features.Set<IEndpointFeature>(context);
httpContext.Features.Set<IRouteValuesFeature>(context);
return (httpContext, context); return (httpContext, context);
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -24,6 +24,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var routeContext = new RouteContext(httpContext); var routeContext = new RouteContext(httpContext);
await _inner.RouteAsync(routeContext); await _inner.RouteAsync(routeContext);

View File

@ -1,11 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.AspNetCore.Routing.TestObjects; using Microsoft.AspNetCore.Routing.TestObjects;
@ -96,7 +95,12 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task RouteAsync(RouteContext routeContext) public async Task RouteAsync(RouteContext routeContext)
{ {
var context = new EndpointSelectorContext(routeContext.HttpContext); var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get<IEndpointFeature>();
// This is needed due to a quirk of our tests - they reuse the endpoint feature
// across requests.
context.Endpoint = null;
await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores)); await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores));
if (context.Endpoint != null) if (context.Endpoint != null)
{ {

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -26,6 +26,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
throw new ArgumentNullException(nameof(httpContext)); throw new ArgumentNullException(nameof(httpContext));
} }
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var routeContext = new RouteContext(httpContext); var routeContext = new RouteContext(httpContext);
await _inner.RouteAsync(routeContext); await _inner.RouteAsync(routeContext);

View File

@ -99,7 +99,11 @@ namespace Microsoft.AspNetCore.Routing.Matching
public async Task RouteAsync(RouteContext routeContext) public async Task RouteAsync(RouteContext routeContext)
{ {
var context = new EndpointSelectorContext(routeContext.HttpContext); var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get<IEndpointFeature>();
// This is needed due to a quirk of our tests - they reuse the endpoint feature.
context.Endpoint = null;
await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores)); await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores));
if (context.Endpoint != null) if (context.Endpoint != null)
{ {

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;

View File

@ -10,7 +10,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
@ -19,18 +18,16 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
internal partial class HttpProtocol : IHttpRequestFeature, internal partial class HttpProtocol : IHttpRequestFeature,
IHttpResponseFeature, IHttpResponseFeature,
IResponseBodyPipeFeature, IResponseBodyPipeFeature,
IRequestBodyPipeFeature, IRequestBodyPipeFeature,
IHttpUpgradeFeature, IHttpUpgradeFeature,
IHttpConnectionFeature, IHttpConnectionFeature,
IHttpRequestLifetimeFeature, IHttpRequestLifetimeFeature,
IHttpRequestIdentifierFeature, IHttpRequestIdentifierFeature,
IHttpBodyControlFeature, IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature, IHttpMaxRequestBodySizeFeature,
IHttpResponseStartFeature, IHttpResponseStartFeature
IEndpointFeature,
IRouteValuesFeature
{ {
// NOTE: When feature interfaces are added to or removed from this HttpProtocol class implementation, // NOTE: When feature interfaces are added to or removed from this HttpProtocol class implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated. // then the list of `implementedFeatures` in the generated code project MUST also be updated.
@ -261,24 +258,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
} }
Endpoint IEndpointFeature.Endpoint
{
get;
set;
}
RouteValueDictionary IRouteValuesFeature.RouteValues
{
get
{
return _routeValues ??= new RouteValueDictionary();
}
set
{
_routeValues = value;
}
}
protected void ResetHttp1Features() protected void ResetHttp1Features()
{ {
_currentIHttpMinRequestBodyDataRateFeature = this; _currentIHttpMinRequestBodyDataRateFeature = this;

View File

@ -21,8 +21,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private static readonly Type IServiceProvidersFeatureType = typeof(IServiceProvidersFeature); private static readonly Type IServiceProvidersFeatureType = typeof(IServiceProvidersFeature);
private static readonly Type IHttpRequestLifetimeFeatureType = typeof(IHttpRequestLifetimeFeature); private static readonly Type IHttpRequestLifetimeFeatureType = typeof(IHttpRequestLifetimeFeature);
private static readonly Type IHttpConnectionFeatureType = typeof(IHttpConnectionFeature); private static readonly Type IHttpConnectionFeatureType = typeof(IHttpConnectionFeature);
private static readonly Type IRouteValuesFeatureType = typeof(IRouteValuesFeature);
private static readonly Type IEndpointFeatureType = typeof(IEndpointFeature);
private static readonly Type IHttpAuthenticationFeatureType = typeof(IHttpAuthenticationFeature); private static readonly Type IHttpAuthenticationFeatureType = typeof(IHttpAuthenticationFeature);
private static readonly Type IQueryFeatureType = typeof(IQueryFeature); private static readonly Type IQueryFeatureType = typeof(IQueryFeature);
private static readonly Type IFormFeatureType = typeof(IFormFeature); private static readonly Type IFormFeatureType = typeof(IFormFeature);
@ -49,8 +47,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private object _currentIServiceProvidersFeature; private object _currentIServiceProvidersFeature;
private object _currentIHttpRequestLifetimeFeature; private object _currentIHttpRequestLifetimeFeature;
private object _currentIHttpConnectionFeature; private object _currentIHttpConnectionFeature;
private object _currentIRouteValuesFeature;
private object _currentIEndpointFeature;
private object _currentIHttpAuthenticationFeature; private object _currentIHttpAuthenticationFeature;
private object _currentIQueryFeature; private object _currentIQueryFeature;
private object _currentIFormFeature; private object _currentIFormFeature;
@ -86,8 +82,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_currentIHttpMaxRequestBodySizeFeature = this; _currentIHttpMaxRequestBodySizeFeature = this;
_currentIHttpBodyControlFeature = this; _currentIHttpBodyControlFeature = this;
_currentIHttpResponseStartFeature = this; _currentIHttpResponseStartFeature = this;
_currentIRouteValuesFeature = this;
_currentIEndpointFeature = this;
_currentIServiceProvidersFeature = null; _currentIServiceProvidersFeature = null;
_currentIHttpAuthenticationFeature = null; _currentIHttpAuthenticationFeature = null;
@ -189,14 +183,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
feature = _currentIHttpConnectionFeature; feature = _currentIHttpConnectionFeature;
} }
else if (key == IRouteValuesFeatureType)
{
feature = _currentIRouteValuesFeature;
}
else if (key == IEndpointFeatureType)
{
feature = _currentIEndpointFeature;
}
else if (key == IHttpAuthenticationFeatureType) else if (key == IHttpAuthenticationFeatureType)
{ {
feature = _currentIHttpAuthenticationFeature; feature = _currentIHttpAuthenticationFeature;
@ -309,14 +295,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
_currentIHttpConnectionFeature = value; _currentIHttpConnectionFeature = value;
} }
else if (key == IRouteValuesFeatureType)
{
_currentIRouteValuesFeature = value;
}
else if (key == IEndpointFeatureType)
{
_currentIEndpointFeature = value;
}
else if (key == IHttpAuthenticationFeatureType) else if (key == IHttpAuthenticationFeatureType)
{ {
_currentIHttpAuthenticationFeature = value; _currentIHttpAuthenticationFeature = value;
@ -427,14 +405,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
feature = (TFeature)_currentIHttpConnectionFeature; feature = (TFeature)_currentIHttpConnectionFeature;
} }
else if (typeof(TFeature) == typeof(IRouteValuesFeature))
{
feature = (TFeature)_currentIRouteValuesFeature;
}
else if (typeof(TFeature) == typeof(IEndpointFeature))
{
feature = (TFeature)_currentIEndpointFeature;
}
else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature))
{ {
feature = (TFeature)_currentIHttpAuthenticationFeature; feature = (TFeature)_currentIHttpAuthenticationFeature;
@ -551,14 +521,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
_currentIHttpConnectionFeature = feature; _currentIHttpConnectionFeature = feature;
} }
else if (typeof(TFeature) == typeof(IRouteValuesFeature))
{
_currentIRouteValuesFeature = feature;
}
else if (typeof(TFeature) == typeof(IEndpointFeature))
{
_currentIEndpointFeature = feature;
}
else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature))
{ {
_currentIHttpAuthenticationFeature = feature; _currentIHttpAuthenticationFeature = feature;
@ -667,14 +629,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature); yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature);
} }
if (_currentIRouteValuesFeature != null)
{
yield return new KeyValuePair<Type, object>(IRouteValuesFeatureType, _currentIRouteValuesFeature);
}
if (_currentIEndpointFeature != null)
{
yield return new KeyValuePair<Type, object>(IEndpointFeatureType, _currentIEndpointFeature);
}
if (_currentIHttpAuthenticationFeature != null) if (_currentIHttpAuthenticationFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature); yield return new KeyValuePair<Type, object>(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature);

View File

@ -17,7 +17,6 @@ using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
@ -65,7 +64,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private readonly HttpConnectionContext _context; private readonly HttpConnectionContext _context;
private DefaultHttpContext _httpContext; private DefaultHttpContext _httpContext;
private RouteValueDictionary _routeValues;
protected string _methodText = null; protected string _methodText = null;
private string _scheme = null; private string _scheme = null;

View File

@ -19,8 +19,6 @@ namespace CodeGenerator
"IServiceProvidersFeature", "IServiceProvidersFeature",
"IHttpRequestLifetimeFeature", "IHttpRequestLifetimeFeature",
"IHttpConnectionFeature", "IHttpConnectionFeature",
"IRouteValuesFeature",
"IEndpointFeature"
}; };
var commonFeatures = new[] var commonFeatures = new[]
@ -72,9 +70,7 @@ namespace CodeGenerator
"IHttpConnectionFeature", "IHttpConnectionFeature",
"IHttpMaxRequestBodySizeFeature", "IHttpMaxRequestBodySizeFeature",
"IHttpBodyControlFeature", "IHttpBodyControlFeature",
"IHttpResponseStartFeature", "IHttpResponseStartFeature"
"IRouteValuesFeature",
"IEndpointFeature"
}; };
var usings = $@" var usings = $@"