mirror of https://github.com/grpc/grpc-java.git
xds: fix matchRoute in RoutingUtils and clean up implementations in XdsNameResolver. (#10095)
This commit is contained in:
parent
9d1e089c27
commit
c5b825aa7d
|
@ -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)) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {}
|
||||||
|
|
Loading…
Reference in New Issue