From 72b24f6f7d1263c0f6546da723fef5fc3b96861b Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Wed, 15 Apr 2026 11:57:03 +0530 Subject: [PATCH 1/7] direct-path-interconnect --- api/src/main/java/io/grpc/Uri.java | 2 +- .../GoogleCloudToProdNameResolver.java | 138 ++++++++--- .../GoogleCloudToProdNameResolverTest.java | 219 ++++++++++++++++++ 3 files changed, 324 insertions(+), 35 deletions(-) diff --git a/api/src/main/java/io/grpc/Uri.java b/api/src/main/java/io/grpc/Uri.java index 9f8a5a87848..c45abef5e7d 100644 --- a/api/src/main/java/io/grpc/Uri.java +++ b/api/src/main/java/io/grpc/Uri.java @@ -793,7 +793,7 @@ public Builder setQuery(@Nullable String query) { } @CanIgnoreReturnValue - Builder setRawQuery(String query) { + public Builder setRawQuery(String query) { checkPercentEncodedArg(query, "query", queryChars); this.query = query; return this; diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 427c0658531..59a898783f8 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.CharStreams; @@ -76,23 +77,43 @@ final class GoogleCloudToProdNameResolver extends NameResolver { private static final String serverUriOverride = System.getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"); - @GuardedBy("GoogleCloudToProdNameResolver.class") + private static final Object BOOTSTRAP_LOCK = new Object(); + private static final Object FORCE_XDS_BOOTSTRAP_LOCK = new Object(); + + @GuardedBy("BOOTSTRAP_LOCK") private static BootstrapInfo bootstrapInfo; + @GuardedBy("FORCE_XDS_BOOTSTRAP_LOCK") + private static BootstrapInfo forceXdsBootstrapInfo; private static HttpConnectionProvider httpConnectionProvider = HttpConnectionFactory.INSTANCE; private static int c2pId = new Random().nextInt(); - private static synchronized BootstrapInfo getBootstrapInfo() + private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) throws XdsInitializationException, IOException { - if (bootstrapInfo != null) { - return bootstrapInfo; - } - BootstrapInfo bootstrapInfoTmp = - InternalGrpcBootstrapperImpl.parseBootstrap(generateBootstrap()); - // Avoid setting global when testing - if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { - bootstrapInfo = bootstrapInfoTmp; + if (isForcedXds) { + synchronized (FORCE_XDS_BOOTSTRAP_LOCK) { + if (forceXdsBootstrapInfo != null) { + return forceXdsBootstrapInfo; + } + BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( + generateBootstrap("", true, true)); + if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { + forceXdsBootstrapInfo = newInfo; + } + return newInfo; + } + } else { + synchronized (BOOTSTRAP_LOCK) { + if (bootstrapInfo != null) { + return bootstrapInfo; + } + BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( + generateBootstrap()); + if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { + bootstrapInfo = newInfo; + } + return newInfo; + } } - return bootstrapInfoTmp; } private final String authority; @@ -102,7 +123,8 @@ private static synchronized BootstrapInfo getBootstrapInfo() private final MetricRecorder metricRecorder; private final NameResolver delegate; private final boolean usingExecutorResource; - private final String schemeOverride = !isOnGcp ? "dns" : "xds"; + private final boolean forceXds; + private final String schemeOverride; private XdsClientResult xdsClientPool; private XdsClient xdsClient; private Executor executor; @@ -121,6 +143,11 @@ private static synchronized BootstrapInfo getBootstrapInfo() GoogleCloudToProdNameResolver(URI targetUri, Args args, Resource executorResource, NameResolver.Factory nameResolverFactory) { this.executorResource = checkNotNull(executorResource, "executorResource"); + String query = targetUri.getRawQuery(); + this.forceXds = checkForceXds(query); + this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns"; + String newQuery = stripForceXds(query); + String targetPath = checkNotNull(checkNotNull(targetUri, "targetUri").getPath(), "targetPath"); Preconditions.checkArgument( targetPath.startsWith("/"), @@ -129,9 +156,31 @@ private static synchronized BootstrapInfo getBootstrapInfo() targetUri); authority = GrpcUtil.checkAuthority(targetPath.substring(1)); syncContext = checkNotNull(args, "args").getSynchronizationContext(); - targetUri = overrideUriScheme(targetUri, schemeOverride); + + String rawAuthority = schemeOverride.equals("xds") + ? C2P_AUTHORITY + : targetUri.getRawAuthority(); + String rawPath = targetUri.getRawPath(); + String rawFragment = targetUri.getRawFragment(); + try { + StringBuilder uriStr = new StringBuilder(); + uriStr.append(schemeOverride).append(":"); + if (rawAuthority != null) { + uriStr.append("//").append(rawAuthority); + } + uriStr.append(rawPath); + if (newQuery != null) { + uriStr.append("?").append(newQuery); + } + if (rawFragment != null) { + uriStr.append("#").append(rawFragment); + } + targetUri = new URI(uriStr.toString()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid URI", e); + } + if (schemeOverride.equals("xds")) { - targetUri = overrideUriAuthority(targetUri, C2P_AUTHORITY); args = args.toBuilder() .setArg(XdsNameResolverProvider.XDS_CLIENT_SUPPLIER, () -> xdsClient) .build(); @@ -155,6 +204,11 @@ private static synchronized BootstrapInfo getBootstrapInfo() Resource executorResource, NameResolver.Factory nameResolverFactory) { this.executorResource = checkNotNull(executorResource, "executorResource"); + String query = targetUri.getRawQuery(); + this.forceXds = checkForceXds(query); + this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns"; + String newQuery = stripForceXds(query); + Preconditions.checkArgument( targetUri.isPathAbsolute(), "the path component of the target (%s) must start with '/'", @@ -167,6 +221,12 @@ private static synchronized BootstrapInfo getBootstrapInfo() authority = GrpcUtil.checkAuthority(pathSegments.get(0)); syncContext = checkNotNull(args, "args").getSynchronizationContext(); Uri.Builder modifiedTargetBuilder = targetUri.toBuilder().setScheme(schemeOverride); + if (newQuery != null) { + modifiedTargetBuilder.setRawQuery(newQuery); + } else { + modifiedTargetBuilder.setQuery(null); + } + if (schemeOverride.equals("xds")) { modifiedTargetBuilder.setRawAuthority(C2P_AUTHORITY); args = @@ -226,7 +286,7 @@ class Resolve implements Runnable { public void run() { BootstrapInfo bootstrapInfo = null; try { - bootstrapInfo = getBootstrapInfo(); + bootstrapInfo = getBootstrapInfo(forceXds); } catch (IOException e) { listener.onError( Status.INTERNAL.withDescription("Unable to get metadata").withCause(e)); @@ -263,16 +323,18 @@ public void run() { static ImmutableMap generateBootstrap() throws IOException { return generateBootstrap( queryZoneMetadata(METADATA_URL_ZONE), - queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6)); + queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6), false); } - private static ImmutableMap generateBootstrap(String zone, boolean supportIpv6) { + static ImmutableMap generateBootstrap( + String zone, boolean supportIpv6, boolean isForcedXds) { ImmutableMap.Builder nodeBuilder = ImmutableMap.builder(); - nodeBuilder.put("id", "C2P-" + (c2pId & Integer.MAX_VALUE)); - if (!zone.isEmpty()) { + String nodeIdPrefix = isOnGcp ? "C2P-" : "C2P-non-gcp-"; + nodeBuilder.put("id", nodeIdPrefix + (c2pId & Integer.MAX_VALUE)); + if (!isForcedXds && !zone.isEmpty()) { nodeBuilder.put("locality", ImmutableMap.of("zone", zone)); } - if (supportIpv6) { + if (isForcedXds || supportIpv6) { nodeBuilder.put("metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); } @@ -373,24 +435,32 @@ static void setC2pId(int c2pId) { GoogleCloudToProdNameResolver.c2pId = c2pId; } - private static URI overrideUriScheme(URI uri, String scheme) { - URI res; - try { - res = new URI(scheme, uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment()); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException("Invalid scheme: " + scheme, ex); + private static boolean checkForceXds(String query) { + if (query == null) { + return false; + } + for (String part : Splitter.on('&').split(query)) { + if (part.equals("force-xds") || part.startsWith("force-xds=")) { + return true; + } } - return res; + return false; } - private static URI overrideUriAuthority(URI uri, String authority) { - URI res; - try { - res = new URI(uri.getScheme(), authority, uri.getPath(), uri.getQuery(), uri.getFragment()); - } catch (URISyntaxException ex) { - throw new IllegalArgumentException("Invalid authority: " + authority, ex); + private static String stripForceXds(String query) { + if (query == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (String part : Splitter.on('&').split(query)) { + if (!part.equals("force-xds") && !part.startsWith("force-xds=")) { + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(part); + } } - return res; + return sb.length() == 0 ? null : sb.toString(); } private enum HttpConnectionFactory implements HttpConnectionProvider { diff --git a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java index d3d3cfc4bff..3c4866ca224 100644 --- a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java +++ b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java @@ -103,6 +103,8 @@ public void close(Executor instance) {} private final NameResolverRegistry nsRegistry = new NameResolverRegistry(); private final Map delegatedResolver = new HashMap<>(); + private final Map delegatedUri = new HashMap<>(); + private final Map delegatedRfcUri = new HashMap<>(); @Mock private NameResolver.Listener2 mockListener; @@ -187,9 +189,178 @@ public void onGcpAndNoProvidedBootstrap_DelegateToXds() { verify(Iterables.getOnlyElement(delegatedResolver.values())).start(mockListener); } + @Test + public void notOnGcpButForceXds_DelegateToXds() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?force-xds"; + resolver = + enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("xds"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getQuery()).isNull(); + } else { + URI delegatedUriValue = delegatedUri.get("xds"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getQuery()).isNull(); + } + } + + @Test + public void notOnGcpButForceXds_KeyValueTrue_DelegateToXds() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?force-xds=true"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("xds"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getQuery()).isNull(); + } else { + URI delegatedUriValue = delegatedUri.get("xds"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getQuery()).isNull(); + } + } + + @Test + public void notOnGcpButForceXds_KeyValueOne_DelegateToXds() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?force-xds=1"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("xds"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getQuery()).isNull(); + } else { + URI delegatedUriValue = delegatedUri.get("xds"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getQuery()).isNull(); + } + } + + @Test + public void notOnGcpButForceXds_WithMultipleParams_DelegateToXds() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?foo=bar&force-xds&baz=qux"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("xds"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getQuery()).isEqualTo("foo=bar&baz=qux"); + } else { + URI delegatedUriValue = delegatedUri.get("xds"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getQuery()).isEqualTo("foo=bar&baz=qux"); + } + } + + @Test + public void notOnGcpButForceXds_WithEncodedAmpersand_DelegateToXds() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?force-xds&foo=bar%26baz"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("xds"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getRawQuery()).isEqualTo("foo=bar%26baz"); + } else { + URI delegatedUriValue = delegatedUri.get("xds"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getRawQuery()).isEqualTo("foo=bar%26baz"); + } + } + + @Test + public void notOnGcpButForceXds_EdgeCaseAmpersands_DelegateToXds() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?&force-xds&"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("xds"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getQuery()).isNull(); + } else { + URI delegatedUriValue = delegatedUri.get("xds"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getQuery()).isNull(); + } + } + + @Test + public void notOnGcpButForceXds_CaseSensitive_DelegateToDns() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?FORCE-XDS"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + assertThat(delegatedResolver.keySet()).containsExactly("dns"); + + if (enableRfc3986UrisParam) { + Uri delegatedRfcUriValue = delegatedRfcUri.get("dns"); + assertThat(delegatedRfcUriValue).isNotNull(); + assertThat(delegatedRfcUriValue.getQuery()).isEqualTo("FORCE-XDS"); + } else { + URI delegatedUriValue = delegatedUri.get("dns"); + assertThat(delegatedUriValue).isNotNull(); + assertThat(delegatedUriValue.getQuery()).isEqualTo("FORCE-XDS"); + } + } + @SuppressWarnings("unchecked") @Test public void generateBootstrap_ipv6() throws IOException { + GoogleCloudToProdNameResolver.isOnGcp = true; Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap(); Map node = (Map) bootstrap.get("node"); assertThat(node).containsExactly( @@ -208,9 +379,44 @@ public void generateBootstrap_ipv6() throws IOException { ImmutableMap.of("xds_servers", ImmutableList.of(server))); } + @SuppressWarnings("unchecked") + @Test + public void generateBootstrap_forceXds() throws IOException { + GoogleCloudToProdNameResolver.isOnGcp = false; + Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap("", true, true); + Map node = (Map) bootstrap.get("node"); + assertThat(node).containsExactly( + "id", "C2P-non-gcp-991614323", + "metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); + + Map server = Iterables.getOnlyElement( + (List>) bootstrap.get("xds_servers")); + assertThat(server).containsExactly( + "server_uri", "directpath-pa.googleapis.com", + "channel_creds", ImmutableList.of(ImmutableMap.of("type", "google_default")), + "server_features", ImmutableList.of("xds_v3", "ignore_resource_deletion")); + Map authorities = (Map) bootstrap.get("authorities"); + assertThat(authorities).containsExactly( + "traffic-director-c2p.xds.googleapis.com", + ImmutableMap.of("xds_servers", ImmutableList.of(server))); + } + + @SuppressWarnings("unchecked") + @Test + public void generateBootstrap_onGcpAndForceXds() throws IOException { + GoogleCloudToProdNameResolver.isOnGcp = true; + Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap("", true, true); + Map node = (Map) bootstrap.get("node"); + assertThat(node).containsExactly( + "id", "C2P-991614323", + "metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); + assertThat(node).doesNotContainKey("locality"); + } + @SuppressWarnings("unchecked") @Test public void generateBootstrap_noIpV6() throws IOException { + GoogleCloudToProdNameResolver.isOnGcp = true; responseToIpV6 = null; Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap(); Map node = (Map) bootstrap.get("node"); @@ -232,6 +438,7 @@ public void generateBootstrap_noIpV6() throws IOException { @SuppressWarnings("unchecked") @Test public void emptyResolverMeetadataValue() throws IOException { + GoogleCloudToProdNameResolver.isOnGcp = true; responseToIpV6 = ""; Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap(); Map node = (Map) bootstrap.get("node"); @@ -270,6 +477,18 @@ private FakeNsProvider(String scheme) { @Override public NameResolver newNameResolver(URI targetUri, Args args) { if (scheme.equals(targetUri.getScheme())) { + delegatedUri.put(scheme, targetUri); + NameResolver resolver = mock(NameResolver.class); + delegatedResolver.put(scheme, resolver); + return resolver; + } + return null; + } + + @Override + public NameResolver newNameResolver(Uri targetUri, Args args) { + if (scheme.equals(targetUri.getScheme())) { + delegatedRfcUri.put(scheme, targetUri); NameResolver resolver = mock(NameResolver.class); delegatedResolver.put(scheme, resolver); return resolver; From 9b65e38b82162acb20571faa9078fe62fc4b759f Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Wed, 15 Apr 2026 13:43:54 +0530 Subject: [PATCH 2/7] direct-path-interconnect --- .../GoogleCloudToProdNameResolver.java | 44 ++++++++----------- .../GoogleCloudToProdNameResolverTest.java | 14 ++++++ 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 59a898783f8..797de707be4 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -48,7 +48,6 @@ import java.io.Reader; import java.net.HttpURLConnection; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.List; @@ -96,6 +95,7 @@ private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) } BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( generateBootstrap("", true, true)); + // Avoid setting global when testing if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { forceXdsBootstrapInfo = newInfo; } @@ -108,6 +108,7 @@ private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) } BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( generateBootstrap()); + // Avoid setting global when testing if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { bootstrapInfo = newInfo; } @@ -143,12 +144,13 @@ private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) GoogleCloudToProdNameResolver(URI targetUri, Args args, Resource executorResource, NameResolver.Factory nameResolverFactory) { this.executorResource = checkNotNull(executorResource, "executorResource"); - String query = targetUri.getRawQuery(); + String targetPath = checkNotNull(checkNotNull(targetUri, "targetUri").getPath(), "targetPath"); + Uri grpcUri = Uri.create(targetUri.toString()); + String query = grpcUri.getRawQuery(); this.forceXds = checkForceXds(query); this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns"; String newQuery = stripForceXds(query); - String targetPath = checkNotNull(checkNotNull(targetUri, "targetUri").getPath(), "targetPath"); Preconditions.checkArgument( targetPath.startsWith("/"), "the path component (%s) of the target (%s) must start with '/'", @@ -157,28 +159,16 @@ private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) authority = GrpcUtil.checkAuthority(targetPath.substring(1)); syncContext = checkNotNull(args, "args").getSynchronizationContext(); - String rawAuthority = schemeOverride.equals("xds") - ? C2P_AUTHORITY - : targetUri.getRawAuthority(); - String rawPath = targetUri.getRawPath(); - String rawFragment = targetUri.getRawFragment(); - try { - StringBuilder uriStr = new StringBuilder(); - uriStr.append(schemeOverride).append(":"); - if (rawAuthority != null) { - uriStr.append("//").append(rawAuthority); - } - uriStr.append(rawPath); - if (newQuery != null) { - uriStr.append("?").append(newQuery); - } - if (rawFragment != null) { - uriStr.append("#").append(rawFragment); - } - targetUri = new URI(uriStr.toString()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Invalid URI", e); + Uri.Builder modifiedTargetBuilder = grpcUri.toBuilder().setScheme(schemeOverride); + if (newQuery != null) { + modifiedTargetBuilder.setRawQuery(newQuery); + } else { + modifiedTargetBuilder.setQuery(null); } + if (schemeOverride.equals("xds")) { + modifiedTargetBuilder.setRawAuthority(C2P_AUTHORITY); + } + targetUri = URI.create(modifiedTargetBuilder.build().toString()); if (schemeOverride.equals("xds")) { args = args.toBuilder() @@ -440,9 +430,13 @@ private static boolean checkForceXds(String query) { return false; } for (String part : Splitter.on('&').split(query)) { - if (part.equals("force-xds") || part.startsWith("force-xds=")) { + if (part.equals("force-xds")) { return true; } + if (part.startsWith("force-xds=")) { + String value = part.substring("force-xds=".length()); + return !value.equalsIgnoreCase("false") && !value.equals("0"); + } } return false; } diff --git a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java index 3c4866ca224..ee73afa17af 100644 --- a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java +++ b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java @@ -238,6 +238,20 @@ public void notOnGcpButForceXds_KeyValueTrue_DelegateToXds() { } } + @Test + public void notOnGcpButForceXds_KeyValueFalse_DelegateToDns() { + GoogleCloudToProdNameResolver.isOnGcp = false; + String target = TARGET_URI + "?force-xds=false"; + resolver = enableRfc3986UrisParam + ? new GoogleCloudToProdNameResolver( + Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) + : new GoogleCloudToProdNameResolver( + URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); + resolver.start(mockListener); + fakeExecutor.runDueTasks(); + assertThat(delegatedResolver.keySet()).containsExactly("dns"); + } + @Test public void notOnGcpButForceXds_KeyValueOne_DelegateToXds() { GoogleCloudToProdNameResolver.isOnGcp = false; From a52050ec45cb9f67dd9ea8500d140ff98eb86c46 Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Thu, 7 May 2026 18:14:06 +0530 Subject: [PATCH 3/7] rework with QueryParams --- .../GoogleCloudToProdNameResolver.java | 29 ++++-------- .../GoogleCloudToProdNameResolverTest.java | 47 ++----------------- 2 files changed, 15 insertions(+), 61 deletions(-) diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 797de707be4..9ec6c4270c0 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -20,7 +20,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.CharStreams; @@ -28,6 +27,7 @@ import io.grpc.MetricRecorder; import io.grpc.NameResolver; import io.grpc.NameResolverRegistry; +import io.grpc.QueryParams; import io.grpc.Status; import io.grpc.SynchronizationContext; import io.grpc.Uri; @@ -163,7 +163,7 @@ private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) if (newQuery != null) { modifiedTargetBuilder.setRawQuery(newQuery); } else { - modifiedTargetBuilder.setQuery(null); + modifiedTargetBuilder.setRawQuery(null); } if (schemeOverride.equals("xds")) { modifiedTargetBuilder.setRawAuthority(C2P_AUTHORITY); @@ -214,7 +214,7 @@ private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) if (newQuery != null) { modifiedTargetBuilder.setRawQuery(newQuery); } else { - modifiedTargetBuilder.setQuery(null); + modifiedTargetBuilder.setRawQuery(null); } if (schemeOverride.equals("xds")) { @@ -429,14 +429,11 @@ private static boolean checkForceXds(String query) { if (query == null) { return false; } - for (String part : Splitter.on('&').split(query)) { - if (part.equals("force-xds")) { + QueryParams params = QueryParams.fromRawQuery(query); + for (QueryParams.Entry entry : params.asList()) { + if ("force-xds".equals(entry.getKey())) { return true; } - if (part.startsWith("force-xds=")) { - String value = part.substring("force-xds=".length()); - return !value.equalsIgnoreCase("false") && !value.equals("0"); - } } return false; } @@ -445,16 +442,10 @@ private static String stripForceXds(String query) { if (query == null) { return null; } - StringBuilder sb = new StringBuilder(); - for (String part : Splitter.on('&').split(query)) { - if (!part.equals("force-xds") && !part.startsWith("force-xds=")) { - if (sb.length() > 0) { - sb.append("&"); - } - sb.append(part); - } - } - return sb.length() == 0 ? null : sb.toString(); + QueryParams params = QueryParams.fromRawQuery(query); + params.asList().removeIf(entry -> "force-xds".equals(entry.getKey())); + params.asList().removeIf(entry -> entry.getKey().isEmpty() && !entry.hasValue()); + return params.toRawQuery(); } private enum HttpConnectionFactory implements HttpConnectionProvider { diff --git a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java index ee73afa17af..398335818d0 100644 --- a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java +++ b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java @@ -206,7 +206,7 @@ public void notOnGcpButForceXds_DelegateToXds() { if (enableRfc3986UrisParam) { Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getQuery()).isNull(); + assertThat(delegatedRfcUriValue.getRawQuery()).isNull(); } else { URI delegatedUriValue = delegatedUri.get("xds"); assertThat(delegatedUriValue).isNotNull(); @@ -230,7 +230,7 @@ public void notOnGcpButForceXds_KeyValueTrue_DelegateToXds() { if (enableRfc3986UrisParam) { Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getQuery()).isNull(); + assertThat(delegatedRfcUriValue.getRawQuery()).isNull(); } else { URI delegatedUriValue = delegatedUri.get("xds"); assertThat(delegatedUriValue).isNotNull(); @@ -238,43 +238,6 @@ public void notOnGcpButForceXds_KeyValueTrue_DelegateToXds() { } } - @Test - public void notOnGcpButForceXds_KeyValueFalse_DelegateToDns() { - GoogleCloudToProdNameResolver.isOnGcp = false; - String target = TARGET_URI + "?force-xds=false"; - resolver = enableRfc3986UrisParam - ? new GoogleCloudToProdNameResolver( - Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) - : new GoogleCloudToProdNameResolver( - URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); - resolver.start(mockListener); - fakeExecutor.runDueTasks(); - assertThat(delegatedResolver.keySet()).containsExactly("dns"); - } - - @Test - public void notOnGcpButForceXds_KeyValueOne_DelegateToXds() { - GoogleCloudToProdNameResolver.isOnGcp = false; - String target = TARGET_URI + "?force-xds=1"; - resolver = enableRfc3986UrisParam - ? new GoogleCloudToProdNameResolver( - Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) - : new GoogleCloudToProdNameResolver( - URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); - resolver.start(mockListener); - fakeExecutor.runDueTasks(); - assertThat(delegatedResolver.keySet()).containsExactly("xds"); - - if (enableRfc3986UrisParam) { - Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); - assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getQuery()).isNull(); - } else { - URI delegatedUriValue = delegatedUri.get("xds"); - assertThat(delegatedUriValue).isNotNull(); - assertThat(delegatedUriValue.getQuery()).isNull(); - } - } @Test public void notOnGcpButForceXds_WithMultipleParams_DelegateToXds() { @@ -292,7 +255,7 @@ public void notOnGcpButForceXds_WithMultipleParams_DelegateToXds() { if (enableRfc3986UrisParam) { Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getQuery()).isEqualTo("foo=bar&baz=qux"); + assertThat(delegatedRfcUriValue.getRawQuery()).isEqualTo("foo=bar&baz=qux"); } else { URI delegatedUriValue = delegatedUri.get("xds"); assertThat(delegatedUriValue).isNotNull(); @@ -340,7 +303,7 @@ public void notOnGcpButForceXds_EdgeCaseAmpersands_DelegateToXds() { if (enableRfc3986UrisParam) { Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getQuery()).isNull(); + assertThat(delegatedRfcUriValue.getRawQuery()).isNull(); } else { URI delegatedUriValue = delegatedUri.get("xds"); assertThat(delegatedUriValue).isNotNull(); @@ -363,7 +326,7 @@ public void notOnGcpButForceXds_CaseSensitive_DelegateToDns() { if (enableRfc3986UrisParam) { Uri delegatedRfcUriValue = delegatedRfcUri.get("dns"); assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getQuery()).isEqualTo("FORCE-XDS"); + assertThat(delegatedRfcUriValue.getRawQuery()).isEqualTo("FORCE-XDS"); } else { URI delegatedUriValue = delegatedUri.get("dns"); assertThat(delegatedUriValue).isNotNull(); From 45cf39ce6ac4cac5b8aa37a1fe461517a48ff18f Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Thu, 7 May 2026 19:20:42 +0530 Subject: [PATCH 4/7] use one bootstrap file --- .../GoogleCloudToProdNameResolver.java | 49 +++++++++---------- .../GoogleCloudToProdNameResolverTest.java | 1 + 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 9ec6c4270c0..911a2d700dc 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -77,43 +77,31 @@ final class GoogleCloudToProdNameResolver extends NameResolver { System.getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"); private static final Object BOOTSTRAP_LOCK = new Object(); - private static final Object FORCE_XDS_BOOTSTRAP_LOCK = new Object(); @GuardedBy("BOOTSTRAP_LOCK") - private static BootstrapInfo bootstrapInfo; - @GuardedBy("FORCE_XDS_BOOTSTRAP_LOCK") - private static BootstrapInfo forceXdsBootstrapInfo; + static BootstrapInfo bootstrapInfo; private static HttpConnectionProvider httpConnectionProvider = HttpConnectionFactory.INSTANCE; private static int c2pId = new Random().nextInt(); private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) throws XdsInitializationException, IOException { - if (isForcedXds) { - synchronized (FORCE_XDS_BOOTSTRAP_LOCK) { - if (forceXdsBootstrapInfo != null) { - return forceXdsBootstrapInfo; - } - BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( - generateBootstrap("", true, true)); - // Avoid setting global when testing - if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { - forceXdsBootstrapInfo = newInfo; - } - return newInfo; + synchronized (BOOTSTRAP_LOCK) { + if (bootstrapInfo != null) { + return bootstrapInfo; } - } else { - synchronized (BOOTSTRAP_LOCK) { - if (bootstrapInfo != null) { - return bootstrapInfo; - } - BootstrapInfo newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( + BootstrapInfo newInfo; + if (isForcedXds) { + newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( + generateBootstrap("", true, true)); + } else { + newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( generateBootstrap()); - // Avoid setting global when testing - if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { - bootstrapInfo = newInfo; - } - return newInfo; } + // Avoid setting global when testing + if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { + bootstrapInfo = newInfo; + } + return newInfo; } } @@ -425,6 +413,13 @@ static void setC2pId(int c2pId) { GoogleCloudToProdNameResolver.c2pId = c2pId; } + @VisibleForTesting + static void resetBootstrapInfo() { + synchronized (BOOTSTRAP_LOCK) { + bootstrapInfo = null; + } + } + private static boolean checkForceXds(String query) { if (query == null) { return false; diff --git a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java index 398335818d0..82dad59ce25 100644 --- a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java +++ b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java @@ -155,6 +155,7 @@ public HttpURLConnection createConnection(String url) throws IOException { public void tearDown() { GoogleCloudToProdNameResolver.isOnGcp = originalIsOnGcp; GoogleCloudToProdNameResolver.setHttpConnectionProvider(null); + GoogleCloudToProdNameResolver.resetBootstrapInfo(); if (resolver != null) { resolver.shutdown(); verify(Iterables.getOnlyElement(delegatedResolver.values())).shutdown(); From 39bce961b93d1b781d3bbbc32aae65781cbf71ad Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Thu, 7 May 2026 19:40:05 +0530 Subject: [PATCH 5/7] resolve comments --- .../GoogleCloudToProdNameResolver.java | 33 +++---- .../GoogleCloudToProdNameResolverTest.java | 93 ------------------- 2 files changed, 12 insertions(+), 114 deletions(-) diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 911a2d700dc..5a8f62a3dcd 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -76,33 +76,31 @@ final class GoogleCloudToProdNameResolver extends NameResolver { private static final String serverUriOverride = System.getenv("GRPC_TEST_ONLY_GOOGLE_C2P_RESOLVER_TRAFFIC_DIRECTOR_URI"); - private static final Object BOOTSTRAP_LOCK = new Object(); - - @GuardedBy("BOOTSTRAP_LOCK") - static BootstrapInfo bootstrapInfo; + @GuardedBy("GoogleCloudToProdNameResolver.class") + private static BootstrapInfo bootstrapInfo; private static HttpConnectionProvider httpConnectionProvider = HttpConnectionFactory.INSTANCE; private static int c2pId = new Random().nextInt(); - private static BootstrapInfo getBootstrapInfo(boolean isForcedXds) + private static synchronized BootstrapInfo getBootstrapInfo(boolean isForcedXds) throws XdsInitializationException, IOException { - synchronized (BOOTSTRAP_LOCK) { - if (bootstrapInfo != null) { + if (bootstrapInfo != null) { return bootstrapInfo; } BootstrapInfo newInfo; if (isForcedXds) { newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( - generateBootstrap("", true, true)); + generateBootstrap("", true)); } else { newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( - generateBootstrap()); + generateBootstrap( + queryZoneMetadata(METADATA_URL_ZONE), + queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6))); } // Avoid setting global when testing if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { bootstrapInfo = newInfo; } return newInfo; - } } private final String authority; @@ -297,22 +295,15 @@ public void run() { executor.execute(new Resolve()); } - @VisibleForTesting - static ImmutableMap generateBootstrap() throws IOException { - return generateBootstrap( - queryZoneMetadata(METADATA_URL_ZONE), - queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6), false); - } - - static ImmutableMap generateBootstrap( - String zone, boolean supportIpv6, boolean isForcedXds) { + private static ImmutableMap generateBootstrap( + String zone, boolean supportIpv6) { ImmutableMap.Builder nodeBuilder = ImmutableMap.builder(); String nodeIdPrefix = isOnGcp ? "C2P-" : "C2P-non-gcp-"; nodeBuilder.put("id", nodeIdPrefix + (c2pId & Integer.MAX_VALUE)); - if (!isForcedXds && !zone.isEmpty()) { + if (!zone.isEmpty()) { nodeBuilder.put("locality", ImmutableMap.of("zone", zone)); } - if (isForcedXds || supportIpv6) { + if (supportIpv6) { nodeBuilder.put("metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); } diff --git a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java index 82dad59ce25..0ac5716b828 100644 --- a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java +++ b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java @@ -21,8 +21,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import io.grpc.ChannelLogger; import io.grpc.MetricRecorder; @@ -46,7 +44,6 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.Executor; @@ -335,96 +332,6 @@ public void notOnGcpButForceXds_CaseSensitive_DelegateToDns() { } } - @SuppressWarnings("unchecked") - @Test - public void generateBootstrap_ipv6() throws IOException { - GoogleCloudToProdNameResolver.isOnGcp = true; - Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap(); - Map node = (Map) bootstrap.get("node"); - assertThat(node).containsExactly( - "id", "C2P-991614323", - "locality", ImmutableMap.of("zone", ZONE), - "metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); - Map server = Iterables.getOnlyElement( - (List>) bootstrap.get("xds_servers")); - assertThat(server).containsExactly( - "server_uri", "directpath-pa.googleapis.com", - "channel_creds", ImmutableList.of(ImmutableMap.of("type", "google_default")), - "server_features", ImmutableList.of("xds_v3", "ignore_resource_deletion")); - Map authorities = (Map) bootstrap.get("authorities"); - assertThat(authorities).containsExactly( - "traffic-director-c2p.xds.googleapis.com", - ImmutableMap.of("xds_servers", ImmutableList.of(server))); - } - - @SuppressWarnings("unchecked") - @Test - public void generateBootstrap_forceXds() throws IOException { - GoogleCloudToProdNameResolver.isOnGcp = false; - Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap("", true, true); - Map node = (Map) bootstrap.get("node"); - assertThat(node).containsExactly( - "id", "C2P-non-gcp-991614323", - "metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); - - Map server = Iterables.getOnlyElement( - (List>) bootstrap.get("xds_servers")); - assertThat(server).containsExactly( - "server_uri", "directpath-pa.googleapis.com", - "channel_creds", ImmutableList.of(ImmutableMap.of("type", "google_default")), - "server_features", ImmutableList.of("xds_v3", "ignore_resource_deletion")); - Map authorities = (Map) bootstrap.get("authorities"); - assertThat(authorities).containsExactly( - "traffic-director-c2p.xds.googleapis.com", - ImmutableMap.of("xds_servers", ImmutableList.of(server))); - } - - @SuppressWarnings("unchecked") - @Test - public void generateBootstrap_onGcpAndForceXds() throws IOException { - GoogleCloudToProdNameResolver.isOnGcp = true; - Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap("", true, true); - Map node = (Map) bootstrap.get("node"); - assertThat(node).containsExactly( - "id", "C2P-991614323", - "metadata", ImmutableMap.of("TRAFFICDIRECTOR_DIRECTPATH_C2P_IPV6_CAPABLE", true)); - assertThat(node).doesNotContainKey("locality"); - } - - @SuppressWarnings("unchecked") - @Test - public void generateBootstrap_noIpV6() throws IOException { - GoogleCloudToProdNameResolver.isOnGcp = true; - responseToIpV6 = null; - Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap(); - Map node = (Map) bootstrap.get("node"); - assertThat(node).containsExactly( - "id", "C2P-991614323", - "locality", ImmutableMap.of("zone", ZONE)); - Map server = Iterables.getOnlyElement( - (List>) bootstrap.get("xds_servers")); - assertThat(server).containsExactly( - "server_uri", "directpath-pa.googleapis.com", - "channel_creds", ImmutableList.of(ImmutableMap.of("type", "google_default")), - "server_features", ImmutableList.of("xds_v3", "ignore_resource_deletion")); - Map authorities = (Map) bootstrap.get("authorities"); - assertThat(authorities).containsExactly( - "traffic-director-c2p.xds.googleapis.com", - ImmutableMap.of("xds_servers", ImmutableList.of(server))); - } - - @SuppressWarnings("unchecked") - @Test - public void emptyResolverMeetadataValue() throws IOException { - GoogleCloudToProdNameResolver.isOnGcp = true; - responseToIpV6 = ""; - Map bootstrap = GoogleCloudToProdNameResolver.generateBootstrap(); - Map node = (Map) bootstrap.get("node"); - assertThat(node).containsExactly( - "id", "C2P-991614323", - "locality", ImmutableMap.of("zone", ZONE)); - } - @Test public void failToQueryMetadata() { GoogleCloudToProdNameResolver.isOnGcp = true; From 8106aaec7b2d938f65bae7a9f5589b83d6bb31a4 Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Thu, 7 May 2026 20:04:12 +0530 Subject: [PATCH 6/7] remove empty ampersand edge case --- .../GoogleCloudToProdNameResolver.java | 42 ++++++++----------- .../GoogleCloudToProdNameResolverTest.java | 25 ----------- 2 files changed, 17 insertions(+), 50 deletions(-) diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 5a8f62a3dcd..11c08e25f95 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -84,23 +84,23 @@ final class GoogleCloudToProdNameResolver extends NameResolver { private static synchronized BootstrapInfo getBootstrapInfo(boolean isForcedXds) throws XdsInitializationException, IOException { if (bootstrapInfo != null) { - return bootstrapInfo; - } - BootstrapInfo newInfo; - if (isForcedXds) { - newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( - generateBootstrap("", true)); - } else { - newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( - generateBootstrap( - queryZoneMetadata(METADATA_URL_ZONE), - queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6))); - } - // Avoid setting global when testing - if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { - bootstrapInfo = newInfo; - } - return newInfo; + return bootstrapInfo; + } + BootstrapInfo newInfo; + if (isForcedXds) { + newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( + generateBootstrap("", true)); + } else { + newInfo = InternalGrpcBootstrapperImpl.parseBootstrap( + generateBootstrap( + queryZoneMetadata(METADATA_URL_ZONE), + queryIpv6SupportMetadata(METADATA_URL_SUPPORT_IPV6))); + } + // Avoid setting global when testing + if (httpConnectionProvider == HttpConnectionFactory.INSTANCE) { + bootstrapInfo = newInfo; + } + return newInfo; } private final String authority; @@ -404,13 +404,6 @@ static void setC2pId(int c2pId) { GoogleCloudToProdNameResolver.c2pId = c2pId; } - @VisibleForTesting - static void resetBootstrapInfo() { - synchronized (BOOTSTRAP_LOCK) { - bootstrapInfo = null; - } - } - private static boolean checkForceXds(String query) { if (query == null) { return false; @@ -430,7 +423,6 @@ private static String stripForceXds(String query) { } QueryParams params = QueryParams.fromRawQuery(query); params.asList().removeIf(entry -> "force-xds".equals(entry.getKey())); - params.asList().removeIf(entry -> entry.getKey().isEmpty() && !entry.hasValue()); return params.toRawQuery(); } diff --git a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java index 0ac5716b828..bbd3ba3ef05 100644 --- a/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java +++ b/googleapis/src/test/java/io/grpc/googleapis/GoogleCloudToProdNameResolverTest.java @@ -152,7 +152,6 @@ public HttpURLConnection createConnection(String url) throws IOException { public void tearDown() { GoogleCloudToProdNameResolver.isOnGcp = originalIsOnGcp; GoogleCloudToProdNameResolver.setHttpConnectionProvider(null); - GoogleCloudToProdNameResolver.resetBootstrapInfo(); if (resolver != null) { resolver.shutdown(); verify(Iterables.getOnlyElement(delegatedResolver.values())).shutdown(); @@ -285,30 +284,6 @@ public void notOnGcpButForceXds_WithEncodedAmpersand_DelegateToXds() { } } - @Test - public void notOnGcpButForceXds_EdgeCaseAmpersands_DelegateToXds() { - GoogleCloudToProdNameResolver.isOnGcp = false; - String target = TARGET_URI + "?&force-xds&"; - resolver = enableRfc3986UrisParam - ? new GoogleCloudToProdNameResolver( - Uri.create(target), args, fakeExecutorResource, nsRegistry.asFactory()) - : new GoogleCloudToProdNameResolver( - URI.create(target), args, fakeExecutorResource, nsRegistry.asFactory()); - resolver.start(mockListener); - fakeExecutor.runDueTasks(); - assertThat(delegatedResolver.keySet()).containsExactly("xds"); - - if (enableRfc3986UrisParam) { - Uri delegatedRfcUriValue = delegatedRfcUri.get("xds"); - assertThat(delegatedRfcUriValue).isNotNull(); - assertThat(delegatedRfcUriValue.getRawQuery()).isNull(); - } else { - URI delegatedUriValue = delegatedUri.get("xds"); - assertThat(delegatedUriValue).isNotNull(); - assertThat(delegatedUriValue.getQuery()).isNull(); - } - } - @Test public void notOnGcpButForceXds_CaseSensitive_DelegateToDns() { GoogleCloudToProdNameResolver.isOnGcp = false; From 20bf74ac11d077bfdab8864dd6ba5a36e23bade1 Mon Sep 17 00:00:00 2001 From: MV Shiva Prasad Date: Fri, 8 May 2026 11:35:00 +0530 Subject: [PATCH 7/7] use QueryParams instance to handle target --- .../GoogleCloudToProdNameResolver.java | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java index 11c08e25f95..cd6e49bb3ad 100644 --- a/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java +++ b/googleapis/src/main/java/io/grpc/googleapis/GoogleCloudToProdNameResolver.java @@ -132,10 +132,11 @@ private static synchronized BootstrapInfo getBootstrapInfo(boolean isForcedXds) this.executorResource = checkNotNull(executorResource, "executorResource"); String targetPath = checkNotNull(checkNotNull(targetUri, "targetUri").getPath(), "targetPath"); Uri grpcUri = Uri.create(targetUri.toString()); - String query = grpcUri.getRawQuery(); - this.forceXds = checkForceXds(query); + QueryParams queryParams = QueryParams.fromRawQuery(grpcUri.getRawQuery()); + this.forceXds = checkForceXds(queryParams); this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns"; - String newQuery = stripForceXds(query); + stripForceXds(queryParams); + String newQuery = queryParams.toRawQuery(); Preconditions.checkArgument( targetPath.startsWith("/"), @@ -180,10 +181,11 @@ private static synchronized BootstrapInfo getBootstrapInfo(boolean isForcedXds) Resource executorResource, NameResolver.Factory nameResolverFactory) { this.executorResource = checkNotNull(executorResource, "executorResource"); - String query = targetUri.getRawQuery(); - this.forceXds = checkForceXds(query); + QueryParams queryParams = QueryParams.fromRawQuery(targetUri.getRawQuery()); + this.forceXds = checkForceXds(queryParams); this.schemeOverride = (forceXds || isOnGcp) ? "xds" : "dns"; - String newQuery = stripForceXds(query); + stripForceXds(queryParams); + String newQuery = queryParams.toRawQuery(); Preconditions.checkArgument( targetUri.isPathAbsolute(), @@ -404,11 +406,7 @@ static void setC2pId(int c2pId) { GoogleCloudToProdNameResolver.c2pId = c2pId; } - private static boolean checkForceXds(String query) { - if (query == null) { - return false; - } - QueryParams params = QueryParams.fromRawQuery(query); + private static boolean checkForceXds(QueryParams params) { for (QueryParams.Entry entry : params.asList()) { if ("force-xds".equals(entry.getKey())) { return true; @@ -417,13 +415,8 @@ private static boolean checkForceXds(String query) { return false; } - private static String stripForceXds(String query) { - if (query == null) { - return null; - } - QueryParams params = QueryParams.fromRawQuery(query); + private static void stripForceXds(QueryParams params) { params.asList().removeIf(entry -> "force-xds".equals(entry.getKey())); - return params.toRawQuery(); } private enum HttpConnectionFactory implements HttpConnectionProvider {