diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs index 9e3f050d8d..58c50f1225 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/EndpointRoutingBenchmarkBase.cs @@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Routing protected (HttpContext httpContext, RouteValueDictionary ambientValues) CreateCurrentRequestContext( object ambientValues = null) { - var feature = new EndpointFeature { RouteValues = new RouteValueDictionary(ambientValues) }; + var feature = new EndpointSelectorContext { RouteValues = new RouteValueDictionary(ambientValues) }; var context = new DefaultHttpContext(); context.Features.Set(feature); context.Features.Set(feature); diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs index a934316c1f..5f437fad33 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherAzureBenchmark.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Routing.Matching private Matcher _dfa; private int[] _samples; - private EndpointFeature _feature; + private EndpointSelectorContext _feature; [GlobalSetup] public void Setup() @@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Routing.Matching _baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder()); _dfa = SetupMatcher(CreateDfaMatcherBuilder()); - _feature = new EndpointFeature(); + _feature = new EndpointSelectorContext(); } [Benchmark(Baseline = true, OperationsPerInvoke = SampleCount)] diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs index 96b938825c..8219f02b78 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherFindCandidateSetSmallEntryCountBenchmark.cs @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Routing.Matching _baseline = (TrivialMatcher)SetupMatcher(new TrivialMatcherBuilder()); _dfa = (DfaMatcher)SetupMatcher(CreateDfaMatcherBuilder()); - _feature = new EndpointFeature(); + _feature = new EndpointSelectorContext(); } private void SetupEndpoints() diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs index 3657b42c8b..059e97239c 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherGithubBenchmark.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Routing.Matching private BarebonesMatcher _baseline; private Matcher _dfa; - private EndpointFeature _feature; + private EndpointSelectorContext _feature; [GlobalSetup] public void Setup() @@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Routing.Matching _baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder()); _dfa = SetupMatcher(CreateDfaMatcherBuilder()); - _feature = new EndpointFeature(); + _feature = new EndpointSelectorContext(); } [Benchmark(Baseline = true, OperationsPerInvoke = EndpointCount)] diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs index 1c15e43862..412ffdaef3 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/MatcherSingleEntryBenchmark.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Routing.Matching private Matcher _route; private Matcher _tree; - private EndpointFeature _feature; + private EndpointSelectorContext _feature; [GlobalSetup] public void Setup() @@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Routing.Matching _route = SetupMatcher(new RouteMatcherBuilder()); _tree = SetupMatcher(new TreeRouterMatcherBuilder()); - _feature = new EndpointFeature(); + _feature = new EndpointSelectorContext(); } private Matcher SetupMatcher(MatcherBuilder builder) diff --git a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/TrivialMatcher.cs b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/TrivialMatcher.cs index ffe2428fe3..3e00ce1999 100644 --- a/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/TrivialMatcher.cs +++ b/benchmarks/Microsoft.AspNetCore.Routing.Performance/Matching/TrivialMatcher.cs @@ -23,23 +23,23 @@ namespace Microsoft.AspNetCore.Routing.Matching _candidates = new Candidate[] { new Candidate(endpoint), }; } - public sealed override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } - if (feature == null) + if (context == null) { - throw new ArgumentNullException(nameof(feature)); + throw new ArgumentNullException(nameof(context)); } var path = httpContext.Request.Path.Value; if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase)) { - feature.Endpoint = _endpoint; - feature.RouteValues = new RouteValueDictionary(); + context.Endpoint = _endpoint; + context.RouteValues = new RouteValueDictionary(); } return Task.CompletedTask; diff --git a/build/dependencies.props b/build/dependencies.props index d0951b7b77..0015a2a4ad 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -34,7 +34,7 @@ 3.0.0-alpha1-10549 2.0.9 2.1.3 - 2.2.0-preview2-26905-02 + 2.2.0-preview3-26927-02 15.6.1 4.9.0 2.0.3 diff --git a/src/Microsoft.AspNetCore.Routing/EndpointRoutingMiddleware.cs b/src/Microsoft.AspNetCore.Routing/EndpointRoutingMiddleware.cs index 1b0385e212..b0d740b440 100644 --- a/src/Microsoft.AspNetCore.Routing/EndpointRoutingMiddleware.cs +++ b/src/Microsoft.AspNetCore.Routing/EndpointRoutingMiddleware.cs @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Routing public async Task Invoke(HttpContext httpContext) { - var feature = new EndpointFeature(); + var feature = new EndpointSelectorContext(); // 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. @@ -66,7 +66,7 @@ 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. - SetEndpointFeature(httpContext, feature); + SetFeatures(httpContext, feature); Log.MatchSuccess(_logger, feature); } @@ -78,13 +78,13 @@ namespace Microsoft.AspNetCore.Routing await _next(httpContext); } - private static void SetEndpointFeature(HttpContext httpContext, EndpointFeature feature) + private static void SetFeatures(HttpContext httpContext, EndpointSelectorContext context) { - // For back-compat EndpointRouteValuesFeature implements IEndpointFeature, + // For back-compat EndpointSelectorContext implements IEndpointFeature, // IRouteValuesFeature and IRoutingFeature - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); + httpContext.Features.Set(context); + httpContext.Features.Set(context); } // Initialization is async to avoid blocking threads while reflection and things @@ -126,9 +126,9 @@ namespace Microsoft.AspNetCore.Routing new EventId(2, "MatchFailure"), "Request did not match any endpoints."); - public static void MatchSuccess(ILogger logger, EndpointFeature feature) + public static void MatchSuccess(ILogger logger, EndpointSelectorContext context) { - _matchSuccess(logger, feature.Endpoint.DisplayName, null); + _matchSuccess(logger, context.Endpoint.DisplayName, null); } public static void MatchFailure(ILogger logger) diff --git a/src/Microsoft.AspNetCore.Routing/EndpointFeature.cs b/src/Microsoft.AspNetCore.Routing/EndpointSelectorContext.cs similarity index 95% rename from src/Microsoft.AspNetCore.Routing/EndpointFeature.cs rename to src/Microsoft.AspNetCore.Routing/EndpointSelectorContext.cs index 582f96cb7c..848cc27366 100644 --- a/src/Microsoft.AspNetCore.Routing/EndpointFeature.cs +++ b/src/Microsoft.AspNetCore.Routing/EndpointSelectorContext.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Routing { - public sealed class EndpointFeature : IEndpointFeature, IRouteValuesFeature, IRoutingFeature + public sealed class EndpointSelectorContext : IEndpointFeature, IRouteValuesFeature, IRoutingFeature { private RouteData _routeData; private RouteValueDictionary _routeValues; diff --git a/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs b/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs index 6b2e66ab56..4a004018b6 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; +using System.Collections.Specialized; using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; @@ -14,6 +16,12 @@ namespace Microsoft.AspNetCore.Routing.Matching /// public sealed class CandidateSet { + private const int BitVectorSize = 32; + + // Cannot be readonly because we need to modify it in place. + private BitVector32 _validity; + private readonly BitArray _largeCapactityValidity; + // We inline storage for 4 candidates here to avoid allocations in common // cases. There's no real reason why 4 is important, it just seemed like // a plausible number. @@ -26,8 +34,8 @@ namespace Microsoft.AspNetCore.Routing.Matching /// /// - /// Initializes a new instances of the candidate set structure with the provided list of endpoints - /// and associated scores. + /// Initializes a new instances of the class with the provided , + /// , and . /// /// /// The constructor is provided to enable unit tests of implementations of @@ -35,9 +43,30 @@ namespace Microsoft.AspNetCore.Routing.Matching /// /// /// The list of endpoints, sorted in descending priority order. + /// The list of instances. /// The list of endpoint scores. . - public CandidateSet(Endpoint[] endpoints, int[] scores) + public CandidateSet(Endpoint[] endpoints, RouteValueDictionary[] values, int[] scores) { + if (endpoints == null) + { + throw new ArgumentNullException(nameof(endpoints)); + } + + if (values == null) + { + throw new ArgumentNullException(nameof(values)); + } + + if (scores == null) + { + throw new ArgumentNullException(nameof(scores)); + } + + if (endpoints.Length != values.Length || endpoints.Length != scores.Length) + { + throw new ArgumentException($"The provided {nameof(endpoints)}, {nameof(values)}, and {nameof(scores)} must have the same length."); + } + Count = endpoints.Length; switch (endpoints.Length) @@ -46,40 +75,51 @@ namespace Microsoft.AspNetCore.Routing.Matching return; case 1: - _state0 = new CandidateState(endpoints[0], score: scores[0]); + _state0 = new CandidateState(endpoints[0], values[0], scores[0]); break; case 2: - _state0 = new CandidateState(endpoints[0], score: scores[0]); - _state1 = new CandidateState(endpoints[1], score: scores[1]); + _state0 = new CandidateState(endpoints[0], values[0], scores[0]); + _state1 = new CandidateState(endpoints[1], values[1], scores[1]); break; case 3: - _state0 = new CandidateState(endpoints[0], score: scores[0]); - _state1 = new CandidateState(endpoints[1], score: scores[1]); - _state2 = new CandidateState(endpoints[2], score: scores[2]); + _state0 = new CandidateState(endpoints[0], values[0], scores[0]); + _state1 = new CandidateState(endpoints[1], values[1], scores[1]); + _state2 = new CandidateState(endpoints[2], values[2], scores[2]); break; case 4: - _state0 = new CandidateState(endpoints[0], score: scores[0]); - _state1 = new CandidateState(endpoints[1], score: scores[1]); - _state2 = new CandidateState(endpoints[2], score: scores[2]); - _state3 = new CandidateState(endpoints[3], score: scores[3]); + _state0 = new CandidateState(endpoints[0], values[0], scores[0]); + _state1 = new CandidateState(endpoints[1], values[1], scores[1]); + _state2 = new CandidateState(endpoints[2], values[2], scores[2]); + _state3 = new CandidateState(endpoints[3], values[3], scores[3]); break; default: - _state0 = new CandidateState(endpoints[0], score: scores[0]); - _state1 = new CandidateState(endpoints[1], score: scores[1]); - _state2 = new CandidateState(endpoints[2], score: scores[2]); - _state3 = new CandidateState(endpoints[3], score: scores[3]); + _state0 = new CandidateState(endpoints[0], values[0], scores[0]); + _state1 = new CandidateState(endpoints[1], values[1], scores[1]); + _state2 = new CandidateState(endpoints[2], values[2], scores[2]); + _state3 = new CandidateState(endpoints[3], values[3], scores[3]); _additionalCandidates = new CandidateState[endpoints.Length - 4]; for (var i = 4; i < endpoints.Length; i++) { - _additionalCandidates[i - 4] = new CandidateState(endpoints[i], score: scores[i]); + _additionalCandidates[i - 4] = new CandidateState(endpoints[i], values[i], scores[i]); } break; } + + // Initialize validity to valid by default. + if (Count < BitVectorSize) + { + // Sets the bit for each candidate that exists (bits > Count will be 0). + _validity = new BitVector32(unchecked((int)~(0xFFFFFFFFu << Count))); + } + else + { + _largeCapactityValidity = new BitArray(Count, defaultValue: true); + } } internal CandidateSet(Candidate[] candidates) @@ -126,6 +166,17 @@ namespace Microsoft.AspNetCore.Routing.Matching } break; } + + // Initialize validity to valid by default. + if (Count < BitVectorSize) + { + // Sets the bit for each candidate that exists (bits > Count will be 0). + _validity = new BitVector32(unchecked((int)~(0xFFFFFFFFu << Count))); + } + else + { + _largeCapactityValidity = new BitArray(Count, defaultValue: true); + } } /// @@ -139,12 +190,11 @@ namespace Microsoft.AspNetCore.Routing.Matching /// /// The candidate index. /// - /// A reference to the . The result is returned by reference - /// and intended to be mutated. + /// A reference to the . The result is returned by reference. /// public ref CandidateState this[int index] { - // Note that this is a ref-return because of both mutability and performance. + // Note that this is a ref-return because of performance. // We don't want to copy these fat structs if it can be avoided. // PERF: Force inlining @@ -177,6 +227,60 @@ namespace Microsoft.AspNetCore.Routing.Matching } } + /// + /// Gets a value which indicates where the is considered + /// a valid candiate for the current request. + /// + /// The candidate index. + /// + /// true if the candidate at position is considered value + /// for the current request, otherwise false. + /// + public bool IsValidCandidate(int index) + { + // Friendliness for inlining + if ((uint)index >= Count) + { + ThrowIndexArgumentOutOfRangeException(); + } + + if (Count < BitVectorSize) + { + // Get the n-th bit + return _validity[0x00000001 << index]; + } + else + { + return _largeCapactityValidity[index]; + } + } + + /// + /// Sets the validitity of the candidate at the provided index. + /// + /// The candidate index. + /// + /// The value to set. If true the candidate is considered valid for the current request. + /// + public void SetValidity(int index, bool value) + { + // Friendliness for inlining + if ((uint)index >= Count) + { + ThrowIndexArgumentOutOfRangeException(); + } + + if (Count < BitVectorSize) + { + // Set the n-th bit + _validity[0x00000001 << index] = value; + } + else + { + _largeCapactityValidity[index] = value; + } + } + private static void ThrowIndexArgumentOutOfRangeException() { throw new ArgumentOutOfRangeException("index"); diff --git a/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs b/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs index 8cc0c1319e..5b198beb8e 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/CandidateState.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Routing.Matching { /// - /// The mutable state associated with a candidate in a . + /// The state associated with a candidate in a . /// public struct CandidateState { @@ -14,11 +14,16 @@ namespace Microsoft.AspNetCore.Routing.Matching { Endpoint = endpoint; Score = score; - - IsValidCandidate = true; Values = null; } + internal CandidateState(Endpoint endpoint, RouteValueDictionary values, int score) + { + Endpoint = endpoint; + Values = values; + Score = score; + } + /// /// Gets the . /// @@ -42,16 +47,9 @@ namespace Microsoft.AspNetCore.Routing.Matching public int Score { get; } /// - /// Gets or sets a value which indicates where the is considered - /// a valid candiate for the current request. Set this value to false to exclude an - /// from consideration. - /// - public bool IsValidCandidate { get; set; } - - /// - /// Gets or sets the associated with the + /// Gets associated with the /// and the current request. /// - public RouteValueDictionary Values { get; set; } + public RouteValueDictionary Values { get; internal set; } } } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs b/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs index 85a1214402..fec90b0d64 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DataSourceDependentMatcher.cs @@ -27,9 +27,9 @@ namespace Microsoft.AspNetCore.Routing.Matching // Used in tests internal Matcher CurrentMatcher => _cache.Value; - public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { - return CurrentMatcher.MatchAsync(httpContext, feature); + return CurrentMatcher.MatchAsync(httpContext, context); } private Matcher CreateMatcher(IReadOnlyList endpoints) diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs b/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs index c2128d0e7a..bd670d86f5 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DefaultEndpointSelector.cs @@ -6,14 +6,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Routing.Matching { internal class DefaultEndpointSelector : EndpointSelector { private readonly IEndpointSelectorPolicy[] _selectorPolicies; - + public DefaultEndpointSelector(IEnumerable matcherPolicies) { if (matcherPolicies == null) @@ -24,28 +23,88 @@ namespace Microsoft.AspNetCore.Routing.Matching _selectorPolicies = matcherPolicies.OrderBy(p => p.Order).OfType().ToArray(); } - public override async Task SelectAsync( + public override Task SelectAsync( HttpContext httpContext, - EndpointFeature feature, + EndpointSelectorContext context, + CandidateSet candidateSet) + { + if (httpContext == null) + { + throw new ArgumentNullException(nameof(httpContext)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (candidateSet == null) + { + throw new ArgumentNullException(nameof(candidateSet)); + } + + if (_selectorPolicies.Length > 0) + { + // Slow path: we need async to run policies + return SelectAsyncSlow(httpContext, context, candidateSet); + } + + // Fast path: We can specialize for trivial numbers of candidates since there can + // be no ambiguities and we don't need to run policies. + switch (candidateSet.Count) + { + case 0: + { + // Do nothing + break; + } + + case 1: + { + if (candidateSet.IsValidCandidate(0)) + { + ref var state = ref candidateSet[0]; + context.Endpoint = state.Endpoint; + context.RouteValues = state.Values; + } + + break; + } + + default: + { + // Slow path: There's more than one candidate (to say nothing of validity) so we + // have to process for ambiguities. + ProcessFinalCandidates(httpContext, context, candidateSet); + break; + } + } + + return Task.CompletedTask; + } + + private async Task SelectAsyncSlow( + HttpContext httpContext, + EndpointSelectorContext context, CandidateSet candidateSet) { var selectorPolicies = _selectorPolicies; - for (var i = 0; i < _selectorPolicies.Length; i++) + for (var i = 0; i < selectorPolicies.Length; i++) { - await selectorPolicies[i].ApplyAsync(httpContext, feature, candidateSet); - if (feature.Endpoint != null) + await selectorPolicies[i].ApplyAsync(httpContext, context, candidateSet); + if (context.Endpoint != null) { // This is a short circuit, the selector chose an endpoint. return; } } - ProcessFinalCandidates(httpContext, feature, candidateSet); + ProcessFinalCandidates(httpContext, context, candidateSet); } private static void ProcessFinalCandidates( HttpContext httpContext, - EndpointFeature feature, + EndpointSelectorContext context, CandidateSet candidateSet) { Endpoint endpoint = null; @@ -54,8 +113,7 @@ namespace Microsoft.AspNetCore.Routing.Matching for (var i = 0; i < candidateSet.Count; i++) { ref var state = ref candidateSet[i]; - - var isValid = state.IsValidCandidate; + var isValid = candidateSet.IsValidCandidate(i); if (isValid && foundScore == null) { // This is the first match we've seen - speculatively assign it. @@ -87,8 +145,8 @@ namespace Microsoft.AspNetCore.Routing.Matching if (endpoint != null) { - feature.Endpoint = endpoint; - feature.RouteValues = values; + context.Endpoint = endpoint; + context.RouteValues = values; } } @@ -99,10 +157,9 @@ namespace Microsoft.AspNetCore.Routing.Matching var matches = new List(); for (var i = 0; i < candidates.Count; i++) { - ref var state = ref candidates[i]; - if (state.IsValidCandidate) + if (candidates.IsValidCandidate(i)) { - matches.Add(state.Endpoint); + matches.Add(candidates[i].Endpoint); } } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs b/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs index 54209ab0e4..212acaf1b5 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/DfaMatcher.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Routing.Patterns; namespace Microsoft.AspNetCore.Routing.Matching @@ -23,16 +22,16 @@ namespace Microsoft.AspNetCore.Routing.Matching _maxSegmentCount = maxSegmentCount; } - public sealed override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } - if (feature == null) + if (context == null) { - throw new ArgumentNullException(nameof(feature)); + throw new ArgumentNullException(nameof(context)); } // The sequence of actions we take is optimized to avoid doing expensive work @@ -118,21 +117,26 @@ namespace Microsoft.AspNetCore.Routing.Matching // Now that we have the route values, we need to process complex segments. // Complex segments go through an old API that requires a fully-materialized // route value dictionary. - var isMatch = true; if ((flags & Candidate.CandidateFlags.HasComplexSegments) != 0) { - isMatch &= ProcessComplexSegments(candidate.ComplexSegments, path, segments, values); + if (!ProcessComplexSegments(candidate.ComplexSegments, path, segments, values)) + { + candidateSet.SetValidity(i, false); + continue; + } } if ((flags & Candidate.CandidateFlags.HasConstraints) != 0) { - isMatch &= ProcessConstraints(candidate.Constraints, httpContext, values); + if (!ProcessConstraints(candidate.Constraints, httpContext, values)) + { + candidateSet.SetValidity(i, false); + continue; + } } - - state.IsValidCandidate = isMatch; } - return _selector.SelectAsync(httpContext, feature, candidateSet); + return _selector.SelectAsync(httpContext, context, candidateSet); } internal Candidate[] FindCandidateSet( diff --git a/src/Microsoft.AspNetCore.Routing/Matching/EndpointSelector.cs b/src/Microsoft.AspNetCore.Routing/Matching/EndpointSelector.cs index d1f08ab207..ba1345bb5a 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/EndpointSelector.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/EndpointSelector.cs @@ -18,16 +18,16 @@ namespace Microsoft.AspNetCore.Routing.Matching /// Asynchronously selects an from the . /// /// The associated with the current request. - /// The associated with the current request. + /// The associated with the current request. /// The . /// A that completes asynchronously once endpoint selection is complete. /// - /// An should assign the - /// and properties once an endpoint is selected. + /// An should assign the + /// and properties once an endpoint is selected. /// public abstract Task SelectAsync( HttpContext httpContext, - EndpointFeature feature, + EndpointSelectorContext context, CandidateSet candidates); } } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/IEndpointSelectorPolicy.cs b/src/Microsoft.AspNetCore.Routing/Matching/IEndpointSelectorPolicy.cs index 1617822f03..f1488bc98d 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/IEndpointSelectorPolicy.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/IEndpointSelectorPolicy.cs @@ -20,21 +20,21 @@ namespace Microsoft.AspNetCore.Routing.Matching /// /// The associated with the current request. /// - /// - /// The associated with the current request. + /// + /// The associated with the current request. /// /// The . /// /// /// Implementations of should implement this method /// and filter the set of candidates in the by setting - /// to false where desired. + /// to false where desired. /// /// - /// To signal an error condition, set to an + /// To signal an error condition, set to an /// value that will produce the desired error when executed. /// /// - Task ApplyAsync(HttpContext httpContext, EndpointFeature feature, CandidateSet candidates); + Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates); } } diff --git a/src/Microsoft.AspNetCore.Routing/Matching/Matcher.cs b/src/Microsoft.AspNetCore.Routing/Matching/Matcher.cs index 0d84df9dd4..b713e4bdf3 100644 --- a/src/Microsoft.AspNetCore.Routing/Matching/Matcher.cs +++ b/src/Microsoft.AspNetCore.Routing/Matching/Matcher.cs @@ -17,10 +17,10 @@ namespace Microsoft.AspNetCore.Routing.Matching /// Attempts to asynchronously select an for the current request. /// /// The associated with the current request. - /// + /// /// The associated with the current request. The - /// will be mutated to contain the result of the operation. + /// will be mutated to contain the result of the operation. /// A which represents the asynchronous completion of the operation. - public abstract Task MatchAsync(HttpContext httpContext, EndpointFeature feature); + public abstract Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context); } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/EndpointMiddlewareTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/EndpointMiddlewareTest.cs index 6be0ade53b..3958222fa4 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/EndpointMiddlewareTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/EndpointMiddlewareTest.cs @@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Routing var httpContext = new DefaultHttpContext(); httpContext.RequestServices = new ServiceProvider(); - httpContext.Features.Set(new EndpointFeature() + httpContext.Features.Set(new EndpointSelectorContext() { Endpoint = null, }); @@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Routing return Task.CompletedTask; }; - httpContext.Features.Set(new EndpointFeature() + httpContext.Features.Set(new EndpointSelectorContext() { Endpoint = new Endpoint(endpointFunc, EndpointMetadataCollection.Empty, "Test"), }); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/EndpointRoutingMiddlewareTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/EndpointRoutingMiddlewareTest.cs index 1e2f1bbca3..a15d94e340 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/EndpointRoutingMiddlewareTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/EndpointRoutingMiddlewareTest.cs @@ -105,11 +105,11 @@ namespace Microsoft.AspNetCore.Routing private HttpContext CreateHttpContext() { - var feature = new EndpointFeature(); + var context = new EndpointSelectorContext(); var httpContext = new DefaultHttpContext(); - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); + httpContext.Features.Set(context); httpContext.RequestServices = new TestServiceProvider(); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/EndpointFeatureTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/EndpointSelectorContextTest.cs similarity index 85% rename from test/Microsoft.AspNetCore.Routing.Tests/EndpointFeatureTest.cs rename to test/Microsoft.AspNetCore.Routing.Tests/EndpointSelectorContextTest.cs index fb27335677..05ca8850da 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/EndpointFeatureTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/EndpointSelectorContextTest.cs @@ -9,7 +9,7 @@ using Xunit; namespace Microsoft.AspNetCore.Routing { - public class EndpointFeatureTest + public class EndpointSelectorContextTest { [Fact] public void RouteData_CanIntializeDataTokens_WithMetadata() @@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Routing // Arrange var expected = new RouteValueDictionary(new { foo = 17, bar = "hello", }); - var feature = new EndpointFeature() + var context = new EndpointSelectorContext() { Endpoint = new RouteEndpoint( TestConstants.EmptyRequestDelegate, @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Routing }; // Act - var routeData = ((IRoutingFeature)feature).RouteData; + var routeData = ((IRoutingFeature)context).RouteData; // Assert Assert.NotSame(expected, routeData.DataTokens); @@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.Routing public void RouteData_DataTokensIsEmpty_WithoutMetadata() { // Arrange - var feature = new EndpointFeature() + var context = new EndpointSelectorContext() { Endpoint = new RouteEndpoint( TestConstants.EmptyRequestDelegate, @@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Routing }; // Act - var routeData = ((IRoutingFeature)feature).RouteData; + var routeData = ((IRoutingFeature)context).RouteData; // Assert Assert.Empty(routeData.DataTokens); diff --git a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs index ee16dc1666..85462d314d 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorEndpointNameExtensionsTest.cs @@ -26,12 +26,12 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); - var feature = new EndpointFeature() + var context = new EndpointSelectorContext() { RouteValues = new RouteValueDictionary(new { p = "5", }) }; var httpContext = CreateHttpContext(); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); var values = new { query = "some?query", }; diff --git a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs index 3c3e63c78e..de046fb2b1 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorRouteValuesAddressExtensionsTest.cs @@ -32,12 +32,12 @@ namespace Microsoft.AspNetCore.Routing var linkGenerator = CreateLinkGenerator(endpoint1, endpoint2); - var feature = new EndpointFeature() + var context = new EndpointSelectorContext() { RouteValues = new RouteValueDictionary(new { action = "Index", }) }; var httpContext = CreateHttpContext(); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); httpContext.Request.PathBase = new PathString("/Foo/Bar?encodeme?"); // Act diff --git a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorTestBase.cs b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorTestBase.cs index efceecf7b9..a2a9c22db9 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorTestBase.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/LinkGeneratorTestBase.cs @@ -18,13 +18,13 @@ namespace Microsoft.AspNetCore.Routing { var httpContext = new DefaultHttpContext(); - var feature = new EndpointFeature + var context = new EndpointSelectorContext { RouteValues = new RouteValueDictionary(ambientValues) }; - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); + httpContext.Features.Set(context); return httpContext; } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/BarebonesMatcher.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/BarebonesMatcher.cs index 9b96df1ed8..f1501b423f 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/BarebonesMatcher.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/BarebonesMatcher.cs @@ -20,16 +20,16 @@ namespace Microsoft.AspNetCore.Routing.Matching Matchers = matchers; } - public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } - if (feature == null) + if (context == null) { - throw new ArgumentNullException(nameof(feature)); + throw new ArgumentNullException(nameof(context)); } var path = httpContext.Request.Path.Value; @@ -37,8 +37,8 @@ namespace Microsoft.AspNetCore.Routing.Matching { if (Matchers[i].TryMatch(path)) { - feature.Endpoint = Matchers[i].Endpoint; - feature.RouteValues = new RouteValueDictionary(); + context.Endpoint = Matchers[i].Endpoint; + context.RouteValues = new RouteValueDictionary(); } } @@ -121,12 +121,12 @@ namespace Microsoft.AspNetCore.Routing.Matching return Array.Empty(); } - public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (TryMatch(httpContext.Request.Path.Value)) { - feature.Endpoint = Endpoint; - feature.RouteValues = new RouteValueDictionary(); + context.Endpoint = Endpoint; + context.RouteValues = new RouteValueDictionary(); } return Task.CompletedTask; diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/CandidateSetTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/CandidateSetTest.cs index b0f3eacd91..b94923df3c 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/CandidateSetTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/CandidateSetTest.cs @@ -22,6 +22,9 @@ namespace Microsoft.AspNetCore.Routing.Matching [InlineData(4)] [InlineData(5)] // this is the break-point where we start to use a list. [InlineData(6)] + [InlineData(31)] + [InlineData(32)] // this is the break point where we use a BitArray + [InlineData(33)] public void Create_CreatesCandidateSet(int count) { // Arrange @@ -41,10 +44,13 @@ namespace Microsoft.AspNetCore.Routing.Matching for (var i = 0; i < candidateSet.Count; i++) { ref var state = ref candidateSet[i]; - Assert.True(state.IsValidCandidate); + Assert.True(candidateSet.IsValidCandidate(i)); Assert.Same(endpoints[i], state.Endpoint); Assert.Equal(candidates[i].Score, state.Score); Assert.Null(state.Values); + + candidateSet.SetValidity(i, false); + Assert.False(candidateSet.IsValidCandidate(i)); } } @@ -58,6 +64,9 @@ namespace Microsoft.AspNetCore.Routing.Matching [InlineData(4)] [InlineData(5)] // this is the break-point where we start to use a list. [InlineData(6)] + [InlineData(31)] + [InlineData(32)] // this is the break point where we use a BitArray + [InlineData(33)] public void Create_CreatesCandidateSet_TestConstructor(int count) { // Arrange @@ -67,17 +76,30 @@ namespace Microsoft.AspNetCore.Routing.Matching endpoints[i] = CreateEndpoint($"/{i}"); } + var values = new RouteValueDictionary[count]; + for (var i = 0; i < endpoints.Length; i++) + { + values[i] = new RouteValueDictionary() + { + { "i", i } + }; + } + // Act - var candidateSet = new CandidateSet(endpoints, Enumerable.Range(0, count).ToArray()); + var candidateSet = new CandidateSet(endpoints, values, Enumerable.Range(0, count).ToArray()); // Assert for (var i = 0; i < candidateSet.Count; i++) { ref var state = ref candidateSet[i]; - Assert.True(state.IsValidCandidate); + Assert.True(candidateSet.IsValidCandidate(i)); Assert.Same(endpoints[i], state.Endpoint); Assert.Equal(i, state.Score); - Assert.Null(state.Values); + Assert.NotNull(state.Values); + Assert.Equal(i, state.Values["i"]); + + candidateSet.SetValidity(i, false); + Assert.False(candidateSet.IsValidCandidate(i)); } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs index fc72765748..eb41ea1890 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DataSourceDependentMatcherTest.cs @@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { public IReadOnlyList Endpoints { get; set; } - public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { throw new NotImplementedException(); } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DefaultEndpointSelectorTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DefaultEndpointSelectorTest.cs index 769ca3200c..ae67bf53fb 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DefaultEndpointSelectorTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DefaultEndpointSelectorTest.cs @@ -21,14 +21,14 @@ namespace Microsoft.AspNetCore.Routing.Matching var scores = new int[] { }; var candidateSet = CreateCandidateSet(endpoints, scores); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Null(feature.Endpoint); + Assert.Null(context.Endpoint); } [Fact] @@ -40,16 +40,16 @@ namespace Microsoft.AspNetCore.Routing.Matching var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].Values = new RouteValueDictionary(); - candidateSet[0].IsValidCandidate = false; + candidateSet.SetValidity(0, false); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Null(feature.Endpoint); + Assert.Null(context.Endpoint); } [Fact] @@ -61,16 +61,16 @@ namespace Microsoft.AspNetCore.Routing.Matching var candidateSet = CreateCandidateSet(endpoints, scores); candidateSet[0].Values = new RouteValueDictionary(); - candidateSet[0].IsValidCandidate = true; + candidateSet.SetValidity(0, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Same(endpoints[0], feature.Endpoint); + Assert.Same(endpoints[0], context.Endpoint); } [Fact] @@ -81,17 +81,17 @@ namespace Microsoft.AspNetCore.Routing.Matching var scores = new int[] { 0, 0 }; var candidateSet = CreateCandidateSet(endpoints, scores); - candidateSet[0].IsValidCandidate = false; - candidateSet[1].IsValidCandidate = true; + candidateSet.SetValidity(0, false); + candidateSet.SetValidity(1, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Same(endpoints[1], feature.Endpoint); + Assert.Same(endpoints[1], context.Endpoint); } [Fact] @@ -102,18 +102,18 @@ namespace Microsoft.AspNetCore.Routing.Matching var scores = new int[] { 0, 0, 1 }; var candidateSet = CreateCandidateSet(endpoints, scores); - candidateSet[0].IsValidCandidate = false; - candidateSet[1].IsValidCandidate = true; - candidateSet[2].IsValidCandidate = true; + candidateSet.SetValidity(0, false); + candidateSet.SetValidity(1, true); + candidateSet.SetValidity(2, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Same(endpoints[1], feature.Endpoint); + Assert.Same(endpoints[1], context.Endpoint); } [Fact] @@ -131,20 +131,20 @@ namespace Microsoft.AspNetCore.Routing.Matching var scores = new int[] { 0, 1, 2, 3, 4 }; var candidateSet = CreateCandidateSet(endpoints, scores); - candidateSet[0].IsValidCandidate = false; - candidateSet[1].IsValidCandidate = false; - candidateSet[2].IsValidCandidate = false; - candidateSet[3].IsValidCandidate = false; - candidateSet[4].IsValidCandidate = true; + candidateSet.SetValidity(0, false); + candidateSet.SetValidity(1, false); + candidateSet.SetValidity(2, false); + candidateSet.SetValidity(3, false); + candidateSet.SetValidity(4, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Same(endpoints[4], feature.Endpoint); + Assert.Same(endpoints[4], context.Endpoint); } [Fact] @@ -155,15 +155,15 @@ namespace Microsoft.AspNetCore.Routing.Matching var scores = new int[] { 0, 1, 1 }; var candidateSet = CreateCandidateSet(endpoints, scores); - candidateSet[0].IsValidCandidate = false; - candidateSet[1].IsValidCandidate = true; - candidateSet[2].IsValidCandidate = true; + candidateSet.SetValidity(0, false); + candidateSet.SetValidity(1, true); + candidateSet.SetValidity(2, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(); // Act - var ex = await Assert.ThrowsAsync(() => selector.SelectAsync(httpContext, feature, candidateSet)); + var ex = await Assert.ThrowsAsync(() => selector.SelectAsync(httpContext, context, candidateSet)); // Assert Assert.Equal( @@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Routing.Matching test: /test2 test: /test3", ex.Message); - Assert.Null(feature.Endpoint); + Assert.Null(context.Endpoint); } [Fact] @@ -185,25 +185,25 @@ test: /test3", ex.Message); var policy = new Mock(); policy .As() - .Setup(p => p.ApplyAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((c, f, cs) => + .Setup(p => p.ApplyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((c, f, cs) => { - cs[1].IsValidCandidate = false; + cs.SetValidity(1, false); return Task.CompletedTask; }); - candidateSet[0].IsValidCandidate = false; - candidateSet[1].IsValidCandidate = true; - candidateSet[2].IsValidCandidate = true; + candidateSet.SetValidity(0, false); + candidateSet.SetValidity(1, true); + candidateSet.SetValidity(2, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(policy.Object); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Same(endpoints[2], feature.Endpoint); + Assert.Same(endpoints[2], context.Endpoint); } [Fact] @@ -217,8 +217,8 @@ test: /test3", ex.Message); var policy1 = new Mock(); policy1 .As() - .Setup(p => p.ApplyAsync(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((c, f, cs) => + .Setup(p => p.ApplyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((c, f, cs) => { f.Endpoint = cs[0].Endpoint; return Task.CompletedTask; @@ -231,31 +231,31 @@ test: /test3", ex.Message); .Returns(1000); policy2 .As() - .Setup(p => p.ApplyAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(p => p.ApplyAsync(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(new InvalidOperationException()); - candidateSet[0].IsValidCandidate = false; - candidateSet[1].IsValidCandidate = true; - candidateSet[2].IsValidCandidate = true; + candidateSet.SetValidity(0, false); + candidateSet.SetValidity(1, true); + candidateSet.SetValidity(2, true); - var (httpContext, feature) = CreateContext(); + var (httpContext, context) = CreateContext(); var selector = CreateSelector(policy1.Object, policy2.Object); // Act - await selector.SelectAsync(httpContext, feature, candidateSet); + await selector.SelectAsync(httpContext, context, candidateSet); // Assert - Assert.Same(endpoints[0], feature.Endpoint); + Assert.Same(endpoints[0], context.Endpoint); } - private static (HttpContext httpContext, EndpointFeature feature) CreateContext() + private static (HttpContext httpContext, EndpointSelectorContext context) CreateContext() { - var feature = new EndpointFeature(); + var context = new EndpointSelectorContext(); var httpContext = new DefaultHttpContext(); - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); + httpContext.Features.Set(context); - return (httpContext, feature); + return (httpContext, context); } private static RouteEndpoint CreateEndpoint(string template) @@ -270,7 +270,7 @@ test: /test3", ex.Message); private static CandidateSet CreateCandidateSet(RouteEndpoint[] endpoints, int[] scores) { - return new CandidateSet(endpoints, scores); + return new CandidateSet(endpoints, new RouteValueDictionary[endpoints.Length], scores); } private static DefaultEndpointSelector CreateSelector(params MatcherPolicy[] policies) diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherConformanceTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherConformanceTest.cs index 5f55304b02..e34abbd5b3 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherConformanceTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherConformanceTest.cs @@ -14,13 +14,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } internal override Matcher CreateMatcher(params RouteEndpoint[] endpoints) diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherTest.cs index 43046764db..924a11b733 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/DfaMatcherTest.cs @@ -55,14 +55,14 @@ namespace Microsoft.AspNetCore.Routing.Matching var matcher = CreateDfaMatcher(endpointDataSource); - var (httpContext, endpointFeature) = CreateHttpContext(); + var (httpContext, context) = CreateContext(); httpContext.Request.Path = "/1"; // Act - await matcher.MatchAsync(httpContext, endpointFeature); + await matcher.MatchAsync(httpContext, context); // Assert - Assert.NotNull(endpointFeature.Endpoint); + Assert.NotNull(context.Endpoint); } [Fact] @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Matching var matcher = CreateDfaMatcher(endpointDataSource); - var (httpContext, endpointFeature) = CreateHttpContext(); + var (httpContext, endpointFeature) = CreateContext(); httpContext.Request.Path = "/One"; // Act @@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Routing.Matching var matcher = CreateDfaMatcher(endpointDataSource); - var (httpContext, endpointFeature) = CreateHttpContext(); + var (httpContext, endpointFeature) = CreateContext(); httpContext.Request.Path = "/Teams"; // Act @@ -120,18 +120,18 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpointSelector = new Mock(); endpointSelector - .Setup(s => s.SelectAsync(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(s => s.SelectAsync(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((c, f, cs) => { Assert.Equal(2, cs.Count); Assert.Same(endpoint1, cs[0].Endpoint); - Assert.True(cs[0].IsValidCandidate); + Assert.True(cs.IsValidCandidate(0)); Assert.Equal(0, cs[0].Score); Assert.Empty(cs[0].Values); Assert.Same(endpoint2, cs[1].Endpoint); - Assert.True(cs[1].IsValidCandidate); + Assert.True(cs.IsValidCandidate(1)); Assert.Equal(1, cs[1].Score); Assert.Empty(cs[1].Values); @@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.Routing.Matching var matcher = CreateDfaMatcher(endpointDataSource, endpointSelector.Object); - var (httpContext, endpointFeature) = CreateHttpContext(); + var (httpContext, endpointFeature) = CreateContext(); httpContext.Request.Path = "/Teams"; // Act @@ -157,15 +157,15 @@ namespace Microsoft.AspNetCore.Routing.Matching Assert.Equal(endpoint2, endpointFeature.Endpoint); } - private (HttpContext httpContext, EndpointFeature feature) CreateHttpContext() + private (HttpContext httpContext, EndpointSelectorContext context) CreateContext() { - var feature = new EndpointFeature(); + var context = new EndpointSelectorContext(); var httpContext = new DefaultHttpContext(); - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); + httpContext.Features.Set(context); - return (httpContext, feature); + return (httpContext, context); } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/FullFeaturedMatcherConformanceTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/FullFeaturedMatcherConformanceTest.cs index b831720e67..b470d9369f 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/FullFeaturedMatcherConformanceTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/FullFeaturedMatcherConformanceTest.cs @@ -28,13 +28,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Fact] @@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Routing.Matching // Arrange var endpoint = CreateEndpoint("/a/{b}/{c}", new { b = "17", c = "18", }); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/a"); + var (httpContext, context) = CreateContext("/a"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, new { b = "17", c = "18", }); + MatcherAssert.AssertMatch(context, httpContext, endpoint, new { b = "17", c = "18", }); } [Fact] @@ -58,13 +58,13 @@ namespace Microsoft.AspNetCore.Routing.Matching // Arrange var endpoint = CreateEndpoint("/a/{b}/{c}", new { b = "17", c = "18", d = "19" }); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/a"); + var (httpContext, context) = CreateContext("/a"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, new { b = "17", c = "18", d = "19" }); + MatcherAssert.AssertMatch(context, httpContext, endpoint, new { b = "17", c = "18", d = "19" }); } [Theory] @@ -83,13 +83,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] @@ -115,13 +115,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Theory] @@ -138,13 +138,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] @@ -162,13 +162,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } // Historically catchall segments don't match an empty segment, but only if it's @@ -182,13 +182,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); // Need to access these to prevent a warning from the xUnit analyzer. // Some of these tests will match (and process the values) and some will not. @@ -217,13 +217,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Theory] @@ -237,13 +237,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] @@ -266,13 +266,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Theory] @@ -292,13 +292,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } // Most of are copied from old routing tests that date back to the VS 2010 era. Enjoy! @@ -317,13 +317,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Theory] @@ -347,13 +347,13 @@ namespace Microsoft.AspNetCore.Routing.Matching // Arrange var matcher = CreateMatcher(other, expected); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true); + MatcherAssert.AssertMatch(context, httpContext, expected, ignoreValues: true); } [Theory] @@ -381,13 +381,13 @@ namespace Microsoft.AspNetCore.Routing.Matching // Arrange var matcher = CreateMatcher(other, expected); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true); + MatcherAssert.AssertMatch(context, httpContext, expected, ignoreValues: true); } [Theory] @@ -434,13 +434,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var expected = endpoints[Array.IndexOf(templates, expectedTemplate)]; var matcher = CreateMatcher(endpoints); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, expected, ignoreValues: true); + MatcherAssert.AssertMatch(context, httpContext, expected, ignoreValues: true); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/HttpMethodMatcherPolicyIntegrationTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/HttpMethodMatcherPolicyIntegrationTest.cs index 6d271b5c83..1aca0d5a64 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/HttpMethodMatcherPolicyIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/HttpMethodMatcherPolicyIntegrationTest.cs @@ -23,13 +23,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -39,13 +39,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }, acceptCorsPreflight: true); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -55,13 +55,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }, acceptCorsPreflight: true); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true); + var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } @@ -72,14 +72,14 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GET", }, acceptCorsPreflight: false); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true); + var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - Assert.NotSame(endpoint, feature.Endpoint); - Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, feature.Endpoint.DisplayName); + Assert.NotSame(endpoint, context.Endpoint); + Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, context.Endpoint.DisplayName); } [Fact] @@ -89,13 +89,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GeT", }); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -105,13 +105,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { "GeT", }, acceptCorsPreflight: true); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true); + var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -121,13 +121,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello"); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -137,13 +137,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", acceptCorsPreflight: true); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true); + var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] // This matches because the endpoint accepts OPTIONS @@ -153,13 +153,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", acceptCorsPreflight: false); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET", corsPreflight: true); + var (httpContext, context) = CreateContext("/hello", "GET", corsPreflight: true); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -169,13 +169,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint = CreateEndpoint("/hello", httpMethods: new string[] { }); var matcher = CreateMatcher(endpoint); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] // When all of the candidates handles specific verbs, use a 405 endpoint @@ -186,19 +186,19 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint2 = CreateEndpoint("/hello", httpMethods: new string[] { "DELETE" }); var matcher = CreateMatcher(endpoint1, endpoint2); - var (httpContext, feature) = CreateContext("/hello", "POST"); + var (httpContext, context) = CreateContext("/hello", "POST"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - Assert.NotSame(endpoint1, feature.Endpoint); - Assert.NotSame(endpoint2, feature.Endpoint); + Assert.NotSame(endpoint1, context.Endpoint); + Assert.NotSame(endpoint2, context.Endpoint); - Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, feature.Endpoint.DisplayName); + Assert.Same(HttpMethodMatcherPolicy.Http405EndpointDisplayName, context.Endpoint.DisplayName); // Invoke the endpoint - await feature.Endpoint.RequestDelegate(httpContext); + await context.Endpoint.RequestDelegate(httpContext); Assert.Equal(405, httpContext.Response.StatusCode); Assert.Equal("DELETE, GET, PUT", httpContext.Response.Headers["Allow"]); } @@ -211,13 +211,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint2 = CreateEndpoint("/hello", httpMethods: new string[] { "DELETE" }); var matcher = CreateMatcher(endpoint1, endpoint2); - var (httpContext, feature) = CreateContext("/hello", "POST", corsPreflight: true); + var (httpContext, context) = CreateContext("/hello", "POST", corsPreflight: true); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Fact] // When one of the candidates handles all verbs, dont use a 405 endpoint @@ -228,13 +228,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint2 = CreateEndpoint("/hello", httpMethods: new string[] { "DELETE" }); var matcher = CreateMatcher(endpoint1, endpoint2); - var (httpContext, feature) = CreateContext("/hello", "POST"); + var (httpContext, context) = CreateContext("/hello", "POST"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Fact] @@ -245,13 +245,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint2 = CreateEndpoint("/bar"); var matcher = CreateMatcher(endpoint1, endpoint2); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint1); + MatcherAssert.AssertMatch(context, httpContext, endpoint1); } [Fact] @@ -262,13 +262,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint2 = CreateEndpoint("/bar", httpMethods: new string[] { }); var matcher = CreateMatcher(endpoint1, endpoint2); - var (httpContext, feature) = CreateContext("/hello", "GET"); + var (httpContext, context) = CreateContext("/hello", "GET"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint1); + MatcherAssert.AssertMatch(context, httpContext, endpoint1); } [Fact] // The non-http-method-specific endpoint is part of the same candidate set @@ -279,13 +279,13 @@ namespace Microsoft.AspNetCore.Routing.Matching var endpoint2 = CreateEndpoint("/{x}", httpMethods: new string[] { }); var matcher = CreateMatcher(endpoint1, endpoint2); - var (httpContext, feature) = CreateContext("/hello", "POST"); + var (httpContext, context) = CreateContext("/hello", "POST"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint2, ignoreValues: true); + MatcherAssert.AssertMatch(context, httpContext, endpoint2, ignoreValues: true); } private static Matcher CreateMatcher(params RouteEndpoint[] endpoints) @@ -305,7 +305,7 @@ namespace Microsoft.AspNetCore.Routing.Matching return builder.Build(); } - internal static (HttpContext httpContext, EndpointFeature feature) CreateContext( + internal static (HttpContext httpContext, EndpointSelectorContext context) CreateContext( string path, string httpMethod, bool corsPreflight = false) @@ -320,11 +320,11 @@ namespace Microsoft.AspNetCore.Routing.Matching httpContext.Request.Headers[AccessControlRequestMethod] = httpMethod; } - var feature = new EndpointFeature(); - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + var context = new EndpointSelectorContext(); + httpContext.Features.Set(context); + httpContext.Features.Set(context); - return (httpContext, feature); + return (httpContext, context); } internal static RouteEndpoint CreateEndpoint( string template, diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherAssert.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherAssert.cs index e760c71739..6053946310 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherAssert.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherAssert.cs @@ -12,22 +12,22 @@ namespace Microsoft.AspNetCore.Routing.Matching { internal static class MatcherAssert { - public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected) + public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected) { - AssertMatch(feature, context, expected, new RouteValueDictionary()); + AssertMatch(context, httpContext, expected, new RouteValueDictionary()); } - public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, bool ignoreValues) + public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, bool ignoreValues) { - AssertMatch(feature, context, expected, new RouteValueDictionary(), ignoreValues); + AssertMatch(context, httpContext, expected, new RouteValueDictionary(), ignoreValues); } - public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, object values) + public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, object values) { - AssertMatch(feature, context, expected, new RouteValueDictionary(values)); + AssertMatch(context, httpContext, expected, new RouteValueDictionary(values)); } - public static void AssertMatch(EndpointFeature feature, HttpContext context, Endpoint expected, string[] keys, string[] values) + public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, string[] keys, string[] values) { keys = keys ?? Array.Empty(); values = values ?? Array.Empty(); @@ -38,33 +38,33 @@ namespace Microsoft.AspNetCore.Routing.Matching } var zipped = keys.Zip(values, (k, v) => new KeyValuePair(k, v)); - AssertMatch(feature, context, expected, new RouteValueDictionary(zipped)); + AssertMatch(context, httpContext, expected, new RouteValueDictionary(zipped)); } public static void AssertMatch( - EndpointFeature feature, - HttpContext context, + EndpointSelectorContext context, + HttpContext httpContext, Endpoint expected, RouteValueDictionary values, bool ignoreValues = false) { - if (feature.Endpoint == null) + if (context.Endpoint == null) { throw new XunitException($"Was expected to match '{expected.DisplayName}' but did not match."); } - var actualValues = context.Features.Get().RouteValues; + var actualValues = httpContext.Features.Get().RouteValues; if (actualValues == null) { throw new XunitException("RouteValues is null."); } - if (!object.ReferenceEquals(expected, feature.Endpoint)) + if (!object.ReferenceEquals(expected, context.Endpoint)) { throw new XunitException( $"Was expected to match '{expected.DisplayName}' but matched " + - $"'{feature.Endpoint.DisplayName}' with values: {FormatRouteValues(actualValues)}."); + $"'{context.Endpoint.DisplayName}' with values: {FormatRouteValues(actualValues)}."); } if (!ignoreValues) @@ -81,13 +81,13 @@ namespace Microsoft.AspNetCore.Routing.Matching } } - public static void AssertNotMatch(EndpointFeature feature, HttpContext context) + public static void AssertNotMatch(EndpointSelectorContext context, HttpContext httpContext) { - if (feature.Endpoint != null) + if (context.Endpoint != null) { throw new XunitException( - $"Was expected not to match '{feature.Endpoint.DisplayName}' " + - $"but matched with values: {FormatRouteValues(context.Features.Get().RouteValues)}."); + $"Was expected not to match '{context.Endpoint.DisplayName}' " + + $"but matched with values: {FormatRouteValues(httpContext.Features.Get().RouteValues)}."); } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.SingleEndpoint.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.SingleEndpoint.cs index 17b569e261..667b5f3f1e 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.SingleEndpoint.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.SingleEndpoint.cs @@ -13,13 +13,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/"); - var (httpContext, feature) = CreateContext("/"); + var (httpContext, context) = CreateContext("/"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -27,13 +27,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/simple"); - var (httpContext, feature) = CreateContext("/simple"); + var (httpContext, context) = CreateContext("/simple"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Fact] @@ -41,13 +41,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/simple"); - var (httpContext, feature) = CreateContext("/simple/"); + var (httpContext, context) = CreateContext("/simple/"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Theory] @@ -58,13 +58,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/Simple"); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Some matchers will optimize for the ASCII case @@ -75,13 +75,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Matchers should operate on the decoded representation - a matcher that calls @@ -93,13 +93,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } [Theory] @@ -113,13 +113,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/simple"); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] @@ -130,13 +130,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext("/simple"); + var (httpContext, context) = CreateContext("/simple"); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Matchers do their own 'splitting' of the path into segments, so including @@ -153,13 +153,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint); + MatcherAssert.AssertMatch(context, httpContext, endpoint); } // Matchers do their own 'splitting' of the path into segments, so including @@ -184,13 +184,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Fact] @@ -198,14 +198,14 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/{p}"); - var (httpContext, feature) = CreateContext("/14"); + var (httpContext, context) = CreateContext("/14"); var values = new RouteValueDictionary(new { p = "14", }); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Fact] @@ -213,14 +213,14 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/{p:int}"); - var (httpContext, feature) = CreateContext("/14"); + var (httpContext, context) = CreateContext("/14"); var values = new RouteValueDictionary(new { p = "14", }); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Fact] @@ -228,14 +228,14 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/{p}"); - var (httpContext, feature) = CreateContext("/14/"); + var (httpContext, context) = CreateContext("/14/"); var values = new RouteValueDictionary(new { p = "14", }); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Fact] @@ -243,7 +243,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/foo/{ }/{.!$%}/{dynamic.data}"); - var (httpContext, feature) = CreateContext("/foo/space/weirdmatch/matcherid"); + var (httpContext, context) = CreateContext("/foo/space/weirdmatch/matcherid"); var values = new RouteValueDictionary() { { " ", "space" }, @@ -252,10 +252,10 @@ namespace Microsoft.AspNetCore.Routing.Matching }; // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, values); } [Theory] @@ -267,13 +267,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher("/{p}"); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } [Theory] @@ -288,13 +288,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertMatch(feature, httpContext, endpoint, keys, values); + MatcherAssert.AssertMatch(context, httpContext, endpoint, keys, values); } [Theory] @@ -317,13 +317,13 @@ namespace Microsoft.AspNetCore.Routing.Matching { // Arrange var (matcher, endpoint) = CreateMatcher(template); - var (httpContext, feature) = CreateContext(path); + var (httpContext, context) = CreateContext(path); // Act - await matcher.MatchAsync(httpContext, feature); + await matcher.MatchAsync(httpContext, context); // Assert - MatcherAssert.AssertNotMatch(feature, httpContext); + MatcherAssert.AssertNotMatch(context, httpContext); } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.cs index 06f4e14a73..c7615bc19c 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/MatcherConformanceTest.cs @@ -14,21 +14,21 @@ namespace Microsoft.AspNetCore.Routing.Matching { internal abstract Matcher CreateMatcher(params RouteEndpoint[] endpoints); - internal static (HttpContext httpContext, EndpointFeature feature) CreateContext(string path) + internal static (HttpContext httpContext, EndpointSelectorContext context) CreateContext(string path) { var httpContext = new DefaultHttpContext(); httpContext.Request.Method = "TEST"; httpContext.Request.Path = path; httpContext.RequestServices = CreateServices(); - var feature = new EndpointFeature + var context = new EndpointSelectorContext() { RouteValues = new RouteValueDictionary() }; - httpContext.Features.Set(feature); - httpContext.Features.Set(feature); + httpContext.Features.Set(context); + httpContext.Features.Set(context); - return (httpContext, feature); + return (httpContext, context); } // The older routing implementations retrieve services when they first execute. diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcher.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcher.cs index 88395c2eff..39eaee6cdd 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcher.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcher.cs @@ -4,7 +4,6 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Routing.Matching { @@ -18,25 +17,25 @@ namespace Microsoft.AspNetCore.Routing.Matching _inner = inner; } - public async override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public async override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } - if (feature == null) + if (context == null) { - throw new ArgumentNullException(nameof(feature)); + throw new ArgumentNullException(nameof(context)); } - var context = new RouteContext(httpContext); - await _inner.RouteAsync(context); + var routeContext = new RouteContext(httpContext); + await _inner.RouteAsync(routeContext); - if (context.Handler != null) + if (routeContext.Handler != null) { - feature.RouteValues = context.RouteData.Values; - await context.Handler(httpContext); + context.RouteValues = routeContext.RouteData.Values; + await routeContext.Handler(httpContext); } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcherBuilder.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcherBuilder.cs index 5d21424cb5..a47aafac5b 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcherBuilder.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/RouteMatcherBuilder.cs @@ -76,6 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { private readonly EndpointSelector _selector; private readonly RouteEndpoint[] _candidates; + private readonly RouteValueDictionary[] _values; private readonly int[] _scores; public SelectorRouter(EndpointSelector selector, RouteEndpoint[] candidates) @@ -83,6 +84,7 @@ namespace Microsoft.AspNetCore.Routing.Matching _selector = selector; _candidates = candidates; + _values = new RouteValueDictionary[_candidates.Length]; _scores = new int[_candidates.Length]; } @@ -91,18 +93,18 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new NotImplementedException(); } - public async Task RouteAsync(RouteContext context) + public async Task RouteAsync(RouteContext routeContext) { - var feature = (EndpointFeature)context.HttpContext.Features.Get(); + var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get(); // This is needed due to a quirk of our tests - they reuse the endpoint feature // across requests. - feature.Endpoint = null; + context.Endpoint = null; - await _selector.SelectAsync(context.HttpContext, feature, new CandidateSet(_candidates, _scores)); - if (feature.Endpoint != null) + await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores)); + if (context.Endpoint != null) { - context.Handler = (_) => Task.CompletedTask; + routeContext.Handler = (_) => Task.CompletedTask; } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcher.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcher.cs index 3c8c10daa0..c7477e4611 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcher.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcher.cs @@ -19,25 +19,25 @@ namespace Microsoft.AspNetCore.Routing.Matching _inner = inner; } - public async override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public async override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } - if (feature == null) + if (context == null) { - throw new ArgumentNullException(nameof(feature)); + throw new ArgumentNullException(nameof(context)); } - var context = new RouteContext(httpContext); - await _inner.RouteAsync(context); + var routeContext = new RouteContext(httpContext); + await _inner.RouteAsync(routeContext); - if (context.Handler != null) + if (routeContext.Handler != null) { - feature.RouteValues = context.RouteData.Values; - await context.Handler(httpContext); + context.RouteValues = routeContext.RouteData.Values; + await routeContext.Handler(httpContext); } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcherBuilder.cs b/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcherBuilder.cs index 83c5115120..0924e3bfd8 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcherBuilder.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/Matching/TreeRouterMatcherBuilder.cs @@ -78,6 +78,7 @@ namespace Microsoft.AspNetCore.Routing.Matching { private readonly EndpointSelector _selector; private readonly RouteEndpoint[] _candidates; + private readonly RouteValueDictionary[] _values; private readonly int[] _scores; public SelectorRouter(EndpointSelector selector, RouteEndpoint[] candidates) @@ -85,6 +86,7 @@ namespace Microsoft.AspNetCore.Routing.Matching _selector = selector; _candidates = candidates; + _values = new RouteValueDictionary[_candidates.Length]; _scores = new int[_candidates.Length]; } @@ -93,17 +95,17 @@ namespace Microsoft.AspNetCore.Routing.Matching throw new NotImplementedException(); } - public async Task RouteAsync(RouteContext context) + public async Task RouteAsync(RouteContext routeContext) { - var feature = (EndpointFeature)context.HttpContext.Features.Get(); + var context = (EndpointSelectorContext)routeContext.HttpContext.Features.Get(); // This is needed due to a quirk of our tests - they reuse the endpoint feature. - feature.Endpoint = null; + context.Endpoint = null; - await _selector.SelectAsync(context.HttpContext, feature, new CandidateSet(_candidates, _scores)); - if (feature.Endpoint != null) + await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores)); + if (context.Endpoint != null) { - context.Handler = (_) => Task.CompletedTask; + routeContext.Handler = (_) => Task.CompletedTask; } } } diff --git a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs index 5da00c6f25..151ca9c67a 100644 --- a/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs +++ b/test/Microsoft.AspNetCore.Routing.Tests/TestObjects/TestMatcher.cs @@ -17,12 +17,12 @@ namespace Microsoft.AspNetCore.Routing.TestObjects _isHandled = isHandled; } - public override Task MatchAsync(HttpContext httpContext, EndpointFeature feature) + public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (_isHandled) { - feature.RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index" }); - feature.Endpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "Test endpoint"); + context.RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index" }); + context.Endpoint = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "Test endpoint"); } return Task.CompletedTask;