xds: fix matchRoute in RoutingUtils and clean up implementations in XdsNameResolver. (#10095)

This commit is contained in:
chenwei321 2023-04-28 04:28:44 +08:00 committed by GitHub
parent 9d1e089c27
commit c5b825aa7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 235 additions and 298 deletions

View File

@ -149,7 +149,7 @@ final class RoutingUtils {
return false; return false;
} }
for (HeaderMatcher headerMatcher : routeMatch.headerMatchers()) { for (HeaderMatcher headerMatcher : routeMatch.headerMatchers()) {
if (!matchHeader(headerMatcher, getHeaderValue(headers, headerMatcher.name()))) { if (!headerMatcher.matches(getHeaderValue(headers, headerMatcher.name()))) {
return false; return false;
} }
} }
@ -170,35 +170,6 @@ final class RoutingUtils {
return pathMatcher.regEx().matches(fullMethodName); return pathMatcher.regEx().matches(fullMethodName);
} }
private static boolean matchHeader(HeaderMatcher headerMatcher, @Nullable String value) {
if (headerMatcher.present() != null) {
return (value == null) == headerMatcher.present().equals(headerMatcher.inverted());
}
if (value == null) {
return false;
}
boolean baseMatch;
if (headerMatcher.exactValue() != null) {
baseMatch = headerMatcher.exactValue().equals(value);
} else if (headerMatcher.safeRegEx() != null) {
baseMatch = headerMatcher.safeRegEx().matches(value);
} else if (headerMatcher.range() != null) {
long numValue;
try {
numValue = Long.parseLong(value);
baseMatch = numValue >= headerMatcher.range().start()
&& numValue <= headerMatcher.range().end();
} catch (NumberFormatException ignored) {
baseMatch = false;
}
} else if (headerMatcher.prefix() != null) {
baseMatch = value.startsWith(headerMatcher.prefix());
} else {
baseMatch = value.endsWith(headerMatcher.suffix());
}
return baseMatch != headerMatcher.inverted();
}
@Nullable @Nullable
private static String getHeaderValue(Metadata headers, String headerName) { private static String getHeaderValue(Metadata headers, String headerName) {
if (headerName.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { if (headerName.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {

View File

@ -60,16 +60,12 @@ import io.grpc.xds.VirtualHost.Route.RouteAction;
import io.grpc.xds.VirtualHost.Route.RouteAction.ClusterWeight; import io.grpc.xds.VirtualHost.Route.RouteAction.ClusterWeight;
import io.grpc.xds.VirtualHost.Route.RouteAction.HashPolicy; import io.grpc.xds.VirtualHost.Route.RouteAction.HashPolicy;
import io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy; import io.grpc.xds.VirtualHost.Route.RouteAction.RetryPolicy;
import io.grpc.xds.VirtualHost.Route.RouteMatch;
import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
import io.grpc.xds.XdsClient.ResourceWatcher; import io.grpc.xds.XdsClient.ResourceWatcher;
import io.grpc.xds.XdsListenerResource.LdsUpdate; import io.grpc.xds.XdsListenerResource.LdsUpdate;
import io.grpc.xds.XdsLogger.XdsLogLevel; import io.grpc.xds.XdsLogger.XdsLogLevel;
import io.grpc.xds.XdsNameResolverProvider.CallCounterProvider; import io.grpc.xds.XdsNameResolverProvider.CallCounterProvider;
import io.grpc.xds.XdsNameResolverProvider.XdsClientPoolFactory; import io.grpc.xds.XdsNameResolverProvider.XdsClientPoolFactory;
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate; import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
import io.grpc.xds.internal.Matchers.FractionMatcher;
import io.grpc.xds.internal.Matchers.HeaderMatcher;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -304,47 +300,6 @@ final class XdsNameResolver extends NameResolver {
receivedConfig = true; receivedConfig = true;
} }
@VisibleForTesting
@Nullable
static VirtualHost findVirtualHostForHostName(List<VirtualHost> virtualHosts, String hostName) {
// Domain search order:
// 1. Exact domain names: ``www.foo.com``.
// 2. Suffix domain wildcards: ``*.foo.com`` or ``*-bar.foo.com``.
// 3. Prefix domain wildcards: ``foo.*`` or ``foo-*``.
// 4. Special wildcard ``*`` matching any domain.
//
// The longest wildcards match first.
// Assuming only a single virtual host in the entire route configuration can match
// on ``*`` and a domain must be unique across all virtual hosts.
int matchingLen = -1; // longest length of wildcard pattern that matches host name
boolean exactMatchFound = false; // true if a virtual host with exactly matched domain found
VirtualHost targetVirtualHost = null; // target VirtualHost with longest matched domain
for (VirtualHost vHost : virtualHosts) {
for (String domain : vHost.domains()) {
boolean selected = false;
if (matchHostName(hostName, domain)) { // matching
if (!domain.contains("*")) { // exact matching
exactMatchFound = true;
targetVirtualHost = vHost;
break;
} else if (domain.length() > matchingLen) { // longer matching pattern
selected = true;
} else if (domain.length() == matchingLen && domain.startsWith("*")) { // suffix matching
selected = true;
}
}
if (selected) {
matchingLen = domain.length();
targetVirtualHost = vHost;
}
}
if (exactMatchFound) {
break;
}
}
return targetVirtualHost;
}
/** /**
* Returns {@code true} iff {@code hostName} matches the domain name {@code pattern} with * Returns {@code true} iff {@code hostName} matches the domain name {@code pattern} with
* case-insensitive. * case-insensitive.
@ -418,7 +373,8 @@ final class XdsNameResolver extends NameResolver {
routingCfg = routingConfig; routingCfg = routingConfig;
selectedOverrideConfigs = new HashMap<>(routingCfg.virtualHostOverrideConfig); selectedOverrideConfigs = new HashMap<>(routingCfg.virtualHostOverrideConfig);
for (Route route : routingCfg.routes) { for (Route route : routingCfg.routes) {
if (matchRoute(route.routeMatch(), "/" + args.getMethodDescriptor().getFullMethodName(), if (RoutingUtils.matchRoute(
route.routeMatch(), "/" + args.getMethodDescriptor().getFullMethodName(),
headers, random)) { headers, random)) {
selectedRoute = route; selectedRoute = route;
selectedOverrideConfigs.putAll(route.filterConfigOverrides()); selectedOverrideConfigs.putAll(route.filterConfigOverrides());
@ -619,34 +575,6 @@ final class XdsNameResolver extends NameResolver {
}; };
} }
@VisibleForTesting
static boolean matchRoute(RouteMatch routeMatch, String fullMethodName,
Metadata headers, ThreadSafeRandom random) {
if (!matchPath(routeMatch.pathMatcher(), fullMethodName)) {
return false;
}
for (HeaderMatcher headerMatcher : routeMatch.headerMatchers()) {
if (!headerMatcher.matches(getHeaderValue(headers, headerMatcher.name()))) {
return false;
}
}
FractionMatcher fraction = routeMatch.fractionMatcher();
return fraction == null || random.nextInt(fraction.denominator()) < fraction.numerator();
}
private static boolean matchPath(PathMatcher pathMatcher, String fullMethodName) {
if (pathMatcher.path() != null) {
return pathMatcher.caseSensitive()
? pathMatcher.path().equals(fullMethodName)
: pathMatcher.path().equalsIgnoreCase(fullMethodName);
} else if (pathMatcher.prefix() != null) {
return pathMatcher.caseSensitive()
? fullMethodName.startsWith(pathMatcher.prefix())
: fullMethodName.toLowerCase().startsWith(pathMatcher.prefix().toLowerCase());
}
return pathMatcher.regEx().matches(fullMethodName);
}
@Nullable @Nullable
private static String getHeaderValue(Metadata headers, String headerName) { private static String getHeaderValue(Metadata headers, String headerName) {
if (headerName.endsWith(Metadata.BINARY_HEADER_SUFFIX)) { if (headerName.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
@ -775,7 +703,7 @@ final class XdsNameResolver extends NameResolver {
private void updateRoutes(List<VirtualHost> virtualHosts, long httpMaxStreamDurationNano, private void updateRoutes(List<VirtualHost> virtualHosts, long httpMaxStreamDurationNano,
@Nullable List<NamedFilterConfig> filterConfigs) { @Nullable List<NamedFilterConfig> filterConfigs) {
String authority = overrideAuthority != null ? overrideAuthority : ldsResourceName; String authority = overrideAuthority != null ? overrideAuthority : ldsResourceName;
VirtualHost virtualHost = findVirtualHostForHostName(virtualHosts, authority); VirtualHost virtualHost = RoutingUtils.findVirtualHostForHostName(virtualHosts, authority);
if (virtualHost == null) { if (virtualHost == null) {
String error = "Failed to find virtual host matching hostname: " + authority; String error = "Failed to find virtual host matching hostname: " + authority;
logger.log(XdsLogLevel.WARNING, error); logger.log(XdsLogLevel.WARNING, error);

View File

@ -0,0 +1,231 @@
/*
* Copyright 2020 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import com.google.common.collect.ImmutableMap;
import com.google.re2j.Pattern;
import io.grpc.Metadata;
import io.grpc.xds.VirtualHost.Route;
import io.grpc.xds.VirtualHost.Route.RouteMatch;
import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
import io.grpc.xds.internal.Matchers.HeaderMatcher;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link RoutingUtils }. */
@RunWith(JUnit4.class)
public class RoutingUtilsTest {
@Test
public void findVirtualHostForHostName_exactMatchFirst() {
String hostname = "a.googleapis.com";
List<Route> routes = Collections.emptyList();
VirtualHost vHost1 = VirtualHost.create("virtualhost01.googleapis.com",
Arrays.asList("a.googleapis.com", "b.googleapis.com"), routes,
ImmutableMap.of());
VirtualHost vHost2 = VirtualHost.create("virtualhost02.googleapis.com",
Collections.singletonList("*.googleapis.com"), routes,
ImmutableMap.of());
VirtualHost vHost3 = VirtualHost.create("virtualhost03.googleapis.com",
Collections.singletonList("*"), routes,
ImmutableMap.of());
List<VirtualHost> virtualHosts = Arrays.asList(vHost1, vHost2, vHost3);
assertThat(RoutingUtils.findVirtualHostForHostName(virtualHosts, hostname))
.isEqualTo(vHost1);
}
@Test
public void findVirtualHostForHostName_preferSuffixDomainOverPrefixDomain() {
String hostname = "a.googleapis.com";
List<Route> routes = Collections.emptyList();
VirtualHost vHost1 = VirtualHost.create("virtualhost01.googleapis.com",
Arrays.asList("*.googleapis.com", "b.googleapis.com"), routes,
ImmutableMap.of());
VirtualHost vHost2 = VirtualHost.create("virtualhost02.googleapis.com",
Collections.singletonList("a.googleapis.*"), routes,
ImmutableMap.of());
VirtualHost vHost3 = VirtualHost.create("virtualhost03.googleapis.com",
Collections.singletonList("*"), routes,
ImmutableMap.of());
List<VirtualHost> virtualHosts = Arrays.asList(vHost1, vHost2, vHost3);
assertThat(RoutingUtils.findVirtualHostForHostName(virtualHosts, hostname))
.isEqualTo(vHost1);
}
@Test
public void findVirtualHostForHostName_asteriskMatchAnyDomain() {
String hostname = "a.googleapis.com";
List<Route> routes = Collections.emptyList();
VirtualHost vHost1 = VirtualHost.create("virtualhost01.googleapis.com",
Collections.singletonList("*"), routes,
ImmutableMap.of());
VirtualHost vHost2 = VirtualHost.create("virtualhost02.googleapis.com",
Collections.singletonList("b.googleapis.com"), routes,
ImmutableMap.of());
List<VirtualHost> virtualHosts = Arrays.asList(vHost1, vHost2);
assertThat(RoutingUtils.findVirtualHostForHostName(virtualHosts, hostname))
.isEqualTo(vHost1);
}
@Test
public void routeMatching_pathOnly() {
Metadata headers = new Metadata();
ThreadSafeRandom random = mock(ThreadSafeRandom.class);
RouteMatch routeMatch1 =
RouteMatch.create(
PathMatcher.fromPath("/FooService/barMethod", true),
Collections.emptyList(), null);
assertThat(RoutingUtils.matchRoute(routeMatch1, "/FooService/barMethod", headers, random))
.isTrue();
assertThat(RoutingUtils.matchRoute(routeMatch1, "/FooService/bazMethod", headers, random))
.isFalse();
RouteMatch routeMatch2 =
RouteMatch.create(
PathMatcher.fromPrefix("/FooService/", true),
Collections.emptyList(), null);
assertThat(RoutingUtils.matchRoute(routeMatch2, "/FooService/barMethod", headers, random))
.isTrue();
assertThat(RoutingUtils.matchRoute(routeMatch2, "/FooService/bazMethod", headers, random))
.isTrue();
assertThat(RoutingUtils.matchRoute(routeMatch2, "/BarService/bazMethod", headers, random))
.isFalse();
RouteMatch routeMatch3 =
RouteMatch.create(
PathMatcher.fromRegEx(Pattern.compile(".*Foo.*")),
Collections.emptyList(), null);
assertThat(RoutingUtils.matchRoute(routeMatch3, "/FooService/barMethod", headers, random))
.isTrue();
}
@Test
public void routeMatching_pathOnly_caseInsensitive() {
Metadata headers = new Metadata();
ThreadSafeRandom random = mock(ThreadSafeRandom.class);
RouteMatch routeMatch1 =
RouteMatch.create(
PathMatcher.fromPath("/FooService/barMethod", false),
Collections.emptyList(), null);
assertThat(RoutingUtils.matchRoute(routeMatch1, "/fooservice/barmethod", headers, random))
.isTrue();
RouteMatch routeMatch2 =
RouteMatch.create(
PathMatcher.fromPrefix("/FooService", false),
Collections.emptyList(), null);
assertThat(RoutingUtils.matchRoute(routeMatch2, "/fooservice/barmethod", headers, random))
.isTrue();
}
@Test
public void routeMatching_withHeaders() {
Metadata headers = new Metadata();
headers.put(Metadata.Key.of("authority", Metadata.ASCII_STRING_MARSHALLER),
"foo.googleapis.com");
headers.put(Metadata.Key.of("grpc-encoding", Metadata.ASCII_STRING_MARSHALLER), "gzip");
headers.put(Metadata.Key.of("user-agent", Metadata.ASCII_STRING_MARSHALLER), "gRPC-Java");
headers.put(Metadata.Key.of("content-length", Metadata.ASCII_STRING_MARSHALLER), "1000");
headers.put(Metadata.Key.of("custom-key", Metadata.ASCII_STRING_MARSHALLER), "custom-value1");
headers.put(Metadata.Key.of("custom-key", Metadata.ASCII_STRING_MARSHALLER), "custom-value2");
ThreadSafeRandom random = mock(ThreadSafeRandom.class);
PathMatcher pathMatcher = PathMatcher.fromPath("/FooService/barMethod", true);
RouteMatch routeMatch1 = RouteMatch.create(
pathMatcher,
Arrays.asList(
HeaderMatcher.forExactValue("grpc-encoding", "gzip", false),
HeaderMatcher.forSafeRegEx("authority", Pattern.compile(".*googleapis.*"), false),
HeaderMatcher.forRange(
"content-length", HeaderMatcher.Range.create(100, 10000), false),
HeaderMatcher.forPresent("user-agent", true, false),
HeaderMatcher.forPrefix("custom-key", "custom-", false),
HeaderMatcher.forSuffix("custom-key", "value2", false)),
null);
assertThat(RoutingUtils.matchRoute(routeMatch1, "/FooService/barMethod", headers, random))
.isTrue();
RouteMatch routeMatch2 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forSafeRegEx("authority", Pattern.compile(".*googleapis.*"), true)),
null);
assertThat(RoutingUtils.matchRoute(routeMatch2, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch3 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("user-agent", "gRPC-Go", false)), null);
assertThat(RoutingUtils.matchRoute(routeMatch3, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch4 = RouteMatch.create(
pathMatcher,
Collections.singletonList(HeaderMatcher.forPresent("user-agent", false, false)),
null);
assertThat(RoutingUtils.matchRoute(routeMatch4, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch5 = RouteMatch.create(
pathMatcher,
Collections.singletonList(HeaderMatcher.forPresent("user-agent", false, true)), // inverted
null);
assertThat(RoutingUtils.matchRoute(routeMatch5, "/FooService/barMethod", headers, random))
.isTrue();
RouteMatch routeMatch6 = RouteMatch.create(
pathMatcher,
Collections.singletonList(HeaderMatcher.forPresent("user-agent", true, true)),
null);
assertThat(RoutingUtils.matchRoute(routeMatch6, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch7 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("custom-key", "custom-value1,custom-value2", false)),
null);
assertThat(RoutingUtils.matchRoute(routeMatch7, "/FooService/barMethod", headers, random))
.isTrue();
RouteMatch routeMatch8 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("content-type", "application/grpc", false)),
null);
assertThat(RoutingUtils.matchRoute(
routeMatch8, "/FooService/barMethod", new Metadata(), random)).isTrue();
RouteMatch routeMatch9 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("custom-key!", "custom-value1,custom-value2", false)),
null);
assertThat(RoutingUtils.matchRoute(routeMatch9, "/FooService/barMethod", headers, random))
.isFalse();
}
}

View File

@ -90,7 +90,6 @@ import io.grpc.xds.VirtualHost.Route.RouteMatch.PathMatcher;
import io.grpc.xds.XdsListenerResource.LdsUpdate; import io.grpc.xds.XdsListenerResource.LdsUpdate;
import io.grpc.xds.XdsNameResolverProvider.XdsClientPoolFactory; import io.grpc.xds.XdsNameResolverProvider.XdsClientPoolFactory;
import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate; import io.grpc.xds.XdsRouteConfigureResource.RdsUpdate;
import io.grpc.xds.internal.Matchers.HeaderMatcher;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -1450,57 +1449,6 @@ public class XdsNameResolverTest {
assertThat(XdsNameResolver.matchHostName("foo-bar", pattern)).isTrue(); assertThat(XdsNameResolver.matchHostName("foo-bar", pattern)).isTrue();
} }
@Test
public void findVirtualHostForHostName_exactMatchFirst() {
String hostname = "a.googleapis.com";
List<Route> routes = Collections.emptyList();
VirtualHost vHost1 = VirtualHost.create("virtualhost01.googleapis.com",
Arrays.asList("a.googleapis.com", "b.googleapis.com"), routes,
ImmutableMap.of());
VirtualHost vHost2 = VirtualHost.create("virtualhost02.googleapis.com",
Collections.singletonList("*.googleapis.com"), routes,
ImmutableMap.of());
VirtualHost vHost3 = VirtualHost.create("virtualhost03.googleapis.com",
Collections.singletonList("*"), routes,
ImmutableMap.of());
List<VirtualHost> virtualHosts = Arrays.asList(vHost1, vHost2, vHost3);
assertThat(XdsNameResolver.findVirtualHostForHostName(virtualHosts, hostname))
.isEqualTo(vHost1);
}
@Test
public void findVirtualHostForHostName_preferSuffixDomainOverPrefixDomain() {
String hostname = "a.googleapis.com";
List<Route> routes = Collections.emptyList();
VirtualHost vHost1 = VirtualHost.create("virtualhost01.googleapis.com",
Arrays.asList("*.googleapis.com", "b.googleapis.com"), routes,
ImmutableMap.of());
VirtualHost vHost2 = VirtualHost.create("virtualhost02.googleapis.com",
Collections.singletonList("a.googleapis.*"), routes,
ImmutableMap.of());
VirtualHost vHost3 = VirtualHost.create("virtualhost03.googleapis.com",
Collections.singletonList("*"), routes,
ImmutableMap.of());
List<VirtualHost> virtualHosts = Arrays.asList(vHost1, vHost2, vHost3);
assertThat(XdsNameResolver.findVirtualHostForHostName(virtualHosts, hostname))
.isEqualTo(vHost1);
}
@Test
public void findVirtualHostForHostName_asteriskMatchAnyDomain() {
String hostname = "a.googleapis.com";
List<Route> routes = Collections.emptyList();
VirtualHost vHost1 = VirtualHost.create("virtualhost01.googleapis.com",
Collections.singletonList("*"), routes,
ImmutableMap.of());
VirtualHost vHost2 = VirtualHost.create("virtualhost02.googleapis.com",
Collections.singletonList("b.googleapis.com"), routes,
ImmutableMap.of());
List<VirtualHost> virtualHosts = Arrays.asList(vHost1, vHost2);
assertThat(XdsNameResolver.findVirtualHostForHostName(virtualHosts, hostname))
.isEqualTo(vHost1);
}
@Test @Test
public void resolved_faultAbortInLdsUpdate() { public void resolved_faultAbortInLdsUpdate() {
resolver.start(mockListener); resolver.start(mockListener);
@ -1901,147 +1849,6 @@ public class XdsNameResolverTest {
verifyRpcFailed(listener, expectedStatus); verifyRpcFailed(listener, expectedStatus);
} }
@Test
public void routeMatching_pathOnly() {
Metadata headers = new Metadata();
ThreadSafeRandom random = mock(ThreadSafeRandom.class);
RouteMatch routeMatch1 =
RouteMatch.create(
PathMatcher.fromPath("/FooService/barMethod", true),
Collections.emptyList(), null);
assertThat(XdsNameResolver.matchRoute(routeMatch1, "/FooService/barMethod", headers, random))
.isTrue();
assertThat(XdsNameResolver.matchRoute(routeMatch1, "/FooService/bazMethod", headers, random))
.isFalse();
RouteMatch routeMatch2 =
RouteMatch.create(
PathMatcher.fromPrefix("/FooService/", true),
Collections.emptyList(), null);
assertThat(XdsNameResolver.matchRoute(routeMatch2, "/FooService/barMethod", headers, random))
.isTrue();
assertThat(XdsNameResolver.matchRoute(routeMatch2, "/FooService/bazMethod", headers, random))
.isTrue();
assertThat(XdsNameResolver.matchRoute(routeMatch2, "/BarService/bazMethod", headers, random))
.isFalse();
RouteMatch routeMatch3 =
RouteMatch.create(
PathMatcher.fromRegEx(Pattern.compile(".*Foo.*")),
Collections.emptyList(), null);
assertThat(XdsNameResolver.matchRoute(routeMatch3, "/FooService/barMethod", headers, random))
.isTrue();
}
@Test
public void routeMatching_pathOnly_caseInsensitive() {
Metadata headers = new Metadata();
ThreadSafeRandom random = mock(ThreadSafeRandom.class);
RouteMatch routeMatch1 =
RouteMatch.create(
PathMatcher.fromPath("/FooService/barMethod", false),
Collections.emptyList(), null);
assertThat(XdsNameResolver.matchRoute(routeMatch1, "/fooservice/barmethod", headers, random))
.isTrue();
RouteMatch routeMatch2 =
RouteMatch.create(
PathMatcher.fromPrefix("/FooService", false),
Collections.emptyList(), null);
assertThat(XdsNameResolver.matchRoute(routeMatch2, "/fooservice/barmethod", headers, random))
.isTrue();
}
@Test
public void routeMatching_withHeaders() {
Metadata headers = new Metadata();
headers.put(Metadata.Key.of("authority", Metadata.ASCII_STRING_MARSHALLER),
"foo.googleapis.com");
headers.put(Metadata.Key.of("grpc-encoding", Metadata.ASCII_STRING_MARSHALLER), "gzip");
headers.put(Metadata.Key.of("user-agent", Metadata.ASCII_STRING_MARSHALLER), "gRPC-Java");
headers.put(Metadata.Key.of("content-length", Metadata.ASCII_STRING_MARSHALLER), "1000");
headers.put(Metadata.Key.of("custom-key", Metadata.ASCII_STRING_MARSHALLER), "custom-value1");
headers.put(Metadata.Key.of("custom-key", Metadata.ASCII_STRING_MARSHALLER), "custom-value2");
ThreadSafeRandom random = mock(ThreadSafeRandom.class);
PathMatcher pathMatcher = PathMatcher.fromPath("/FooService/barMethod", true);
RouteMatch routeMatch1 = RouteMatch.create(
pathMatcher,
Arrays.asList(
HeaderMatcher.forExactValue("grpc-encoding", "gzip", false),
HeaderMatcher.forSafeRegEx("authority", Pattern.compile(".*googleapis.*"), false),
HeaderMatcher.forRange(
"content-length", HeaderMatcher.Range.create(100, 10000), false),
HeaderMatcher.forPresent("user-agent", true, false),
HeaderMatcher.forPrefix("custom-key", "custom-", false),
HeaderMatcher.forSuffix("custom-key", "value2", false)),
null);
assertThat(XdsNameResolver.matchRoute(routeMatch1, "/FooService/barMethod", headers, random))
.isTrue();
RouteMatch routeMatch2 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forSafeRegEx("authority", Pattern.compile(".*googleapis.*"), true)),
null);
assertThat(XdsNameResolver.matchRoute(routeMatch2, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch3 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("user-agent", "gRPC-Go", false)), null);
assertThat(XdsNameResolver.matchRoute(routeMatch3, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch4 = RouteMatch.create(
pathMatcher,
Collections.singletonList(HeaderMatcher.forPresent("user-agent", false, false)),
null);
assertThat(XdsNameResolver.matchRoute(routeMatch4, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch5 = RouteMatch.create(
pathMatcher,
Collections.singletonList(HeaderMatcher.forPresent("user-agent", false, true)), // inverted
null);
assertThat(XdsNameResolver.matchRoute(routeMatch5, "/FooService/barMethod", headers, random))
.isTrue();
RouteMatch routeMatch6 = RouteMatch.create(
pathMatcher,
Collections.singletonList(HeaderMatcher.forPresent("user-agent", true, true)),
null);
assertThat(XdsNameResolver.matchRoute(routeMatch6, "/FooService/barMethod", headers, random))
.isFalse();
RouteMatch routeMatch7 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("custom-key", "custom-value1,custom-value2", false)),
null);
assertThat(XdsNameResolver.matchRoute(routeMatch7, "/FooService/barMethod", headers, random))
.isTrue();
RouteMatch routeMatch8 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("content-type", "application/grpc", false)),
null);
assertThat(XdsNameResolver.matchRoute(
routeMatch8, "/FooService/barMethod", new Metadata(), random)).isTrue();
RouteMatch routeMatch9 = RouteMatch.create(
pathMatcher,
Collections.singletonList(
HeaderMatcher.forExactValue("custom-key!", "custom-value1,custom-value2", false)),
null);
assertThat(XdsNameResolver.matchRoute(routeMatch9, "/FooService/barMethod", headers, random))
.isFalse();
}
private final class FakeXdsClientPoolFactory implements XdsClientPoolFactory { private final class FakeXdsClientPoolFactory implements XdsClientPoolFactory {
@Override @Override
public void setBootstrapOverride(Map<String, ?> bootstrap) {} public void setBootstrapOverride(Map<String, ?> bootstrap) {}