// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Routing.Matching { // A test-only matcher implementation - used as a baseline for more compilated // perf tests. The idea with this matcher is that we can cheat on the requirements // to establish a lower bound for perf comparisons. internal class BarebonesMatcher : Matcher { public readonly InnerMatcher[] Matchers; public BarebonesMatcher(InnerMatcher[] matchers) { Matchers = matchers; } public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } var path = httpContext.Request.Path.Value; for (var i = 0; i < Matchers.Length; i++) { if (Matchers[i].TryMatch(path)) { context.Endpoint = Matchers[i].Endpoint; context.RouteValues = new RouteValueDictionary(); } } return Task.CompletedTask; } public sealed class InnerMatcher : Matcher { public readonly RouteEndpoint Endpoint; private readonly string[] _segments; private readonly Candidate[] _candidates; public InnerMatcher(string[] segments, RouteEndpoint endpoint) { _segments = segments; Endpoint = endpoint; _candidates = new Candidate[] { new Candidate(endpoint), }; } public bool TryMatch(string path) { var segment = 0; var start = 1; // PathString always has a leading slash var end = 0; while ((end = path.IndexOf('/', start)) >= 0) { var comparand = _segments.Length > segment ? _segments[segment] : null; if ((comparand == null && end - start == 0) || (comparand != null && (comparand.Length != end - start || string.Compare( path, start, comparand, 0, comparand.Length, StringComparison.OrdinalIgnoreCase) != 0))) { return false; } start = end + 1; segment++; } // residue var length = path.Length - start; if (length > 0) { var comparand = _segments.Length > segment ? _segments[segment] : null; if (comparand != null && (comparand.Length != length || string.Compare( path, start, comparand, 0, comparand.Length, StringComparison.OrdinalIgnoreCase) != 0)) { return false; } segment++; } return segment == _segments.Length; } internal Candidate[] FindCandidateSet(string path, ReadOnlySpan segments) { if (TryMatch(path)) { return _candidates; } return Array.Empty(); } public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context) { if (TryMatch(httpContext.Request.Path.Value)) { context.Endpoint = Endpoint; context.RouteValues = new RouteValueDictionary(); } return Task.CompletedTask; } } } }