From 245f901df46634cca603a0199e348e5cb0f61846 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 9 Oct 2020 17:40:33 -0700 Subject: [PATCH 1/3] Add case sensitivity option to path matcher. --- xds/src/main/java/io/grpc/xds/RouteMatch.java | 62 ++++++++----------- .../test/java/io/grpc/xds/RouteMatchTest.java | 40 +++++++----- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/RouteMatch.java b/xds/src/main/java/io/grpc/xds/RouteMatch.java index dc5ff221de1..127ffb4a7c6 100644 --- a/xds/src/main/java/io/grpc/xds/RouteMatch.java +++ b/xds/src/main/java/io/grpc/xds/RouteMatch.java @@ -45,10 +45,8 @@ final class RouteMatch { this.headerMatchers = headerMatchers; } - @VisibleForTesting - RouteMatch(@Nullable String pathPrefixMatch, @Nullable String pathExactMatch) { - this( - new PathMatcher(pathExactMatch, pathPrefixMatch, null), + static RouteMatch withPathExactOnly(String pathExact) { + return new RouteMatch(PathMatcher.fromPath(pathExact, true), Collections.emptyList(), null); } @@ -82,19 +80,6 @@ boolean matches(String path, Map> headers) { return fractionMatch == null || fractionMatch.matches(); } - PathMatcher getPathMatch() { - return pathMatch; - } - - List getHeaderMatchers() { - return Collections.unmodifiableList(headerMatchers); - } - - @Nullable - FractionMatcher getFractionMatch() { - return fractionMatch; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -132,35 +117,37 @@ static final class PathMatcher { private final String prefix; @Nullable private final Pattern regEx; + private final boolean caseSensitive; - PathMatcher(@Nullable String path, @Nullable String prefix, @Nullable Pattern regEx) { + private PathMatcher(@Nullable String path, @Nullable String prefix, @Nullable Pattern regEx, + boolean caseSensitive) { this.path = path; this.prefix = prefix; this.regEx = regEx; + this.caseSensitive = caseSensitive; } - private boolean matches(String fullMethodName) { - if (path != null) { - return path.equals(fullMethodName); - } else if (prefix != null) { - return fullMethodName.startsWith(prefix); - } - return regEx.matches(fullMethodName); + static PathMatcher fromPath(String path, boolean caseSensitive) { + return new PathMatcher(path, null, null, caseSensitive); } - @Nullable - String getPath() { - return path; + static PathMatcher fromPrefix(String prefix, boolean caseSensitive) { + return new PathMatcher(null, prefix, null, caseSensitive); } - @Nullable - String getPrefix() { - return prefix; + static PathMatcher fromRegEx(Pattern regEx) { + return new PathMatcher(null, null, regEx, false /* doesn't matter */); } - @Nullable - Pattern getRegEx() { - return regEx; + boolean matches(String fullMethodName) { + if (path != null) { + return caseSensitive ? path.equals(fullMethodName) : path.equalsIgnoreCase(fullMethodName); + } else if (prefix != null) { + return caseSensitive + ? fullMethodName.startsWith(prefix) + : fullMethodName.toLowerCase().startsWith(prefix.toLowerCase()); + } + return regEx.matches(fullMethodName); } @Override @@ -174,6 +161,7 @@ public boolean equals(Object o) { PathMatcher that = (PathMatcher) o; return Objects.equals(path, that.path) && Objects.equals(prefix, that.prefix) + && Objects.equals(caseSensitive, that.caseSensitive) && Objects.equals( regEx == null ? null : regEx.pattern(), that.regEx == null ? null : that.regEx.pattern()); @@ -181,7 +169,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(path, prefix, regEx == null ? null : regEx.pattern()); + return Objects.hash(path, prefix, caseSensitive, regEx == null ? null : regEx.pattern()); } @Override @@ -189,10 +177,10 @@ public String toString() { ToStringHelper toStringHelper = MoreObjects.toStringHelper(this); if (path != null) { - toStringHelper.add("path", path); + toStringHelper.add("path", path).add("caseSensitive", caseSensitive); } if (prefix != null) { - toStringHelper.add("prefix", prefix); + toStringHelper.add("prefix", prefix).add("caseSensitive", caseSensitive); } if (regEx != null) { toStringHelper.add("regEx", regEx.pattern()); diff --git a/xds/src/test/java/io/grpc/xds/RouteMatchTest.java b/xds/src/test/java/io/grpc/xds/RouteMatchTest.java index 463473d7fc6..10543547014 100644 --- a/xds/src/test/java/io/grpc/xds/RouteMatchTest.java +++ b/xds/src/test/java/io/grpc/xds/RouteMatchTest.java @@ -51,14 +51,14 @@ public void setUp() { public void routeMatching_pathOnly() { RouteMatch routeMatch1 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + PathMatcher.fromPath("/FooService/barMethod", true), Collections.emptyList(), null); assertThat(routeMatch1.matches("/FooService/barMethod", headers)).isTrue(); assertThat(routeMatch1.matches("/FooService/bazMethod", headers)).isFalse(); RouteMatch routeMatch2 = new RouteMatch( - new PathMatcher(null, "/FooService/", null), + PathMatcher.fromPrefix("/FooService/", true), Collections.emptyList(), null); assertThat(routeMatch2.matches("/FooService/barMethod", headers)).isTrue(); assertThat(routeMatch2.matches("/FooService/bazMethod", headers)).isTrue(); @@ -66,15 +66,25 @@ public void routeMatching_pathOnly() { RouteMatch routeMatch3 = new RouteMatch( - new PathMatcher(null, null, Pattern.compile(".*Foo.*")), + PathMatcher.fromRegEx(Pattern.compile(".*Foo.*")), Collections.emptyList(), null); assertThat(routeMatch3.matches("/FooService/barMethod", headers)).isTrue(); } + @Test + public void pathMatching_caseInsensitive() { + PathMatcher pathMatcher1 = PathMatcher.fromPath("/FooService/barMethod", false); + assertThat(pathMatcher1.matches("/fooservice/barmethod")).isTrue(); + + PathMatcher pathMatcher2 = PathMatcher.fromPrefix("/FooService", false); + assertThat(pathMatcher2.matches("/fooservice/barmethod")).isTrue(); + } + @Test public void routeMatching_withHeaders() { + PathMatcher pathMatcher = PathMatcher.fromPath("/FooService/barMethod", true); RouteMatch routeMatch1 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Arrays.asList( new HeaderMatcher( "grpc-encoding", "gzip", null, null, null, null, null, false), @@ -90,7 +100,7 @@ public void routeMatching_withHeaders() { assertThat(routeMatch1.matches("/FooService/barMethod", headers)).isTrue(); RouteMatch routeMatch2 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.singletonList( new HeaderMatcher( "authority", null, Pattern.compile(".*googleapis.*"), null, null, null, @@ -99,7 +109,7 @@ public void routeMatching_withHeaders() { assertThat(routeMatch2.matches("/FooService/barMethod", headers)).isFalse(); RouteMatch routeMatch3 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.singletonList( new HeaderMatcher( "user-agent", "gRPC-Go", null, null, null, null, @@ -108,7 +118,7 @@ public void routeMatching_withHeaders() { assertThat(routeMatch3.matches("/FooService/barMethod", headers)).isFalse(); RouteMatch routeMatch4 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.singletonList( new HeaderMatcher( "user-agent", null, null, null, false, null, @@ -117,7 +127,7 @@ public void routeMatching_withHeaders() { assertThat(routeMatch4.matches("/FooService/barMethod", headers)).isFalse(); RouteMatch routeMatch5 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.singletonList( new HeaderMatcher( "user-agent", null, null, null, false, null, @@ -126,7 +136,7 @@ public void routeMatching_withHeaders() { assertThat(routeMatch5.matches("/FooService/barMethod", headers)).isTrue(); RouteMatch routeMatch6 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.singletonList( new HeaderMatcher( "user-agent", null, null, null, true, null, @@ -135,7 +145,7 @@ public void routeMatching_withHeaders() { assertThat(routeMatch6.matches("/FooService/barMethod", headers)).isFalse(); RouteMatch routeMatch7 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.singletonList( new HeaderMatcher( "custom-key", "custom-value1,custom-value2", null, null, null, null, @@ -146,16 +156,17 @@ public void routeMatching_withHeaders() { @Test public void routeMatching_withRuntimeFraction() { + PathMatcher pathMatcher = PathMatcher.fromPath("/FooService/barMethod", true); RouteMatch routeMatch1 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.emptyList(), new FractionMatcher(100, 1000, new FakeRandom(50))); assertThat(routeMatch1.matches("/FooService/barMethod", headers)).isTrue(); RouteMatch routeMatch2 = new RouteMatch( - new PathMatcher("/FooService/barMethod", null, null), + pathMatcher, Collections.emptyList(), new FractionMatcher(100, 1000, new FakeRandom(100))); assertThat(routeMatch2.matches("/FooService/barMethod", headers)).isFalse(); @@ -163,11 +174,12 @@ public void routeMatching_withRuntimeFraction() { @Test public void headerMatching_specialCaseGrpcHeaders() { + PathMatcher pathMatcher = PathMatcher.fromPath("/FooService/barMethod", true); Map> headers = new HashMap<>(); headers.put("grpc-previous-rpc-attempts", Collections.singletonList("0")); RouteMatch routeMatch1 = - new RouteMatch(new PathMatcher("/FooService/barMethod", null, null), + new RouteMatch(pathMatcher, Arrays.asList( new HeaderMatcher( "grpc-previous-rpc-attempts", "0", null, null, null, null, @@ -176,7 +188,7 @@ public void headerMatching_specialCaseGrpcHeaders() { assertThat(routeMatch1.matches("/FooService/barMethod", headers)).isFalse(); RouteMatch routeMatch2 = - new RouteMatch(new PathMatcher("/FooService/barMethod", null, null), + new RouteMatch(pathMatcher, Arrays.asList( new HeaderMatcher( "content-type", "application/grpc", null, null, null, null, From 3a6479935f90930a383e43f3c142ec1cec01ce86 Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 9 Oct 2020 17:41:31 -0700 Subject: [PATCH 2/3] Enhance PathMatcher cretion. --- .../main/java/io/grpc/xds/EnvoyProtoData.java | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java b/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java index c9ce9bfc194..112dc22963c 100644 --- a/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java +++ b/xds/src/main/java/io/grpc/xds/EnvoyProtoData.java @@ -918,16 +918,6 @@ RouteAction getRouteAction() { return routeAction; } - // TODO(chengyuanzhang): delete and do not use after routing feature is always ON. - boolean isDefaultRoute() { - // For backward compatibility, all the other matchers are ignored. - String prefix = routeMatch.getPathMatch().getPrefix(); - if (prefix != null) { - return prefix.isEmpty() || prefix.equals("/"); - } - return false; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -992,17 +982,12 @@ static StructOrError fromEnvoyProtoRoute( } @VisibleForTesting - @SuppressWarnings("deprecation") @Nullable static StructOrError convertEnvoyProtoRouteMatch( io.envoyproxy.envoy.config.route.v3.RouteMatch proto) { if (proto.getQueryParametersCount() != 0) { return null; } - if (proto.hasCaseSensitive() && !proto.getCaseSensitive().getValue()) { - return StructOrError.fromError("Unsupported match option: case insensitive"); - } - StructOrError pathMatch = convertEnvoyProtoPathMatcher(proto); if (pathMatch.getErrorDetail() != null) { return StructOrError.fromError(pathMatch.getErrorDetail()); @@ -1032,32 +1017,28 @@ static StructOrError convertEnvoyProtoRouteMatch( pathMatch.getStruct(), Collections.unmodifiableList(headerMatchers), fractionMatch)); } - @SuppressWarnings("deprecation") private static StructOrError convertEnvoyProtoPathMatcher( io.envoyproxy.envoy.config.route.v3.RouteMatch proto) { - String path = null; - String prefix = null; - Pattern safeRegEx = null; + boolean caseSensitive = proto.getCaseSensitive().getValue(); switch (proto.getPathSpecifierCase()) { case PREFIX: - prefix = proto.getPrefix(); - break; + return StructOrError.fromStruct( + PathMatcher.fromPrefix(proto.getPrefix(), caseSensitive)); case PATH: - path = proto.getPath(); - break; + return StructOrError.fromStruct(PathMatcher.fromPath(proto.getPath(), caseSensitive)); case SAFE_REGEX: String rawPattern = proto.getSafeRegex().getRegex(); + Pattern safeRegEx; try { safeRegEx = Pattern.compile(rawPattern); } catch (PatternSyntaxException e) { return StructOrError.fromError("Malformed safe regex pattern: " + e.getMessage()); } - break; + return StructOrError.fromStruct(PathMatcher.fromRegEx(safeRegEx)); case PATHSPECIFIER_NOT_SET: default: return StructOrError.fromError("Unknown path match type"); } - return StructOrError.fromStruct(new PathMatcher(path, prefix, safeRegEx)); } private static StructOrError convertEnvoyProtoFraction( From d85cbb8a78d2e9111c063aa74be71d25d697eaae Mon Sep 17 00:00:00 2001 From: Chengyuan Zhang Date: Fri, 9 Oct 2020 17:41:40 -0700 Subject: [PATCH 3/3] Fix tests. --- .../java/io/grpc/xds/EnvoyProtoDataTest.java | 67 ++++++------------- .../java/io/grpc/xds/XdsNameResolverTest.java | 32 ++++----- 2 files changed, 35 insertions(+), 64 deletions(-) diff --git a/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java b/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java index 5ffabdf3642..0758a697e87 100644 --- a/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java +++ b/xds/src/test/java/io/grpc/xds/EnvoyProtoDataTest.java @@ -50,7 +50,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -211,7 +210,7 @@ public void convertRoute() { assertThat(struct1.getStruct()) .isEqualTo( new Route( - new RouteMatch(new PathMatcher("/service/method", null, null), + new RouteMatch(PathMatcher.fromPath("/service/method", false), Collections.emptyList(), null), new RouteAction(TimeUnit.SECONDS.toNanos(15L), "cluster-foo", null))); @@ -259,38 +258,6 @@ public void convertRoute_skipWithUnsupportedAction() { assertThat(Route.fromEnvoyProtoRoute(proto)).isNull(); } - @Test - public void isDefaultRoute() { - StructOrError struct1 = Route.fromEnvoyProtoRoute(buildSimpleRouteProto("", null)); - StructOrError struct2 = Route.fromEnvoyProtoRoute(buildSimpleRouteProto("/", null)); - StructOrError struct3 = - Route.fromEnvoyProtoRoute(buildSimpleRouteProto("/service/", null)); - StructOrError struct4 = - Route.fromEnvoyProtoRoute(buildSimpleRouteProto(null, "/service/method")); - - assertThat(struct1.getStruct().isDefaultRoute()).isTrue(); - assertThat(struct2.getStruct().isDefaultRoute()).isTrue(); - assertThat(struct3.getStruct().isDefaultRoute()).isFalse(); - assertThat(struct4.getStruct().isDefaultRoute()).isFalse(); - } - - private static io.envoyproxy.envoy.config.route.v3.Route buildSimpleRouteProto( - @Nullable String pathPrefix, @Nullable String path) { - io.envoyproxy.envoy.config.route.v3.Route.Builder routeBuilder = - io.envoyproxy.envoy.config.route.v3.Route.newBuilder() - .setName("simple-route") - .setRoute(io.envoyproxy.envoy.config.route.v3.RouteAction.newBuilder() - .setCluster("simple-cluster")); - if (pathPrefix != null) { - routeBuilder.setMatch(io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder() - .setPrefix(pathPrefix)); - } else if (path != null) { - routeBuilder.setMatch(io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder() - .setPath(path)); - } - return routeBuilder.build(); - } - @Test public void convertRouteMatch_pathMatching() { // path_specifier = prefix @@ -300,7 +267,13 @@ public void convertRouteMatch_pathMatching() { assertThat(struct1.getErrorDetail()).isNull(); assertThat(struct1.getStruct()).isEqualTo( new RouteMatch( - new PathMatcher(null, "/", null), Collections.emptyList(), null)); + PathMatcher.fromPrefix("/", false), Collections.emptyList(), null)); + + proto1 = proto1.toBuilder().setCaseSensitive(BoolValue.newBuilder().setValue(true)).build(); + struct1 = Route.convertEnvoyProtoRouteMatch(proto1); + assertThat(struct1.getStruct()).isEqualTo( + new RouteMatch( + PathMatcher.fromPrefix("/", true), Collections.emptyList(), null)); // path_specifier = path io.envoyproxy.envoy.config.route.v3.RouteMatch proto2 = @@ -311,7 +284,14 @@ public void convertRouteMatch_pathMatching() { assertThat(struct2.getErrorDetail()).isNull(); assertThat(struct2.getStruct()).isEqualTo( new RouteMatch( - new PathMatcher("/service/method", null, null), + PathMatcher.fromPath("/service/method", false), + Collections.emptyList(), null)); + + proto2 = proto2.toBuilder().setCaseSensitive(BoolValue.newBuilder().setValue(true)).build(); + struct2 = Route.convertEnvoyProtoRouteMatch(proto2); + assertThat(struct2.getStruct()).isEqualTo( + new RouteMatch( + PathMatcher.fromPath("/service/method", true), Collections.emptyList(), null)); // path_specifier = safe_regex @@ -323,18 +303,9 @@ public void convertRouteMatch_pathMatching() { assertThat(struct4.getErrorDetail()).isNull(); assertThat(struct4.getStruct()).isEqualTo( new RouteMatch( - new PathMatcher(null, null, Pattern.compile(".")), + PathMatcher.fromRegEx(Pattern.compile(".")), Collections.emptyList(), null)); - // case_sensitive = false - io.envoyproxy.envoy.config.route.v3.RouteMatch proto5 = - io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder() - .setCaseSensitive(BoolValue.newBuilder().setValue(false)) - .build(); - StructOrError struct5 = Route.convertEnvoyProtoRouteMatch(proto5); - assertThat(struct5.getErrorDetail()).isNotNull(); - assertThat(struct5.getStruct()).isNull(); - // query_parameters is set io.envoyproxy.envoy.config.route.v3.RouteMatch proto6 = io.envoyproxy.envoy.config.route.v3.RouteMatch.newBuilder() @@ -370,7 +341,7 @@ public void convertRouteMatch_withHeaderMatching() { assertThat(struct.getStruct()) .isEqualTo( new RouteMatch( - new PathMatcher(null, "", null), + PathMatcher.fromPrefix("", false), Arrays.asList( new HeaderMatcher(":scheme", null, null, null, null, "http", null, false), new HeaderMatcher(":method", "PUT", null, null, null, null, null, false)), @@ -394,7 +365,7 @@ public void convertRouteMatch_withRuntimeFraction() { assertThat(struct.getStruct()) .isEqualTo( new RouteMatch( - new PathMatcher(null, "", null), Collections.emptyList(), + PathMatcher.fromPrefix( "", false), Collections.emptyList(), new FractionMatcher(30, 100))); } diff --git a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java index ad5e99d961f..1a2fbede6d4 100644 --- a/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java +++ b/xds/src/test/java/io/grpc/xds/XdsNameResolverTest.java @@ -279,9 +279,9 @@ public void resolving_matchingVirtualHostNotFoundInRdsResource() { } private List buildUnmatchedVirtualHosts() { - Route route1 = new Route(new RouteMatch(null, call2.getFullMethodNameForPath()), + Route route1 = new Route(RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)); - Route route2 = new Route(new RouteMatch(null, call1.getFullMethodNameForPath()), + Route route2 = new Route(RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster1, null)); return Arrays.asList( new VirtualHost("virtualhost-foo", Collections.singletonList("hello.googleapis.com"), @@ -323,10 +323,10 @@ public void resolved_resourceUpdateAfterCallStarted() { AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(20L), "another-cluster", null)), new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); verify(mockListener).onResult(resolutionResultCaptor.capture()); ResolutionResult result = resolutionResultCaptor.getValue(); @@ -358,10 +358,10 @@ public void resolved_resourceUpdatedBeforeCallStarted() { AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(20L), "another-cluster", null)), new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); // Two consecutive service config updates: one for removing clcuster1, // one for adding "another=cluster". @@ -389,10 +389,10 @@ public void resolved_raceBetweenCallAndRepeatedResourceUpdate() { AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(20L), "another-cluster", null)), new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); verify(mockListener).onResult(resolutionResultCaptor.capture()); @@ -405,10 +405,10 @@ public void resolved_raceBetweenCallAndRepeatedResourceUpdate() { AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), "another-cluster", null)), new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); verifyNoMoreInteractions(mockListener); // no cluster added/deleted assertCallSelectResult(call1, configSelector, "another-cluster", 15.0); @@ -423,16 +423,16 @@ public void resolved_raceBetweenClusterReleasedAndResourceUpdateAddBackAgain() { AUTHORITY, Collections.singletonList( new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); xdsClient.deliverRoutesViaLds( AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster1, null)), new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); result.getCommittedCallback().run(); verifyNoMoreInteractions(mockListener); @@ -448,7 +448,7 @@ public void resolved_simpleCallSucceeds_routeToWeightedCluster() { AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction( TimeUnit.SECONDS.toNanos(20L), null, Arrays.asList( @@ -504,10 +504,10 @@ private InternalConfigSelector resolveToClusters() { AUTHORITY, Arrays.asList( new Route( - new RouteMatch(null, call1.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call1.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster1, null)), new Route( - new RouteMatch(null, call2.getFullMethodNameForPath()), + RouteMatch.withPathExactOnly(call2.getFullMethodNameForPath()), new RouteAction(TimeUnit.SECONDS.toNanos(15L), cluster2, null)))); verify(mockListener).onResult(resolutionResultCaptor.capture()); ResolutionResult result = resolutionResultCaptor.getValue();