public async Task apply_should_have_candidate_for_matched_api_version()
        {
            // arrange
            var feature = new Mock <IApiVersioningFeature>();
            var context = new EndpointSelectorContext();
            var items   = new object[]
            {
                new ActionDescriptor()
                {
                    Properties = { [typeof(ApiVersionModel)] = new ApiVersionModel(new ApiVersion(1, 0)) },
                },
            };
            var endpoint   = new Endpoint(c => CompletedTask, new EndpointMetadataCollection(items), default);
            var candidates = new CandidateSet(new[] { endpoint }, new[] { new RouteValueDictionary() }, new[] { 0 });
            var policy     = new ApiVersionMatcherPolicy(NewDefaultOptions(), NewReporter(), NewLoggerFactory());

            feature.SetupProperty(f => f.RequestedApiVersion, new ApiVersion(1, 0));

            var httpContext = NewHttpContext(feature);

            // act
            await policy.ApplyAsync(httpContext, context, candidates);

            // assert
            candidates.IsValidCandidate(0).Should().BeTrue();
        }
示例#2
0
        internal static void Select(
            HttpContext httpContext,
            EndpointSelectorContext context,
            CandidateState[] candidateState)
        {
            // Fast path: We can specialize for trivial numbers of candidates since there can
            // be no ambiguities
            switch (candidateState.Length)
            {
            case 0:
            {
                // Do nothing
                break;
            }

            case 1:
            {
                ref var state = ref candidateState[0];
                if (CandidateSet.IsValidCandidate(ref state))
                {
                    context.Endpoint    = state.Endpoint;
                    context.RouteValues = state.Values;
                }

                break;
            }
示例#3
0
        public override Task MatchAsync(ProtoContext 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 async Task Endpoints_InvokeReturnedEndpoint_ActionInvokerProviderCalled()
        {
            // Arrange
            var endpointFeature = new EndpointSelectorContext
            {
                RouteValues = new RouteValueDictionary()
            };

            var featureCollection = new FeatureCollection();

            featureCollection.Set <IEndpointFeature>(endpointFeature);
            featureCollection.Set <IRouteValuesFeature>(endpointFeature);
            featureCollection.Set <IRoutingFeature>(endpointFeature);

            var httpContextMock = new Mock <HttpContext>();

            httpContextMock.Setup(m => m.Features).Returns(featureCollection);

            var descriptorProviderMock = new Mock <IActionDescriptorCollectionProvider>();

            descriptorProviderMock.Setup(m => m.ActionDescriptors).Returns(new ActionDescriptorCollection(new List <ActionDescriptor>
            {
                new ActionDescriptor
                {
                    AttributeRouteInfo = new AttributeRouteInfo
                    {
                        Template = string.Empty
                    },
                    FilterDescriptors = new List <FilterDescriptor>()
                }
            }, 0));

            var actionInvokerCalled = false;
            var actionInvokerMock   = new Mock <IActionInvoker>();

            actionInvokerMock.Setup(m => m.InvokeAsync()).Returns(() =>
            {
                actionInvokerCalled = true;
                return(Task.CompletedTask);
            });

            var actionInvokerProviderMock = new Mock <IActionInvokerFactory>();

            actionInvokerProviderMock.Setup(m => m.CreateInvoker(It.IsAny <ActionContext>())).Returns(actionInvokerMock.Object);

            var dataSource = CreateMvcEndpointDataSource(
                descriptorProviderMock.Object,
                new MvcEndpointInvokerFactory(actionInvokerProviderMock.Object));

            // Act
            var endpoints = dataSource.Endpoints;

            // Assert
            var endpoint        = Assert.Single(endpoints);
            var matcherEndpoint = Assert.IsType <RouteEndpoint>(endpoint);

            await matcherEndpoint.RequestDelegate(httpContextMock.Object);

            Assert.True(actionInvokerCalled);
        }
        /// <inheritdoc />
        public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            Arg.NotNull(httpContext, nameof(httpContext));
            Arg.NotNull(context, nameof(context));
            Arg.NotNull(candidates, nameof(candidates));

            if (IsRequestedApiVersionAmbiguous(httpContext, context, out var apiVersion))
            {
                return(CompletedTask);
            }

            if (apiVersion == null && Options.AssumeDefaultVersionWhenUnspecified)
            {
                apiVersion = TrySelectApiVersion(httpContext, candidates);
                httpContext.Features.Get <IApiVersioningFeature>().RequestedApiVersion = apiVersion;
            }

            var finalMatches = EvaluateApiVersion(httpContext, candidates, apiVersion);

            if (finalMatches.Count == 0)
            {
                context.Endpoint = ClientError(httpContext, candidates);
            }
            else
            {
                for (var i = 0; i < finalMatches.Count; i++)
                {
                    candidates.SetValidity(finalMatches[i].index, true);
                }
            }

            return(CompletedTask);
        }
        public async Task ApplyAsync_EndpointAllowsAnyContentType_MatchWithAnyContentType()
        {
            // Arrange
            var endpoints = new[]
            {
                CreateEndpoint("/", new ConsumesMetadata(Array.Empty <string>())),
            };

            var candidates  = CreateCandidateSet(endpoints);
            var context     = new EndpointSelectorContext();
            var httpContext = new DefaultHttpContext()
            {
                Request =
                {
                    ContentType = "text/plain",
                },
            };

            var policy = CreatePolicy();

            // Act
            await policy.ApplyAsync(httpContext, context, candidates);

            // Assert
            Assert.True(candidates.IsValidCandidate(0));
        }
        public async Task apply_should_use_400_endpoint_for_ambiguous_api_version()
        {
            // arrange
            var feature        = new Mock <IApiVersioningFeature>();
            var errorResponses = new Mock <IErrorResponseProvider>();
            var result         = new Mock <IActionResult>();

            feature.SetupGet(f => f.RequestedApiVersion).Throws(new AmbiguousApiVersionException("Test", new[] { "1.0", "2.0" }));
            result.Setup(r => r.ExecuteResultAsync(It.IsAny <ActionContext>())).Returns(CompletedTask);
            errorResponses.Setup(er => er.CreateResponse(It.IsAny <ErrorResponseContext>())).Returns(result.Object);

            var options = Options.Create(new ApiVersioningOptions()
            {
                ErrorResponses = errorResponses.Object
            });
            var policy      = new ApiVersionMatcherPolicy(options, NewReporter(), NewLoggerFactory());
            var httpContext = NewHttpContext(feature);
            var context     = new EndpointSelectorContext();
            var candidates  = new CandidateSet(Empty <Endpoint>(), Empty <RouteValueDictionary>(), Empty <int>());

            // act
            await policy.ApplyAsync(httpContext, context, candidates);

            await context.Endpoint.RequestDelegate(httpContext);

            // assert
            result.Verify(r => r.ExecuteResultAsync(It.IsAny <ActionContext>()), Once());
            errorResponses.Verify(er => er.CreateResponse(It.Is <ErrorResponseContext>(c => c.StatusCode == 400 && c.ErrorCode == "AmbiguousApiVersion")), Once());
        }
        public async Task ApplyAsync_EndpointDoesNotMatch_DoesNotReturns415WithContentTypeWildcardEndpoint()
        {
            // Arrange
            var endpoints = new[]
            {
                CreateEndpoint("/", new ConsumesMetadata(new string[] { "text/xml", "application/xml", })),
                CreateEndpoint("/", new ConsumesMetadata(new string[] { "*/*", }))
            };

            var candidates  = CreateCandidateSet(endpoints);
            var context     = new EndpointSelectorContext();
            var httpContext = new DefaultHttpContext()
            {
                Request =
                {
                    ContentType = "application/json",
                },
            };

            var policy = CreatePolicy();

            // Act
            await policy.ApplyAsync(httpContext, context, candidates);

            // Assert
            Assert.False(candidates.IsValidCandidate(0));
            Assert.True(candidates.IsValidCandidate(1));
            Assert.Null(context.Endpoint);
        }
        public async Task ApplyAsync_EndpointHasMultipleContentType_MatchWithValidContentType()
        {
            // Arrange
            var endpoints = new[]
            {
                CreateEndpoint("/", new ConsumesMetadata(new string[] { "text/xml", "application/xml", })),
            };

            var candidates  = CreateCandidateSet(endpoints);
            var context     = new EndpointSelectorContext();
            var httpContext = new DefaultHttpContext()
            {
                Request =
                {
                    ContentType = "application/xml",
                },
            };

            var policy = CreatePolicy();

            // Act
            await policy.ApplyAsync(httpContext, context, candidates);

            // Assert
            Assert.True(candidates.IsValidCandidate(0));
        }
示例#10
0
        public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate = ref candidates[i];
                var     endpoint  = candidate.Endpoint;

                var page = endpoint.Metadata.GetMetadata <PageActionDescriptor>();
                if (page != null)
                {
                    // We found an endpoint instance that has a PageActionDescriptor, but not a
                    // CompiledPageActionDescriptor. Update the CandidateSet.
                    var compiled = _loader.LoadAsync(page);
                    if (compiled.IsCompletedSuccessfully)
                    {
                        candidates.ReplaceEndpoint(i, compiled.Result.Endpoint, candidate.Values);
                    }
                    else
                    {
                        // In the most common case, GetOrAddAsync will return a synchronous result.
                        // Avoid going async since this is a fairly hot path.
                        return(ApplyAsyncAwaited(candidates, compiled, i));
                    }
                }
            }
        public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate = ref candidates[i];
                var     endpoint  = (RouteEndpoint)candidate.Endpoint;

                var page = endpoint.Metadata.GetMetadata <PageActionDescriptor>();
                if (page != null)
                {
                    var compiled = _loader.Load(page);
                    candidates.ReplaceEndpoint(i, compiled.Endpoint, candidate.Values);
                }
            }
