Beispiel #1
0
        public void Create_CreatesCandidateSet()
        {
            // Arrange
            var count     = 10;
            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));
            }
Beispiel #2
0
        /// <summary>
        /// For framework use only.
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="candidates"></param>
        /// <returns></returns>
        public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException(nameof(httpContext));
            }

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

            // Returning a 405 here requires us to return keep track of all 'seen' HTTP methods. We allocate to
            // keep track of this beause we either need to keep track of the HTTP methods or keep track of the
            // endpoints - both allocate.
            //
            // Those code only runs in the presence of dynamic endpoints anyway.
            //
            // We want to return a 405 iff we eliminated ALL of the currently valid endpoints due to HTTP method
            // mismatch.
            bool?            needs405Endpoint = null;
            HashSet <string> methods          = null;

            for (var i = 0; i < candidates.Count; i++)
            {
                // We do this check first for consistency with how 405 is implemented for the graph version
                // of this code. We still want to know if any endpoints in this set require an HTTP method
                // even if those endpoints are already invalid.
                var metadata = candidates[i].Endpoint.Metadata.GetMetadata <IHttpMethodMetadata>();
                if (metadata == null || metadata.HttpMethods.Count == 0)
                {
                    // Can match any method.
                    needs405Endpoint = false;
                    continue;
                }

                // Saw a valid endpoint.
                needs405Endpoint = needs405Endpoint ?? true;

                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var httpMethod = httpContext.Request.Method;
                var headers    = httpContext.Request.Headers;
                if (metadata.AcceptCorsPreflight &&
                    string.Equals(httpMethod, PreflightHttpMethod, StringComparison.OrdinalIgnoreCase) &&
                    headers.ContainsKey(HeaderNames.Origin) &&
                    headers.TryGetValue(HeaderNames.AccessControlRequestMethod, out var accessControlRequestMethod) &&
                    !StringValues.IsNullOrEmpty(accessControlRequestMethod))
                {
                    needs405Endpoint = false; // We don't return a 405 for a CORS preflight request when the endpoints accept CORS preflight.
                    httpMethod       = accessControlRequestMethod;
                }

                var matched = false;
                for (var j = 0; j < metadata.HttpMethods.Count; j++)
                {
                    var candidateMethod = metadata.HttpMethods[j];
                    if (!string.Equals(httpMethod, candidateMethod, StringComparison.OrdinalIgnoreCase))
                    {
                        methods = methods ?? new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                        methods.Add(candidateMethod);
                        continue;
                    }

                    matched          = true;
                    needs405Endpoint = false;
                    break;
                }

                if (!matched)
                {
                    candidates.SetValidity(i, false);
                }
            }

            if (needs405Endpoint == true)
            {
                // We saw some endpoints coming in, and we eliminated them all.
                httpContext.SetEndpoint(CreateRejectionEndpoint(methods.OrderBy(m => m, StringComparer.OrdinalIgnoreCase)));
                httpContext.Request.RouteValues = null;
            }

            return(Task.CompletedTask);
        }
        public Task ApplyAsync(HttpContext httpContext, 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++)
            {
                if (!candidates.IsValidCandidate(i))
                {
                    continue;
                }

                var hosts = candidates[i].Endpoint.Metadata.GetMetadata <IngressHostMetadata>()?.Hosts;
                if (hosts == null || hosts.Count == 0)
                {
                    // Can match any host.
                    continue;
                }

                var matched = false;
                var(requestHost, requestPort) = GetHostAndPort(httpContext);
                for (var j = 0; j < hosts.Count; j++)
                {
                    var host = hosts[j].AsSpan();
                    var port = ReadOnlySpan <char> .Empty;

                    // Split into host and port
                    var pivot = host.IndexOf(':');
                    if (pivot >= 0)
                    {
                        port = host.Slice(pivot + 1);
                        host = host.Slice(0, pivot);
                    }

                    if (host == null || host.Equals(WildcardHost, StringComparison.OrdinalIgnoreCase))
                    {
                        // Can match any host
                    }
                    else if (
                        host.StartsWith(WildcardPrefix) &&

                        // Note that we only slice of the `*`. We want to match the leading `.` also.
                        MemoryExtensions.EndsWith(requestHost, host.Slice(WildcardHost.Length), StringComparison.OrdinalIgnoreCase))
                    {
                        // Matches a suffix wildcard.
                    }
                    else if (MemoryExtensions.Equals(requestHost, host, StringComparison.OrdinalIgnoreCase))
                    {
                        // Matches exactly
                    }
                    else
                    {
                        // If we get here then the host doesn't match.
                        continue;
                    }

                    if (port.Equals(WildcardHost, StringComparison.OrdinalIgnoreCase))
                    {
                        // Port is a wildcard, we allow any port.
                    }
                    else if (port.Length > 0 && (!int.TryParse(port, out var parsed) || parsed != requestPort))
                    {
                        // If we get here then the port doesn't match.
                        continue;
                    }

                    matched = true;
                    break;
                }

                if (!matched)
                {
                    candidates.SetValidity(i, false);
                }
            }

            return(Task.CompletedTask);
        }