diff --git a/src/Http/Routing/perf/EndpointRoutingBenchmarkBase.cs b/src/Http/Routing/perf/EndpointRoutingBenchmarkBase.cs index 0af2021a72..3c6fabf7af 100644 --- a/src/Http/Routing/perf/EndpointRoutingBenchmarkBase.cs +++ b/src/Http/Routing/perf/EndpointRoutingBenchmarkBase.cs @@ -131,10 +131,9 @@ namespace Microsoft.AspNetCore.Routing protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext( object ambientValues = null) { - var feature = new EndpointSelectorContext { RouteValues = new RouteValueDictionary(ambientValues) }; + var context = new DefaultHttpContext(); - context.Features.Set(feature); - context.Features.Set(feature); + var feature = new EndpointSelectorContext(context) { RouteValues = new RouteValueDictionary(ambientValues) }; return (context, feature.RouteValues); } diff --git a/src/Http/Routing/perf/Matching/MatcherSingleEntryBenchmark.cs b/src/Http/Routing/perf/Matching/MatcherSingleEntryBenchmark.cs index 412ffdaef3..a9b1b1c067 100644 --- a/src/Http/Routing/perf/Matching/MatcherSingleEntryBenchmark.cs +++ b/src/Http/Routing/perf/Matching/MatcherSingleEntryBenchmark.cs @@ -18,8 +18,6 @@ namespace Microsoft.AspNetCore.Routing.Matching private Matcher _route; private Matcher _tree; - private EndpointSelectorContext _feature; - [GlobalSetup] public void Setup() { @@ -35,8 +33,6 @@ namespace Microsoft.AspNetCore.Routing.Matching _dfa = SetupMatcher(CreateDfaMatcherBuilder()); _route = SetupMatcher(new RouteMatcherBuilder()); _tree = SetupMatcher(new TreeRouterMatcherBuilder()); - - _feature = new EndpointSelectorContext(); } private Matcher SetupMatcher(MatcherBuilder builder) @@ -48,8 +44,8 @@ namespace Microsoft.AspNetCore.Routing.Matching [Benchmark(Baseline = true)] public async Task Baseline() { - var feature = _feature; var httpContext = Requests[0]; + var feature = new EndpointSelectorContext(httpContext); await _baseline.MatchAsync(httpContext, feature); Validate(httpContext, Endpoints[0], feature.Endpoint); @@ -58,8 +54,8 @@ namespace Microsoft.AspNetCore.Routing.Matching [Benchmark] public async Task Dfa() { - var feature = _feature; var httpContext = Requests[0]; + var feature = new EndpointSelectorContext(httpContext); await _dfa.MatchAsync(httpContext, feature); Validate(httpContext, Endpoints[0], feature.Endpoint); @@ -68,12 +64,11 @@ namespace Microsoft.AspNetCore.Routing.Matching [Benchmark] public async Task LegacyTreeRouter() { - var feature = _feature; - var httpContext = Requests[0]; + var feature = new EndpointSelectorContext(httpContext); - // This is required to make the legacy router implementation work with global routing. - httpContext.Features.Set(feature); + //// This is required to make the legacy router implementation work with global routing. + //httpContext.Features.Set(feature); await _tree.MatchAsync(httpContext, feature); Validate(httpContext, Endpoints[0], feature.Endpoint); @@ -82,14 +77,14 @@ namespace Microsoft.AspNetCore.Routing.Matching [Benchmark] public async Task LegacyRouter() { - var feature = _feature; var httpContext = Requests[0]; + var feature = new EndpointSelectorContext(httpContext); // This is required to make the legacy router implementation work with global routing. - httpContext.Features.Set(feature); + //httpContext.Features.Set(feature); await _route.MatchAsync(httpContext, feature); Validate(httpContext, Endpoints[0], feature.Endpoint); } } -} \ No newline at end of file +} diff --git a/src/Http/Routing/perf/Matching/TrivialMatcher.cs b/src/Http/Routing/perf/Matching/TrivialMatcher.cs index 3e00ce1999..2c4a5fd173 100644 --- a/src/Http/Routing/perf/Matching/TrivialMatcher.cs +++ b/src/Http/Routing/perf/Matching/TrivialMatcher.cs @@ -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. using System; @@ -30,11 +30,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - var path = httpContext.Request.Path.Value; if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/Http/Routing/src/EndpointRoutingMiddleware.cs b/src/Http/Routing/src/EndpointRoutingMiddleware.cs index 02b089dbca..043dbe87bc 100644 --- a/src/Http/Routing/src/EndpointRoutingMiddleware.cs +++ b/src/Http/Routing/src/EndpointRoutingMiddleware.cs @@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Routing public Task Invoke(HttpContext httpContext) { - var feature = new EndpointSelectorContext(); + var feature = new EndpointSelectorContext(httpContext); // 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. @@ -97,8 +97,6 @@ namespace Microsoft.AspNetCore.Routing { // Set the endpoint feature only on success. This means we won't overwrite any // existing state for related features unless we did something. - SetFeatures(httpContext, feature); - Log.MatchSuccess(_logger, feature); } else @@ -109,15 +107,6 @@ namespace Microsoft.AspNetCore.Routing return _next(httpContext); } - private static void SetFeatures(HttpContext httpContext, EndpointSelectorContext context) - { - // For back-compat EndpointSelectorContext implements IEndpointFeature, - // IRouteValuesFeature and IRoutingFeature - httpContext.Features.Set(context); - httpContext.Features.Set(context); - httpContext.Features.Set(context); - } - // Initialization is async to avoid blocking threads while reflection and things // of that nature take place. // diff --git a/src/Http/Routing/src/EndpointSelectorContext.cs b/src/Http/Routing/src/EndpointSelectorContext.cs index 1aed4f2227..ecd63dffaf 100644 --- a/src/Http/Routing/src/EndpointSelectorContext.cs +++ b/src/Http/Routing/src/EndpointSelectorContext.cs @@ -3,68 +3,52 @@ using System; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Http.Endpoints; namespace Microsoft.AspNetCore.Routing { - public sealed class EndpointSelectorContext : IEndpointFeature, IRouteValuesFeature, IRoutingFeature + public struct EndpointSelectorContext { - private RouteData _routeData; - private RouteValueDictionary _routeValues; + private HttpContext _httpContext; + + public EndpointSelectorContext(HttpContext httpContext) + { + _httpContext = httpContext; + } /// /// Gets or sets the selected for the current /// request. /// - public Endpoint Endpoint { get; set; } + public Endpoint Endpoint + { + get + { + return _httpContext.GetEndpoint(); + } + set + { + if (value != null) + { + _httpContext.SetEndpoint(value); + } + } + } /// /// Gets or sets the associated with the currrent /// request. /// public RouteValueDictionary RouteValues - { - get => _routeValues ?? (_routeValues = new RouteValueDictionary()); - set - { - _routeValues = value; - - // RouteData will be created next get with new Values - _routeData = null; - } - } - - /// - /// Gets or sets the for the current request. - /// - /// - /// The setter is not implemented. Use to set the route values. - /// - 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(); - if (dataTokensMetadata != null) - { - var dataTokens = _routeData.DataTokens; - foreach (var kvp in dataTokensMetadata.DataTokens) - { - _routeData.DataTokens.Add(kvp.Key, kvp.Value); - } - } - } - - return _routeData; + return _httpContext.Request.RouteValues; + } + set + { + _httpContext.Request.RouteValues = value; } - set => throw new NotSupportedException(); } } } diff --git a/src/Http/Routing/src/Matching/DefaultEndpointSelector.cs b/src/Http/Routing/src/Matching/DefaultEndpointSelector.cs index 113e0a4f01..33b6bc0324 100644 --- a/src/Http/Routing/src/Matching/DefaultEndpointSelector.cs +++ b/src/Http/Routing/src/Matching/DefaultEndpointSelector.cs @@ -21,11 +21,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - if (candidateSet == null) { throw new ArgumentNullException(nameof(candidateSet)); diff --git a/src/Http/Routing/src/Matching/DfaMatcher.cs b/src/Http/Routing/src/Matching/DfaMatcher.cs index 036351760a..7312459dde 100644 --- a/src/Http/Routing/src/Matching/DfaMatcher.cs +++ b/src/Http/Routing/src/Matching/DfaMatcher.cs @@ -34,11 +34,6 @@ namespace Microsoft.AspNetCore.Routing.Matching 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. var log = _logger.IsEnabled(LogLevel.Debug); diff --git a/src/Http/Routing/src/Matching/HostMatcherPolicy.cs b/src/Http/Routing/src/Matching/HostMatcherPolicy.cs index f99afdcfbd..4f52eb3932 100644 --- a/src/Http/Routing/src/Matching/HostMatcherPolicy.cs +++ b/src/Http/Routing/src/Matching/HostMatcherPolicy.cs @@ -77,11 +77,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - if (candidates == null) { throw new ArgumentNullException(nameof(candidates)); diff --git a/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs b/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs index 3111c55a67..4a6d17344f 100644 --- a/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs +++ b/src/Http/Routing/src/Matching/HttpMethodMatcherPolicy.cs @@ -94,11 +94,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - if (candidates == null) { throw new ArgumentNullException(nameof(candidates)); diff --git a/src/Http/Routing/test/UnitTests/DefaultLinkGeneratorTest.cs b/src/Http/Routing/test/UnitTests/DefaultLinkGeneratorTest.cs index ef05217153..41e8c55e64 100644 --- a/src/Http/Routing/test/UnitTests/DefaultLinkGeneratorTest.cs +++ b/src/Http/Routing/test/UnitTests/DefaultLinkGeneratorTest.cs @@ -635,12 +635,12 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(endpointControllerAction, endpointController, endpointEmpty, endpointControllerActionParameter); - var context = new EndpointSelectorContext() + var httpContext = CreateHttpContext(); + // This sets data on the feature directly in the HttpContext + var context = new EndpointSelectorContext(httpContext) { RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", }) }; - var httpContext = CreateHttpContext(); - httpContext.Features.Set(context); var values = new RouteValueDictionary(); for (int i = 0; i < routeNames.Length; i++) @@ -678,12 +678,11 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin); - var context = new EndpointSelectorContext() + var httpContext = CreateHttpContext(); + var context = new EndpointSelectorContext(httpContext) { RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index", }) }; - var httpContext = CreateHttpContext(); - httpContext.Features.Set(context); var values = new RouteValueDictionary(); for (int i = 0; i < routeNames.Length; i++) @@ -721,9 +720,7 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(homeIndex, homeLogin); - var context = new EndpointSelectorContext(); var httpContext = CreateHttpContext(); - httpContext.Features.Set(context); var values = new RouteValueDictionary(); for (int i = 0; i < routeNames.Length; i++) diff --git a/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs b/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs index 17832a6b48..9c1d59cfb2 100644 --- a/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs +++ b/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs @@ -45,10 +45,10 @@ namespace Microsoft.AspNetCore.Routing var httpContext = new DefaultHttpContext(); httpContext.RequestServices = new ServiceProvider(); - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = null, - }); + }; RequestDelegate next = (c) => { @@ -77,10 +77,10 @@ namespace Microsoft.AspNetCore.Routing return Task.CompletedTask; }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test"), - }); + }; RequestDelegate next = (c) => { @@ -108,10 +108,10 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test"), - }); + }; var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, RouteOptions); @@ -131,10 +131,10 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test"), - }); + }; httpContext.Items[EndpointMiddleware.AuthorizationMiddlewareInvokedKey] = true; @@ -155,10 +155,11 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test"), - }); + }; + var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, routeOptions); @@ -178,10 +179,10 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test"), - }); + }; var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, RouteOptions); @@ -201,10 +202,10 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test"), - }); + }; httpContext.Items[EndpointMiddleware.CorsMiddlewareInvokedKey] = true; @@ -225,10 +226,11 @@ namespace Microsoft.AspNetCore.Routing RequestServices = new ServiceProvider() }; - httpContext.Features.Set(new EndpointSelectorContext() + new EndpointSelectorContext(httpContext) { Endpoint = new Endpoint(_ => Task.CompletedTask, new EndpointMetadataCollection(Mock.Of()), "Test"), - }); + }; + var routeOptions = Options.Create(new RouteOptions { SuppressCheckForUnhandledSecurityMetadata = true }); var middleware = new EndpointMiddleware(NullLogger.Instance, _ => Task.CompletedTask, routeOptions); diff --git a/src/Http/Routing/test/UnitTests/EndpointRoutingMiddlewareTest.cs b/src/Http/Routing/test/UnitTests/EndpointRoutingMiddlewareTest.cs index 97f33a2fbe..0dc1331316 100644 --- a/src/Http/Routing/test/UnitTests/EndpointRoutingMiddlewareTest.cs +++ b/src/Http/Routing/test/UnitTests/EndpointRoutingMiddlewareTest.cs @@ -132,12 +132,8 @@ namespace Microsoft.AspNetCore.Routing private HttpContext CreateHttpContext() { - var context = new EndpointSelectorContext(); - var httpContext = new DefaultHttpContext(); - httpContext.Features.Set(context); - httpContext.Features.Set(context); - + var context = new EndpointSelectorContext(httpContext); httpContext.RequestServices = new TestServiceProvider(); return httpContext; diff --git a/src/Http/Routing/test/UnitTests/EndpointSelectorContextTest.cs b/src/Http/Routing/test/UnitTests/EndpointSelectorContextTest.cs index 05ca8850da..ed102cf26c 100644 --- a/src/Http/Routing/test/UnitTests/EndpointSelectorContextTest.cs +++ b/src/Http/Routing/test/UnitTests/EndpointSelectorContextTest.cs @@ -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. using System.Linq; @@ -9,51 +9,52 @@ using Xunit; namespace Microsoft.AspNetCore.Routing { + // The IRoutingFeature is TBD, we need a way to make it lazy. public class EndpointSelectorContextTest { - [Fact] - public void RouteData_CanIntializeDataTokens_WithMetadata() - { - // Arrange - var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", }); + //[Fact] + //public void RouteData_CanIntializeDataTokens_WithMetadata() + //{ + // // Arrange + // var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", }); - var context = new EndpointSelectorContext() - { - Endpoint = new RouteEndpoint( - TestConstants.EmptyRequestDelegate, - RoutePatternFactory.Parse("/"), - 0, - new EndpointMetadataCollection(new DataTokensMetadata(expected)), - "test"), - }; + // var context = new EndpointSelectorContext() + // { + // Endpoint = new RouteEndpoint( + // TestConstants.EmptyRequestDelegate, + // RoutePatternFactory.Parse("/"), + // 0, + // new EndpointMetadataCollection(new DataTokensMetadata(expected)), + // "test"), + // }; - // Act - var routeData = ((IRoutingFeature)context).RouteData; + // // Act + // var routeData = ((IRoutingFeature)context).RouteData; - // Assert - Assert.NotSame(expected, routeData.DataTokens); - Assert.Equal(expected.OrderBy(kvp => kvp.Key), routeData.DataTokens.OrderBy(kvp => kvp.Key)); - } + // // Assert + // Assert.NotSame(expected, routeData.DataTokens); + // Assert.Equal(expected.OrderBy(kvp => kvp.Key), routeData.DataTokens.OrderBy(kvp => kvp.Key)); + //} - [Fact] - public void RouteData_DataTokensIsEmpty_WithoutMetadata() - { - // Arrange - var context = new EndpointSelectorContext() - { - Endpoint = new RouteEndpoint( - TestConstants.EmptyRequestDelegate, - RoutePatternFactory.Parse("/"), - 0, - new EndpointMetadataCollection(), - "test"), - }; + //[Fact] + //public void RouteData_DataTokensIsEmpty_WithoutMetadata() + //{ + // // Arrange + // var context = new EndpointSelectorContext() + // { + // Endpoint = new RouteEndpoint( + // TestConstants.EmptyRequestDelegate, + // RoutePatternFactory.Parse("/"), + // 0, + // new EndpointMetadataCollection(), + // "test"), + // }; - // Act - var routeData = ((IRoutingFeature)context).RouteData; + // // Act + // var routeData = ((IRoutingFeature)context).RouteData; - // Assert - Assert.Empty(routeData.DataTokens); - } + // // Assert + // Assert.Empty(routeData.DataTokens); + //} } } diff --git a/src/Http/Routing/test/UnitTests/LinkGeneratorEndpointNameExtensionsTest.cs b/src/Http/Routing/test/UnitTests/LinkGeneratorEndpointNameExtensionsTest.cs index e2a9382281..d887710288 100644 --- a/src/Http/Routing/test/UnitTests/LinkGeneratorEndpointNameExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/LinkGeneratorEndpointNameExtensionsTest.cs @@ -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. using System.Linq; @@ -26,12 +26,11 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); - var context = new EndpointSelectorContext() + var httpContext = CreateHttpContext(); + var context = new EndpointSelectorContext(httpContext) { RouteValues = new RouteValueDictionary(new { p = "5", }) }; - var httpContext = CreateHttpContext(); - httpContext.Features.Set(context); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); var values = new { query = "some?query", }; diff --git a/src/Http/Routing/test/UnitTests/LinkGeneratorRouteValuesAddressExtensionsTest.cs b/src/Http/Routing/test/UnitTests/LinkGeneratorRouteValuesAddressExtensionsTest.cs index 5f8d509d54..56f8e7f4ea 100644 --- a/src/Http/Routing/test/UnitTests/LinkGeneratorRouteValuesAddressExtensionsTest.cs +++ b/src/Http/Routing/test/UnitTests/LinkGeneratorRouteValuesAddressExtensionsTest.cs @@ -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. using System.Linq; @@ -32,12 +32,11 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); - var context = new EndpointSelectorContext() + var httpContext = CreateHttpContext(); + var context = new EndpointSelectorContext(httpContext) { RouteValues = new RouteValueDictionary(new { action = "Index", }) }; - var httpContext = CreateHttpContext(); - httpContext.Features.Set(context); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); // Act diff --git a/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs b/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs index 19ded38c8b..fbb0654f8b 100644 --- a/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs +++ b/src/Http/Routing/test/UnitTests/LinkGeneratorTestBase.cs @@ -19,13 +19,11 @@ namespace Microsoft.AspNetCore.Routing { var httpContext = new DefaultHttpContext(); - var context = new EndpointSelectorContext + var context = new EndpointSelectorContext(httpContext) { RouteValues = new RouteValueDictionary(ambientValues) }; - httpContext.Features.Set(context); - httpContext.Features.Set(context); return httpContext; } diff --git a/src/Http/Routing/test/UnitTests/Matching/BarebonesMatcher.cs b/src/Http/Routing/test/UnitTests/Matching/BarebonesMatcher.cs index f1501b423f..87eecf6b0f 100644 --- a/src/Http/Routing/test/UnitTests/Matching/BarebonesMatcher.cs +++ b/src/Http/Routing/test/UnitTests/Matching/BarebonesMatcher.cs @@ -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. using System; @@ -27,11 +27,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - var path = httpContext.Request.Path.Value; for (var i = 0; i < Matchers.Length; i++) { @@ -133,4 +128,4 @@ namespace Microsoft.AspNetCore.Routing.Matching } } } -} \ No newline at end of file +} diff --git a/src/Http/Routing/test/UnitTests/Matching/DefaultEndpointSelectorTest.cs b/src/Http/Routing/test/UnitTests/Matching/DefaultEndpointSelectorTest.cs index 9d67c126d6..f1ce9dacbe 100644 --- a/src/Http/Routing/test/UnitTests/Matching/DefaultEndpointSelectorTest.cs +++ b/src/Http/Routing/test/UnitTests/Matching/DefaultEndpointSelectorTest.cs @@ -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. using System; @@ -174,10 +174,8 @@ namespace Microsoft.AspNetCore.Routing.Matching private static (HttpContext httpContext, EndpointSelectorContext context) CreateContext() { - var context = new EndpointSelectorContext(); var httpContext = new DefaultHttpContext(); - httpContext.Features.Set(context); - httpContext.Features.Set(context); + var context = new EndpointSelectorContext(httpContext); return (httpContext, context); } diff --git a/src/Http/Routing/test/UnitTests/Matching/DfaMatcherTest.cs b/src/Http/Routing/test/UnitTests/Matching/DfaMatcherTest.cs index e82f870043..a238bc0b39 100644 --- a/src/Http/Routing/test/UnitTests/Matching/DfaMatcherTest.cs +++ b/src/Http/Routing/test/UnitTests/Matching/DfaMatcherTest.cs @@ -633,7 +633,7 @@ namespace Microsoft.AspNetCore.Routing.Matching var (httpContext, context) = CreateContext(); httpContext.Request.Path = "/test/17"; - + // Act await matcher.MatchAsync(httpContext, context); @@ -727,11 +727,8 @@ namespace Microsoft.AspNetCore.Routing.Matching private (HttpContext httpContext, EndpointSelectorContext context) CreateContext() { - var context = new EndpointSelectorContext(); - var httpContext = new DefaultHttpContext(); - httpContext.Features.Set(context); - httpContext.Features.Set(context); + var context = new EndpointSelectorContext(httpContext); return (httpContext, context); } diff --git a/src/Http/Routing/test/UnitTests/Matching/HostMatcherPolicyIntegrationTestBase.cs b/src/Http/Routing/test/UnitTests/Matching/HostMatcherPolicyIntegrationTestBase.cs index c812ad8d4c..d707084908 100644 --- a/src/Http/Routing/test/UnitTests/Matching/HostMatcherPolicyIntegrationTestBase.cs +++ b/src/Http/Routing/test/UnitTests/Matching/HostMatcherPolicyIntegrationTestBase.cs @@ -303,9 +303,7 @@ namespace Microsoft.AspNetCore.Routing.Matching httpContext.Request.Path = path; httpContext.Request.Scheme = scheme; - var context = new EndpointSelectorContext(); - httpContext.Features.Set(context); - httpContext.Features.Set(context); + var context = new EndpointSelectorContext(httpContext); return (httpContext, context); } diff --git a/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs b/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs index 258e9b0a92..ce561c1f8d 100644 --- a/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs +++ b/src/Http/Routing/test/UnitTests/Matching/HttpMethodMatcherPolicyIntegrationTestBase.cs @@ -352,9 +352,7 @@ namespace Microsoft.AspNetCore.Routing.Matching httpContext.Request.Headers[AccessControlRequestMethod] = httpMethod; } - var context = new EndpointSelectorContext(); - httpContext.Features.Set(context); - httpContext.Features.Set(context); + var context = new EndpointSelectorContext(httpContext); return (httpContext, context); } diff --git a/src/Http/Routing/test/UnitTests/Matching/MatcherConformanceTest.cs b/src/Http/Routing/test/UnitTests/Matching/MatcherConformanceTest.cs index c7615bc19c..98a08f0b1f 100644 --- a/src/Http/Routing/test/UnitTests/Matching/MatcherConformanceTest.cs +++ b/src/Http/Routing/test/UnitTests/Matching/MatcherConformanceTest.cs @@ -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. using System; @@ -21,13 +21,10 @@ namespace Microsoft.AspNetCore.Routing.Matching httpContext.Request.Path = path; httpContext.RequestServices = CreateServices(); - var context = new EndpointSelectorContext() + var context = new EndpointSelectorContext(httpContext) { RouteValues = new RouteValueDictionary() }; - httpContext.Features.Set(context); - httpContext.Features.Set(context); - return (httpContext, context); } diff --git a/src/Http/Routing/test/UnitTests/Matching/RouteMatcher.cs b/src/Http/Routing/test/UnitTests/Matching/RouteMatcher.cs index 39eaee6cdd..24b4a006cb 100644 --- a/src/Http/Routing/test/UnitTests/Matching/RouteMatcher.cs +++ b/src/Http/Routing/test/UnitTests/Matching/RouteMatcher.cs @@ -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. using System; @@ -24,11 +24,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - var routeContext = new RouteContext(httpContext); await _inner.RouteAsync(routeContext); diff --git a/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs b/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs index da068f5198..734ed64f03 100644 --- a/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs +++ b/src/Http/Routing/test/UnitTests/Matching/RouteMatcherBuilder.cs @@ -1,10 +1,11 @@ -// 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. using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.TestObjects; @@ -95,12 +96,7 @@ namespace Microsoft.AspNetCore.Routing.Matching public async Task RouteAsync(RouteContext routeContext) { - var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get(); - - // This is needed due to a quirk of our tests - they reuse the endpoint feature - // across requests. - context.Endpoint = null; - + var context = new EndpointSelectorContext(routeContext.HttpContext); await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores)); if (context.Endpoint != null) { diff --git a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcher.cs b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcher.cs index c7477e4611..ec1c60105a 100644 --- a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcher.cs +++ b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcher.cs @@ -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. using System; @@ -26,11 +26,6 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new ArgumentNullException(nameof(httpContext)); } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - var routeContext = new RouteContext(httpContext); await _inner.RouteAsync(routeContext); diff --git a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs index 5d4e25dfff..e464422592 100644 --- a/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs +++ b/src/Http/Routing/test/UnitTests/Matching/TreeRouterMatcherBuilder.cs @@ -99,11 +99,7 @@ namespace Microsoft.AspNetCore.Routing.Matching public async Task RouteAsync(RouteContext routeContext) { - var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get(); - - // This is needed due to a quirk of our tests - they reuse the endpoint feature. - context.Endpoint = null; - + var context = new EndpointSelectorContext(routeContext.HttpContext); await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores)); if (context.Endpoint != null) { diff --git a/src/Http/Routing/test/testassets/RoutingSandbox/Program.cs b/src/Http/Routing/test/testassets/RoutingSandbox/Program.cs index 35e4e44c01..4f2d60bc76 100644 --- a/src/Http/Routing/test/testassets/RoutingSandbox/Program.cs +++ b/src/Http/Routing/test/testassets/RoutingSandbox/Program.cs @@ -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. using System; diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs index a917555871..fabaf7c0c4 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; @@ -18,16 +19,18 @@ using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { internal partial class HttpProtocol : IHttpRequestFeature, - IHttpResponseFeature, - IResponseBodyPipeFeature, - IRequestBodyPipeFeature, - IHttpUpgradeFeature, - IHttpConnectionFeature, - IHttpRequestLifetimeFeature, - IHttpRequestIdentifierFeature, - IHttpBodyControlFeature, - IHttpMaxRequestBodySizeFeature, - IHttpResponseStartFeature + IHttpResponseFeature, + IResponseBodyPipeFeature, + IRequestBodyPipeFeature, + IHttpUpgradeFeature, + IHttpConnectionFeature, + IHttpRequestLifetimeFeature, + IHttpRequestIdentifierFeature, + IHttpBodyControlFeature, + IHttpMaxRequestBodySizeFeature, + IHttpResponseStartFeature, + IEndpointFeature, + IRouteValuesFeature { // 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. @@ -258,6 +261,24 @@ 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() { _currentIHttpMinRequestBodyDataRateFeature = this; diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs index bd0edf1357..aaeffffe1b 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs @@ -21,6 +21,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private static readonly Type IServiceProvidersFeatureType = typeof(IServiceProvidersFeature); private static readonly Type IHttpRequestLifetimeFeatureType = typeof(IHttpRequestLifetimeFeature); 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 IQueryFeatureType = typeof(IQueryFeature); private static readonly Type IFormFeatureType = typeof(IFormFeature); @@ -47,6 +49,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private object _currentIServiceProvidersFeature; private object _currentIHttpRequestLifetimeFeature; private object _currentIHttpConnectionFeature; + private object _currentIRouteValuesFeature; + private object _currentIEndpointFeature; private object _currentIHttpAuthenticationFeature; private object _currentIQueryFeature; private object _currentIFormFeature; @@ -82,6 +86,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentIHttpMaxRequestBodySizeFeature = this; _currentIHttpBodyControlFeature = this; _currentIHttpResponseStartFeature = this; + _currentIRouteValuesFeature = this; + _currentIEndpointFeature = this; _currentIServiceProvidersFeature = null; _currentIHttpAuthenticationFeature = null; @@ -183,6 +189,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { feature = _currentIHttpConnectionFeature; } + else if (key == IRouteValuesFeatureType) + { + feature = _currentIRouteValuesFeature; + } + else if (key == IEndpointFeatureType) + { + feature = _currentIEndpointFeature; + } else if (key == IHttpAuthenticationFeatureType) { feature = _currentIHttpAuthenticationFeature; @@ -295,6 +309,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { _currentIHttpConnectionFeature = value; } + else if (key == IRouteValuesFeatureType) + { + _currentIRouteValuesFeature = value; + } + else if (key == IEndpointFeatureType) + { + _currentIEndpointFeature = value; + } else if (key == IHttpAuthenticationFeatureType) { _currentIHttpAuthenticationFeature = value; @@ -405,6 +427,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { 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)) { feature = (TFeature)_currentIHttpAuthenticationFeature; @@ -521,6 +551,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { _currentIHttpConnectionFeature = feature; } + else if (typeof(TFeature) == typeof(IRouteValuesFeature)) + { + _currentIRouteValuesFeature = feature; + } + else if (typeof(TFeature) == typeof(IEndpointFeature)) + { + _currentIEndpointFeature = feature; + } else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) { _currentIHttpAuthenticationFeature = feature; @@ -629,6 +667,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { yield return new KeyValuePair(IHttpConnectionFeatureType, _currentIHttpConnectionFeature); } + if (_currentIRouteValuesFeature != null) + { + yield return new KeyValuePair(IRouteValuesFeatureType, _currentIRouteValuesFeature); + } + if (_currentIEndpointFeature != null) + { + yield return new KeyValuePair(IEndpointFeatureType, _currentIEndpointFeature); + } if (_currentIHttpAuthenticationFeature != null) { yield return new KeyValuePair(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs index e7d2dd154b..c721b2661a 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs @@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Internal; +using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; @@ -64,6 +65,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private readonly HttpConnectionContext _context; private DefaultHttpContext _httpContext; + private RouteValueDictionary _routeValues; protected string _methodText = null; private string _scheme = null; diff --git a/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs index f61df14ff2..1be680d38a 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs @@ -19,6 +19,8 @@ namespace CodeGenerator "IServiceProvidersFeature", "IHttpRequestLifetimeFeature", "IHttpConnectionFeature", + "IRouteValuesFeature", + "IEndpointFeature" }; var commonFeatures = new[] @@ -70,7 +72,9 @@ namespace CodeGenerator "IHttpConnectionFeature", "IHttpMaxRequestBodySizeFeature", "IHttpBodyControlFeature", - "IHttpResponseStartFeature" + "IHttpResponseStartFeature", + "IRouteValuesFeature", + "IEndpointFeature" }; var usings = $@"