示例#12
0
        public async Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            if (candidates == null)
            {
                throw new ArgumentNullException(nameof(candidates));
            }

            // There's no real benefit here from trying to avoid the async state machine.
            // We only execute on nodes that contain a dynamic policy, and thus always have
            // to await something.
            for (var i = 0; i < candidates.Count; i++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var endpoint = candidates[i].Endpoint;

                var metadata = endpoint.Metadata.GetMetadata <DynamicPageMetadata>();
                if (metadata == null)
                {
                    continue;
                }

                var matchedValues = candidates[i].Values;
                var endpoints     = _selector.SelectEndpoints(metadata.Values);
                if (endpoints.Count == 0)
                {
                    // If there's no match this is a configuration error. We can't really check
                    // during startup that the action you configured exists.
                    throw new InvalidOperationException(
                              "Cannot find the fallback endpoint specified by route values: " +
                              "{ " + string.Join(", ", metadata.Values.Select(kvp => $"{kvp.Key}: {kvp.Value}")) + " }.");
                }

                // It is possible to have more than one result for pages but they are equivalent.

                var compiled = await _loader.LoadAsync(endpoints[0].Metadata.GetMetadata <PageActionDescriptor>());

                var replacement = compiled.Endpoint;

                // We need to provide the route values associated with this endpoint, so that features
                // like URL generation work.
                var values = new RouteValueDictionary(metadata.Values);

                // Include values that were matched by the fallback route.
                foreach (var kvp in matchedValues)
                {
                    values.TryAdd(kvp.Key, kvp.Value);
                }

                candidates.ReplaceEndpoint(i, replacement, values);
            }
        }
