public void Create_CreatesCandidateSet(int count) { // Arrange var endpoints = new RouteEndpoint[count]; for (var i = 0; i < endpoints.Length; i++) { endpoints[i] = CreateEndpoint($"/{i}"); } var builder = CreateDfaMatcherBuilder(); var candidates = builder.CreateCandidates(endpoints); // Act var candidateSet = new CandidateSet(candidates); // Assert for (var i = 0; i < candidateSet.Count; i++) { ref var state = ref candidateSet[i]; 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)); }
public override Task SelectAsync( ProtoContext httpContext, 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)); } // Fast path: We can specialize for trivial numbers of candidates since there can // be no ambiguities 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; }
public Task ApplyAsync(ProtoContext httpContext, EndpointSelectorContext context, CandidateSet candidates) { throw new NotImplementedException(); }
public sealed override Task MatchAsync(ProtoContext httpContext, EndpointSelectorContext context) { if (httpContext == null) { throw new ArgumentNullException(nameof(httpContext)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } // All of the logging we do here is at level debug, so we can get away with doing a single check. var log = _logger.IsEnabled(LogLevel.Debug); // 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); if (candidates.Length == 0) { if (log) { Logger.CandidatesNotFound(_logger, path); } return(Task.CompletedTask); } if (log) { Logger.CandidatesFound(_logger, path, candidates); } // At this point we have a candidate set, defined as a list of endpoints in // priority order. // // We don't yet know that any candidate can be considered a match, because // we haven't processed things like route constraints and complex segments. // // Now we'll iterate each endpoint to capture route values, process constraints, // and process complex segments. // `candidates` has all of our internal state that we use to process the // set of endpoints before we call the EndpointSelector. // // `candidateSet` is the mutable state that we pass to the EndpointSelector. var candidateSet = new CandidateSet(candidates); for (var i = 0; i < candidates.Length; i++) { // PERF: using ref here to avoid copying around big structs. // // Reminder! // candidate: readonly data about the endpoint and how to match // state: mutable storarge for our processing ref var candidate = ref candidates[i]; ref var state = ref candidateSet[i];
/// <summary> /// Asynchronously selects an <see cref="Endpoint"/> from the <see cref="CandidateSet"/>. /// </summary> /// <param name="httpContext">The <see cref="ProtoContext"/> associated with the current request.</param> /// <param name="context">The <see cref="EndpointSelectorContext"/> associated with the current request.</param> /// <param name="candidates">The <see cref="CandidateSet"/>.</param> /// <returns>A <see cref="Task"/> that completes asynchronously once endpoint selection is complete.</returns> /// <remarks> /// An <see cref="EndpointSelector"/> should assign the <see cref="EndpointSelectorContext.Endpoint"/> /// and <see cref="EndpointSelectorContext.RouteValues"/> properties once an endpoint is selected. /// </remarks> public abstract Task SelectAsync( ProtoContext httpContext, EndpointSelectorContext context, CandidateSet candidates);