public override Task SelectAsync( HttpContext 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; }
internal static void Select(HttpContext httpContext, 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)) { httpContext.SetEndpoint(state.Endpoint); httpContext.Request.RouteValues = state.Values !; } break; }
/// <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); }