示例#13
0
        public async Task LegacyRouter()
        {
            var httpContext = Requests[0];
            var feature     = new EndpointSelectorContext(httpContext);

            await _route.MatchAsync(httpContext, feature);

            Validate(httpContext, Endpoints[0], feature.Endpoint);
        }
示例#14
0
        public async Task Baseline()
        {
            var httpContext = Requests[0];
            var feature     = new EndpointSelectorContext(httpContext);

            await _baseline.MatchAsync(httpContext, feature);

            Validate(httpContext, Endpoints[0], feature.Endpoint);
        }
示例#15
0
        public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidates)
        {
            for (var i = 0; i < candidates.Count; i++)
            {
                ref var candidate   = ref candidates[i];
                var     newEndpoint = _negotiateEndpointCache.GetOrAdd(candidate.Endpoint, CreateNegotiateEndpoint);

                candidates.ReplaceEndpoint(i, newEndpoint, candidate.Values);
            }
示例#16
0
 public static void AssertNotMatch(EndpointSelectorContext context, HttpContext httpContext)
 {
     if (context.Endpoint != null)
     {
         throw new XunitException(
                   $"Was expected not to match '{context.Endpoint.DisplayName}' " +
                   $"but matched with values: {FormatRouteValues(httpContext.Features.Get<IRouteValuesFeature>().RouteValues)}.");
     }
 }
示例#17
0
        public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
        {
            if (_isHandled)
            {
                context.RouteValues = new RouteValueDictionary(new { controller = "Home", action = "Index" });
                context.Endpoint    = new Endpoint(TestConstants.EmptyRequestDelegate, EndpointMetadataCollection.Empty, "Test endpoint");
            }

            return(Task.CompletedTask);
        }
        private static (HttpContext httpContext, EndpointSelectorContext context) CreateContext()
        {
            var context     = new EndpointSelectorContext();
            var httpContext = new DefaultHttpContext();

            httpContext.Features.Set <IEndpointFeature>(context);
            httpContext.Features.Set <IRouteValuesFeature>(context);

            return(httpContext, context);
        }
示例#19
0
            public override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
            {
                if (TryMatch(httpContext.Request.Path.Value))
                {
                    context.Endpoint    = Endpoint;
                    context.RouteValues = new RouteValueDictionary();
                }

                return(Task.CompletedTask);
            }
        public void Setup()
        {
            SetupEndpoints();

            SetupRequests();

            _baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
            _dfa      = SetupMatcher(CreateDfaMatcherBuilder());

            _feature = new EndpointSelectorContext();
        }
示例#21
0
        public static void AssertMatch(EndpointSelectorContext context, HttpContext httpContext, Endpoint expected, string[] keys, string[] values)
        {
            keys   = keys ?? Array.Empty <string>();
            values = values ?? Array.Empty <string>();

            if (keys.Length != values.Length)
            {
                throw new XunitException($"Keys and Values must be the same length.");
            }

            var zipped = keys.Zip(values, (k, v) => new KeyValuePair <string, object>(k, v));

            AssertMatch(context, httpContext, expected, new RouteValueDictionary(zipped));
        }
示例#22
0
            public async Task RouteAsync(RouteContext routeContext)
            {
                var context = new EndpointSelectorContext(routeContext.HttpContext);

                // This is needed due to a quirk of our tests - they reuse the endpoint feature.
                context.Endpoint = null;

                await _selector.SelectAsync(routeContext.HttpContext, context, new CandidateSet(_candidates, _values, _scores));

                if (context.Endpoint != null)
                {
                    routeContext.Handler = (_) => Task.CompletedTask;
                }
            }
示例#23
0
        public Task ApplyAsync(HttpContext httpContext, EndpointSelectorContext context, CandidateSet candidateSet)
        {
            // PERF: we can skip over action constraints if there aren't any app-wide.
            //
            // Running action constraints (or just checking for them) in a candidate set
            // is somewhat expensive compared to other routing operations. This should only
            // happen if user-code adds action constraints.
            if (ShouldRunActionConstraints)
            {
                ApplyActionConstraints(httpContext, candidateSet);
            }

            return(Task.CompletedTask);
        }
示例#24
0
        public void Setup()
        {
            SetupEndpoints();

            SetupRequests();

            // The perf is kinda slow for these benchmarks, so we do some sampling
            // of the request data.
            _samples = SampleRequests(EndpointCount, SampleCount);

            _baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
            _dfa      = SetupMatcher(CreateDfaMatcherBuilder());

            _feature = new EndpointSelectorContext();
        }
        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 context = new EndpointSelectorContext();

            httpContext.Features.Set <IEndpointFeature>(context);
            httpContext.Features.Set <IRouteValuesFeature>(context);

            return(httpContext, context);
        }
示例#26
0
        public async Task AddEndpoints_AttributeRouted_UsesActionInvoker()
        {
            // Arrange
            var values = new
            {
                action     = "Test",
                controller = "Test",
                page       = (string)null,
            };

            var action = CreateActionDescriptor(values, pattern: "/Test");

            var endpointFeature = new EndpointSelectorContext
            {
                RouteValues = new RouteValueDictionary()
            };

            var featureCollection = new FeatureCollection();

            featureCollection.Set <IEndpointFeature>(endpointFeature);
            featureCollection.Set <IRouteValuesFeature>(endpointFeature);
            featureCollection.Set <IRoutingFeature>(endpointFeature);

            var httpContextMock = new Mock <HttpContext>();

            httpContextMock.Setup(m => m.Features).Returns(featureCollection);

            var actionInvokerCalled = false;
            var actionInvokerMock   = new Mock <IActionInvoker>();

            actionInvokerMock.Setup(m => m.InvokeAsync()).Returns(() =>
            {
                actionInvokerCalled = true;
                return(Task.CompletedTask);
            });

            InvokerFactory
            .Setup(m => m.CreateInvoker(It.IsAny <ActionContext>()))
            .Returns(actionInvokerMock.Object);

            // Act
            var endpoint = CreateAttributeRoutedEndpoint(action);

            // Assert
            await endpoint.RequestDelegate(httpContextMock.Object);

            Assert.True(actionInvokerCalled);
        }
示例#27
0
        public async override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            var routeContext = new RouteContext(httpContext);
            await _inner.RouteAsync(routeContext);

            if (routeContext.Handler != null)
            {
                context.RouteValues = routeContext.RouteData.Values;
                await routeContext.Handler(httpContext);
            }
        }
示例#28
0
        public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            var path = httpContext.Request.Path.Value;

            if (string.Equals(_endpoint.RoutePattern.RawText, path, StringComparison.OrdinalIgnoreCase))
            {
                context.Endpoint    = _endpoint;
                context.RouteValues = new RouteValueDictionary();
            }

            return(Task.CompletedTask);
        }
        public void Setup()
        {
            Endpoints    = new RouteEndpoint[1];
            Endpoints[0] = CreateEndpoint("/plaintext");

            Requests    = new ProtoContext[1];
            Requests[0] = new DefaultProtoContext();
            Requests[0].RequestServices = CreateServices();
            Requests[0].Request.Path    = "/plaintext";

            _baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
            _dfa      = SetupMatcher(CreateDfaMatcherBuilder());
            _route    = SetupMatcher(new RouteMatcherBuilder());
            _tree     = SetupMatcher(new TreeRouterMatcherBuilder());

            _feature = new EndpointSelectorContext();
        }
示例#30
0
        public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelectorContext context)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

            // All of the logging we do here is at level debug, so we can get away with doing a single check.
            var log = _logger.IsEnabled(LogLevel.Debug);

            // The sequence of actions we take is optimized to avoid doing expensive work
            // like creating substrings, creating route value dictionaries, and calling
            // into policies like versioning.
            var path = httpContext.Request.Path.Value;

            // First tokenize the path into series of segments.
            Span <PathSegment> buffer = stackalloc PathSegment[_maxSegmentCount];
            var count    = FastPathTokenizer.Tokenize(path, buffer);
            var segments = buffer.Slice(0, count);

            // FindCandidateSet will process the DFA and return a candidate set. This does
            // some preliminary matching of the URL (mostly the literal segments).
            var(candidates, policies) = FindCandidateSet(httpContext, path, segments);
            var candidateCount = candidates.Length;

            if (candidateCount == 0)
            {
                if (log)
                {
                    Logger.CandidatesNotFound(_logger, path);
                }

                return(Task.CompletedTask);
            }

            if (log)
            {
                Logger.CandidatesFound(_logger, path, candidates);
            }

            var policyCount = policies.Length;

            // This is a fast path for single candidate, 0 policies and default selector
            if (candidateCount == 1 && policyCount == 0 && _isDefaultEndpointSelector)
            {
                ref readonly var candidate = ref candidates[0];