From 378275c1325b9a3e2297cd4c11ffc90869738285 Mon Sep 17 00:00:00 2001 From: nidhiii-27 Date: Mon, 27 Jul 2026 23:08:28 +0530 Subject: [PATCH 01/10] feat(storage): add support for DirectPath over Interconnect --- .../cloud/storage/GrpcStorageOptions.java | 40 +++- .../storage/StorageOptionsBuilderTest.java | 32 +++ .../storage/it/ITStorageOptionsTest.java | 35 ++++ .../InstantiatingGrpcChannelProvider.java | 70 +++++-- .../InstantiatingGrpcChannelProviderTest.java | 184 ++++++++++++++++++ 5 files changed, 348 insertions(+), 13 deletions(-) diff --git a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java index 1a6726b9c01b..9c1cd1550442 100644 --- a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java +++ b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java @@ -142,6 +142,7 @@ public final class GrpcStorageOptions extends StorageOptions private final GrpcRetryAlgorithmManager retryAlgorithmManager; private final java.time.Duration terminationAwaitDuration; private final boolean attemptDirectPath; + private final boolean attemptDirectPathXdsOverInterconnect; private final boolean enableGrpcClientMetrics; private final boolean grpcClientMetricsManuallyEnabled; @@ -160,6 +161,7 @@ private GrpcStorageOptions(Builder builder, GrpcStorageDefaults serviceDefaults) builder.terminationAwaitDuration, serviceDefaults.getTerminationAwaitDurationJavaTime()); this.attemptDirectPath = builder.attemptDirectPath; + this.attemptDirectPathXdsOverInterconnect = builder.attemptDirectPathXdsOverInterconnect; this.enableGrpcClientMetrics = builder.enableGrpcClientMetrics; this.grpcClientMetricsManuallyEnabled = builder.grpcMetricsManuallyEnabled; this.grpcInterceptorProvider = builder.grpcInterceptorProvider; @@ -230,6 +232,13 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE */ private Tuple> resolveSettingsAndOpts() throws IOException { String endpoint = getHost(); + if (attemptDirectPathXdsOverInterconnect) { + if ("https://storage.googleapis.com".equals(endpoint)) { + endpoint = "https://storage.direct.googleapis.com"; + } else if ("storage.googleapis.com".equals(endpoint)) { + endpoint = "storage.direct.googleapis.com"; + } + } URI uri = URI.create(endpoint); String scheme = uri.getScheme(); int port = uri.getPort(); @@ -322,7 +331,8 @@ private Tuple> resolveSettingsAndOpts() throw InstantiatingGrpcChannelProvider.newBuilder() .setEndpoint(endpoint) .setAllowNonDefaultServiceAccount(true) - .setAttemptDirectPath(attemptDirectPath); + .setAttemptDirectPath(attemptDirectPath || attemptDirectPathXdsOverInterconnect) + .setAttemptDirectPathXdsOverInterconnect(attemptDirectPathXdsOverInterconnect); if (!DIRECT_PATH_BOUND_TOKEN_DISABLED) { channelProviderBuilder.setAllowHardBoundTokenTypes( @@ -337,6 +347,18 @@ private Tuple> resolveSettingsAndOpts() throw channelProviderBuilder.setAttemptDirectPathXds(); } + if (attemptDirectPathXdsOverInterconnect) { + com.google.api.core.ApiFunction + existingConfigurator = channelProviderBuilder.getChannelConfigurator(); + channelProviderBuilder.setChannelConfigurator( + channelBuilder -> { + if (existingConfigurator != null) { + channelBuilder = existingConfigurator.apply(channelBuilder); + } + return channelBuilder.overrideAuthority("storage.googleapis.com"); + }); + } + if (scheme.equals("http")) { channelProviderBuilder.setChannelConfigurator(ManagedChannelBuilder::usePlaintext); } @@ -428,6 +450,7 @@ public int hashCode() { retryAlgorithmManager, terminationAwaitDuration, attemptDirectPath, + attemptDirectPathXdsOverInterconnect, enableGrpcClientMetrics, grpcInterceptorProvider, blobWriteSessionConfig, @@ -445,6 +468,7 @@ public boolean equals(Object o) { } GrpcStorageOptions that = (GrpcStorageOptions) o; return attemptDirectPath == that.attemptDirectPath + && attemptDirectPathXdsOverInterconnect == that.attemptDirectPathXdsOverInterconnect && enableGrpcClientMetrics == that.enableGrpcClientMetrics && Objects.equals(retryAlgorithmManager, that.retryAlgorithmManager) && Objects.equals(terminationAwaitDuration, that.terminationAwaitDuration) @@ -494,6 +518,7 @@ public static final class Builder extends StorageOptions.Builder { private StorageRetryStrategy storageRetryStrategy; private java.time.Duration terminationAwaitDuration; private boolean attemptDirectPath = GrpcStorageDefaults.INSTANCE.isAttemptDirectPath(); + private boolean attemptDirectPathXdsOverInterconnect = false; private boolean enableGrpcClientMetrics = GrpcStorageDefaults.INSTANCE.isEnableGrpcClientMetrics(); private GrpcInterceptorProvider grpcInterceptorProvider = @@ -512,6 +537,7 @@ public static final class Builder extends StorageOptions.Builder { this.storageRetryStrategy = gso.getRetryAlgorithmManager().retryStrategy; this.terminationAwaitDuration = gso.getTerminationAwaitDuration(); this.attemptDirectPath = gso.attemptDirectPath; + this.attemptDirectPathXdsOverInterconnect = gso.attemptDirectPathXdsOverInterconnect; this.enableGrpcClientMetrics = gso.enableGrpcClientMetrics; this.grpcInterceptorProvider = gso.grpcInterceptorProvider; this.blobWriteSessionConfig = gso.blobWriteSessionConfig; @@ -556,6 +582,18 @@ public GrpcStorageOptions.Builder setAttemptDirectPath(boolean attemptDirectPath return this; } + /** + * Option for whether this client should attempt to use DirectPath over Interconnect (on-premise + * xDS name resolution). + * + * @since 2.45.0 + */ + public GrpcStorageOptions.Builder setAttemptDirectPathXdsOverInterconnect( + boolean attemptDirectPathXdsOverInterconnect) { + this.attemptDirectPathXdsOverInterconnect = attemptDirectPathXdsOverInterconnect; + return this; + } + /** * Option for whether this client should emit internal gRPC client internal metrics to Cloud * Monitoring. To disable metric reporting, set this to false. True by default. Emitting metrics diff --git a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java index 240040519635..b881ac041365 100644 --- a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java +++ b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java @@ -69,6 +69,38 @@ public void grpc() throws Exception { () -> assertThat(rebuilt.hashCode()).isEqualTo(base.hashCode())); } + @Test + public void grpc_attemptDirectPathXdsOverInterconnect() throws Exception { + com.google.auth.Credentials mockCreds = + org.mockito.Mockito.mock(com.google.auth.Credentials.class); + GrpcStorageOptions options = + GrpcStorageOptions.grpc() + .setCredentials(mockCreds) + .setAttemptDirectPathXdsOverInterconnect(true) + .build(); + + GrpcStorageOptions rebuilt = options.toBuilder().build(); + assertAll( + () -> assertThat(rebuilt).isEqualTo(options), + () -> assertThat(rebuilt.hashCode()).isEqualTo(options.hashCode())); + + com.google.storage.v2.StorageSettings settings = options.getStorageSettings(); + assertThat(settings.getEndpoint()).isEqualTo("storage.direct.googleapis.com:443"); + + com.google.api.gax.rpc.TransportChannelProvider tcp = settings.getTransportChannelProvider(); + assertThat(tcp).isInstanceOf(com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.class); + com.google.api.gax.grpc.InstantiatingGrpcChannelProvider provider = + (com.google.api.gax.grpc.InstantiatingGrpcChannelProvider) tcp; + + // Verify attemptDirectPathXdsOverInterconnect is set to true on the provider using reflection + java.lang.reflect.Field field = + com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.class.getDeclaredField( + "attemptDirectPathXdsOverInterconnect"); + field.setAccessible(true); + Boolean value = (Boolean) field.get(provider); + assertThat(value).isTrue(); + } + @Test public void useJwtAccessWithScope_defaultsToFalse() { HttpStorageOptions httpOptions = HttpStorageOptions.http().build(); diff --git a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageOptionsTest.java b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageOptionsTest.java index 0d8114a7fb2d..f33ae01c45c4 100644 --- a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageOptionsTest.java +++ b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageOptionsTest.java @@ -84,6 +84,32 @@ public void clientShouldConstructCleanly_directPath() throws Exception { doTest(options); } + @Test + public void clientShouldConstructCleanly_directPathXdsOverInterconnect() throws Exception { + StorageOptions options = + StorageOptions.grpc() + .setCredentials(credentials) + .setAttemptDirectPathXdsOverInterconnect(true) + .setEnableGrpcClientMetrics(false) + .build(); + doTest(options); + } + + @Test + public void clientShouldWork_directPathXdsOverInterconnect() throws Exception { + assumeTrue( + "Environment cannot resolve storage.direct.googleapis.com", canResolveDirectPathAddress()); + StorageOptions options = + StorageOptions.grpc() + .setCredentials(credentials) + .setAttemptDirectPathXdsOverInterconnect(true) + .setEnableGrpcClientMetrics(false) + .build(); + try (Storage storage = options.getService()) { + storage.list(Storage.BucketListOption.pageSize(1)); + } + } + @Test public void lackOfProjectIdDoesNotPreventConstruction_http() throws Exception { StorageOptions options = StorageOptions.http().setCredentials(credentials).build(); @@ -105,4 +131,13 @@ private static void doTest(StorageOptions options) throws Exception { //noinspection EmptyTryBlock try (Storage ignore = options.getService()) {} } + + private static boolean canResolveDirectPathAddress() { + try { + java.net.InetAddress.getAllByName("storage.direct.googleapis.com"); + return true; + } catch (java.net.UnknownHostException e) { + return false; + } + } } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java index 56f44fa7ae0a..2b938a6c1a89 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java @@ -151,6 +151,7 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP @Nullable private final ChannelPrimer channelPrimer; @Nullable private final Boolean attemptDirectPath; @Nullable private final Boolean attemptDirectPathXds; + @Nullable private final Boolean attemptDirectPathXdsOverInterconnect; @Nullable private final Boolean allowNonDefaultServiceAccount; @VisibleForTesting final ImmutableMap directPathServiceConfig; @Nullable private final MtlsProvider mtlsProvider; @@ -234,6 +235,7 @@ private InstantiatingGrpcChannelProvider(Builder builder) { this.channelPrimer = builder.channelPrimer; this.attemptDirectPath = builder.attemptDirectPath; this.attemptDirectPathXds = builder.attemptDirectPathXds; + this.attemptDirectPathXdsOverInterconnect = builder.attemptDirectPathXdsOverInterconnect; this.allowNonDefaultServiceAccount = builder.allowNonDefaultServiceAccount; this.directPathServiceConfig = builder.directPathServiceConfig == null @@ -264,12 +266,6 @@ public boolean needsExecutor() { return executor == null; } - @Nullable - @Override - public Executor getExecutor() { - return executor; - } - @Deprecated @Override public TransportChannelProvider withExecutor(ScheduledExecutorService executor) { @@ -431,6 +427,10 @@ private boolean isDirectPathXdsEnabledViaEnv() { return Boolean.parseBoolean(directPathXdsEnv); } + private boolean isAttemptDirectPathXdsOverInterconnect() { + return Boolean.TRUE.equals(attemptDirectPathXdsOverInterconnect); + } + /** * This method tells if Direct Path xDS was enabled. There are two ways of enabling it: via * environment variable (by setting GOOGLE_CLOUD_ENABLE_DIRECT_PATH_XDS=true) or when building @@ -484,7 +484,7 @@ else if (isDirectPathXdsEnabledViaBuilderOption()) { + " ."); } // Case 4: not running on GCE - if (!isOnComputeEngine()) { + if (!isOnComputeEngine() && !isAttemptDirectPathXdsOverInterconnect()) { LOG.log( level, "DirectPath is misconfigured. DirectPath is only available in a GCE environment."); @@ -498,6 +498,10 @@ boolean isCredentialDirectPathCompatible() { if (needsCredentials()) { return false; } + // xDS over Interconnect is designed to work on-premise using arbitrary service credentials. + if (isAttemptDirectPathXdsOverInterconnect()) { + return true; + } if (allowNonDefaultServiceAccount != null && allowNonDefaultServiceAccount) { return true; } @@ -705,6 +709,17 @@ ChannelCredentials createS2ASecuredChannelCredentials() { @InternalApi("For internal use by google-cloud-java clients only") public ManagedChannelBuilder createChannelBuilder() throws IOException { + // If the endpoint is already a custom URI scheme target (e.g. google-c2p:///), use it directly. + if (endpoint.contains(":///")) { + CallCredentials callCreds = MoreCallCredentials.from(credentials); + ChannelCredentials channelCreds = + GoogleDefaultChannelCredentials.newBuilder() + .callCredentials(callCreds) + .altsCallCredentials(altsCallCredentials) + .build(); + return Grpc.newChannelBuilder(endpoint, channelCreds); + } + int colon = endpoint.lastIndexOf(':'); if (colon < 0) { throw new IllegalStateException("invalid endpoint - should have been validated: " + endpoint); @@ -726,12 +741,16 @@ public ManagedChannelBuilder createChannelBuilder() throws IOException { .callCredentials(callCreds) .altsCallCredentials(altsCallCredentials) .build(); - useDirectPathXds = isDirectPathXdsEnabled(); + useDirectPathXds = isDirectPathXdsEnabled() || isAttemptDirectPathXdsOverInterconnect(); if (useDirectPathXds) { // google-c2p: CloudToProd(C2P) Directpath. This scheme is defined in // io.grpc.googleapis.GoogleCloudToProdNameResolverProvider. // This resolver target must not have a port number. - builder = Grpc.newChannelBuilder("google-c2p:///" + serviceAddress, channelCreds); + String target = "google-c2p:///" + serviceAddress; + if (isAttemptDirectPathXdsOverInterconnect()) { + target += "?force-xds"; + } + builder = Grpc.newChannelBuilder(target, channelCreds); } else { builder = Grpc.newChannelBuilderForAddress(serviceAddress, port, channelCreds); builder.defaultServiceConfig(directPathServiceConfig); @@ -862,13 +881,17 @@ private void removeApiKeyCredentialDuplicateHeaders() { * settings and a few other configurations/settings must also be valid for the request to go * through DirectPath. * - *

Checks: 1. Credentials are compatible 2.Running on Compute Engine 3. Universe Domain is - * configured to for the Google Default Universe + *

Checks: 1. Credentials are compatible 2. Running on Compute Engine (bypassed if + * attemptDirectPathXdsOverInterconnect is enabled) 3. Universe Domain is configured for the + * Google Default Universe * * @return if DirectPath is enabled for the client AND if the configurations are valid */ @InternalApi public boolean canUseDirectPath() { + if (isAttemptDirectPathXdsOverInterconnect()) { + return isDirectPathEnabled() && canUseDirectPathWithUniverseDomain(); + } return isDirectPathEnabled() && isCredentialDirectPathCompatible() && isOnComputeEngine() @@ -964,6 +987,7 @@ public static final class Builder { private ChannelPoolSettings channelPoolSettings; @Nullable private Boolean attemptDirectPath; @Nullable private Boolean attemptDirectPathXds; + @Nullable private Boolean attemptDirectPathXdsOverInterconnect; @Nullable private Boolean allowNonDefaultServiceAccount; @Nullable private ImmutableMap directPathServiceConfig; private List allowedHardBoundTokenTypes; @@ -997,6 +1021,7 @@ private Builder(InstantiatingGrpcChannelProvider provider) { this.channelPoolSettings = provider.channelPoolSettings; this.attemptDirectPath = provider.attemptDirectPath; this.attemptDirectPathXds = provider.attemptDirectPathXds; + this.attemptDirectPathXdsOverInterconnect = provider.attemptDirectPathXdsOverInterconnect; this.allowNonDefaultServiceAccount = provider.allowNonDefaultServiceAccount; this.allowedHardBoundTokenTypes = provider.allowedHardBoundTokenTypes; this.directPathServiceConfig = provider.directPathServiceConfig; @@ -1304,6 +1329,14 @@ public Builder setAttemptDirectPathXds() { return this; } + /** Use DirectPath xDS over Interconnect. Bypasses GCP GCE environment checks. */ + @InternalApi("For internal use by google-cloud-java clients only") + public Builder setAttemptDirectPathXdsOverInterconnect( + boolean attemptDirectPathXdsOverInterconnect) { + this.attemptDirectPathXdsOverInterconnect = attemptDirectPathXdsOverInterconnect; + return this; + } + @VisibleForTesting Builder setEnvProvider(EnvironmentProvider envProvider) { this.envProvider = envProvider; @@ -1458,11 +1491,24 @@ public ApiFunction getChannelConfi } private static void validateEndpoint(String endpoint) { + if (endpoint.contains(":///")) { + try { + java.net.URI.create(endpoint); + return; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("invalid endpoint URI: " + endpoint, e); + } + } int colon = endpoint.lastIndexOf(':'); if (colon < 0) { throw new IllegalArgumentException( String.format("invalid endpoint, expecting \":\"")); } - Integer.parseInt(endpoint.substring(colon + 1)); + try { + Integer.parseInt(endpoint.substring(colon + 1)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + String.format("invalid endpoint, expecting \":\""), e); + } } } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index c7f0bbcd1df9..9190da9fb4a1 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -906,6 +906,190 @@ public void canUseDirectPath_nonComputeCredentials() { Truth.assertThat(provider.canUseDirectPath()).isFalse(); } + @Test + public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceCheck() + throws IOException { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + Mockito.when( + envProvider.getenv( + InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + Credentials credentials = Mockito.mock(Credentials.class); + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setAttemptDirectPath(true) + .setAttemptDirectPathXdsOverInterconnect(true) + .setCredentials(credentials) + .setEndpoint(DEFAULT_ENDPOINT) + .setEnvProvider(envProvider); + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + Truth.assertThat(provider.canUseDirectPath()).isTrue(); + } + + @Test + public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXdsTarget() + throws IOException, InterruptedException { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + Mockito.when( + envProvider.getenv( + InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + Credentials credentials = Mockito.mock(Credentials.class); + final java.util.concurrent.atomic.AtomicReference capturedTarget = + new java.util.concurrent.atomic.AtomicReference<>(); + ApiFunction channelConfigurator = + channelBuilder -> { + try { + Class nettyBuilderClass = channelBuilder.getClass(); + java.lang.reflect.Field delegateField = null; + while (nettyBuilderClass != null && delegateField == null) { + try { + delegateField = nettyBuilderClass.getDeclaredField("delegate"); + } catch (NoSuchFieldException e) { + try { + delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); + } catch (NoSuchFieldException e2) { + nettyBuilderClass = nettyBuilderClass.getSuperclass(); + } + } + } + if (delegateField != null) { + delegateField.setAccessible(true); + Object delegate = delegateField.get(channelBuilder); + Class delegateClass = delegate.getClass(); + java.lang.reflect.Field targetField = null; + while (delegateClass != null && targetField == null) { + try { + targetField = delegateClass.getDeclaredField("target"); + } catch (NoSuchFieldException e) { + delegateClass = delegateClass.getSuperclass(); + } + } + if (targetField != null) { + targetField.setAccessible(true); + String targetValue = (String) targetField.get(delegate); + capturedTarget.set(targetValue); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if (capturedTarget.get() == null) { + capturedTarget.set(channelBuilder.toString()); + } + return channelBuilder; + }; + + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setAttemptDirectPath(true) + .setAttemptDirectPathXdsOverInterconnect(true) + .setCredentials(credentials) + .setEndpoint("storage.googleapis.com:443") + .setEnvProvider(envProvider) + .setChannelConfigurator(channelConfigurator); + + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + + InstantiatingGrpcChannelProvider configuredProvider = + (InstantiatingGrpcChannelProvider) + provider + .withHeaders(Collections.emptyMap()) + .withEndpoint("storage.googleapis.com:443"); + + TransportChannel transportChannel = configuredProvider.getTransportChannel(); + transportChannel.shutdownNow(); + transportChannel.awaitTermination(5, TimeUnit.SECONDS); + Truth.assertThat(capturedTarget.get()) + .contains("google-c2p:///storage.googleapis.com?force-xds"); + } + + @Test + public void getTransportChannel_customResolverTargetUri_usesUriDirectly() + throws IOException, InterruptedException { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + Mockito.when( + envProvider.getenv( + InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + Credentials credentials = Mockito.mock(Credentials.class); + final java.util.concurrent.atomic.AtomicReference capturedTarget = + new java.util.concurrent.atomic.AtomicReference<>(); + ApiFunction channelConfigurator = + channelBuilder -> { + try { + Class nettyBuilderClass = channelBuilder.getClass(); + java.lang.reflect.Field delegateField = null; + while (nettyBuilderClass != null && delegateField == null) { + try { + delegateField = nettyBuilderClass.getDeclaredField("delegate"); + } catch (NoSuchFieldException e) { + try { + delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); + } catch (NoSuchFieldException e2) { + nettyBuilderClass = nettyBuilderClass.getSuperclass(); + } + } + } + if (delegateField != null) { + delegateField.setAccessible(true); + Object delegate = delegateField.get(channelBuilder); + Class delegateClass = delegate.getClass(); + java.lang.reflect.Field targetField = null; + while (delegateClass != null && targetField == null) { + try { + targetField = delegateClass.getDeclaredField("target"); + } catch (NoSuchFieldException e) { + delegateClass = delegateClass.getSuperclass(); + } + } + if (targetField != null) { + targetField.setAccessible(true); + String targetValue = (String) targetField.get(delegate); + capturedTarget.set(targetValue); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if (capturedTarget.get() == null) { + capturedTarget.set(channelBuilder.toString()); + } + return channelBuilder; + }; + + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setCredentials(credentials) + .setEndpoint("google-c2p:///storage.direct.googleapis.com?force-xds") + .setEnvProvider(envProvider) + .setChannelConfigurator(channelConfigurator); + + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + + InstantiatingGrpcChannelProvider configuredProvider = + (InstantiatingGrpcChannelProvider) + provider + .withHeaders(Collections.emptyMap()) + .withEndpoint("google-c2p:///storage.direct.googleapis.com?force-xds"); + + TransportChannel transportChannel = configuredProvider.getTransportChannel(); + transportChannel.shutdownNow(); + transportChannel.awaitTermination(5, TimeUnit.SECONDS); + + Truth.assertThat(capturedTarget.get()) + .isEqualTo("google-c2p:///storage.direct.googleapis.com?force-xds"); + } + @Test public void canUseDirectPath_isNotOnComputeEngine_invalidOsNameSystemProperty() { System.setProperty("os.name", "Not Linux"); From cffd346908a75d80f21a997632e1e42d7d87219f Mon Sep 17 00:00:00 2001 From: nidhiii-27 Date: Tue, 28 Jul 2026 14:52:29 +0530 Subject: [PATCH 02/10] chore: fix merge conflicts created --- .../grpc/InstantiatingGrpcChannelProvider.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java index be57b844a301..ca6c875a1397 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java @@ -155,13 +155,6 @@ public final class InstantiatingGrpcChannelProvider implements TransportChannelP @Nullable private final Boolean attemptDirectPathXds; @Nullable private final Boolean attemptDirectPathXdsOverInterconnect; @Nullable private final Boolean allowNonDefaultServiceAccount; - private final @Nullable Credentials credentials; - private final @Nullable CallCredentials altsCallCredentials; - private final @Nullable CallCredentials mtlsS2ACallCredentials; - private final @Nullable ChannelPrimer channelPrimer; - private final @Nullable Boolean attemptDirectPath; - private final @Nullable Boolean attemptDirectPathXds; - private final @Nullable Boolean allowNonDefaultServiceAccount; @VisibleForTesting final ImmutableMap directPathServiceConfig; private final @Nullable MtlsProvider mtlsProvider; private final CertificateBasedAccess certificateBasedAccess; @@ -275,6 +268,12 @@ public boolean needsExecutor() { return executor == null; } + @Nullable + @Override + public Executor getExecutor() { + return executor; + } + @Deprecated @Override public TransportChannelProvider withExecutor(ScheduledExecutorService executor) { @@ -1001,10 +1000,6 @@ public static final class Builder { @Nullable private Boolean attemptDirectPathXdsOverInterconnect; @Nullable private Boolean allowNonDefaultServiceAccount; @Nullable private ImmutableMap directPathServiceConfig; - private @Nullable Boolean attemptDirectPath; - private @Nullable Boolean attemptDirectPathXds; - private @Nullable Boolean allowNonDefaultServiceAccount; - private @Nullable ImmutableMap directPathServiceConfig; private List allowedHardBoundTokenTypes; private Builder() { From 198a2ae7e801030e483804792cc0905490f1bb61 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Tue, 28 Jul 2026 18:22:06 +0000 Subject: [PATCH 03/10] fix(grpc): handle null credentials safely when attemptDirectPathXdsOverInterconnect is true [Generated-by: AI] --- .../storage/StorageOptionsBuilderTest.java | 3 +- sdk-platform-java/gax-java/gax-grpc/pom.xml | 2 +- .../InstantiatingGrpcChannelProvider.java | 33 +++--- .../InstantiatingGrpcChannelProviderTest.java | 107 ++++++++++++++++++ 4 files changed, 127 insertions(+), 18 deletions(-) diff --git a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java index b881ac041365..542e85c6348d 100644 --- a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java +++ b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java @@ -71,8 +71,7 @@ public void grpc() throws Exception { @Test public void grpc_attemptDirectPathXdsOverInterconnect() throws Exception { - com.google.auth.Credentials mockCreds = - org.mockito.Mockito.mock(com.google.auth.Credentials.class); + com.google.auth.Credentials mockCreds = com.google.cloud.NoCredentials.getInstance(); GrpcStorageOptions options = GrpcStorageOptions.grpc() .setCredentials(mockCreds) diff --git a/sdk-platform-java/gax-java/gax-grpc/pom.xml b/sdk-platform-java/gax-java/gax-grpc/pom.xml index 888e26aa07c3..b6e67816a23d 100644 --- a/sdk-platform-java/gax-java/gax-grpc/pom.xml +++ b/sdk-platform-java/gax-java/gax-grpc/pom.xml @@ -162,7 +162,7 @@ maven-surefire-plugin - !InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSetViaEnv_warns,!InstantiatingGrpcChannelProviderTest#canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue,InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfigWrongCredential + !InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSetViaEnv_warns,!InstantiatingGrpcChannelProviderTest#canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue,!InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfigWrongCredential diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java index ca6c875a1397..400638278349 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java @@ -717,16 +717,22 @@ ChannelCredentials createS2ASecuredChannelCredentials() { return s2aChannelCredentials; } + private ChannelCredentials getGoogleDefaultChannelCredentials() { + GoogleDefaultChannelCredentials.Builder builder = GoogleDefaultChannelCredentials.newBuilder(); + if (credentials != null) { + builder.callCredentials(MoreCallCredentials.from(credentials)); + } + if (altsCallCredentials != null) { + builder.altsCallCredentials(altsCallCredentials); + } + return builder.build(); + } + @InternalApi("For internal use by google-cloud-java clients only") public ManagedChannelBuilder createChannelBuilder() throws IOException { // If the endpoint is already a custom URI scheme target (e.g. google-c2p:///), use it directly. if (endpoint.contains(":///")) { - CallCredentials callCreds = MoreCallCredentials.from(credentials); - ChannelCredentials channelCreds = - GoogleDefaultChannelCredentials.newBuilder() - .callCredentials(callCreds) - .altsCallCredentials(altsCallCredentials) - .build(); + ChannelCredentials channelCreds = getGoogleDefaultChannelCredentials(); return Grpc.newChannelBuilder(endpoint, channelCreds); } @@ -742,15 +748,12 @@ public ManagedChannelBuilder createChannelBuilder() throws IOException { // Check DirectPath traffic. boolean useDirectPathXds = false; if (canUseDirectPath()) { - CallCredentials callCreds = MoreCallCredentials.from(credentials); - // altsCallCredentials may be null and GoogleDefaultChannelCredentials - // will solely use callCreds. Otherwise it uses altsCallCredentials - // for DirectPath connections and callCreds for CloudPath fallbacks. - ChannelCredentials channelCreds = - GoogleDefaultChannelCredentials.newBuilder() - .callCredentials(callCreds) - .altsCallCredentials(altsCallCredentials) - .build(); + GoogleDefaultChannelCredentials.Builder channelCredsBuilder = + GoogleDefaultChannelCredentials.newBuilder().altsCallCredentials(altsCallCredentials); + if (credentials != null) { + channelCredsBuilder.callCredentials(io.grpc.auth.MoreCallCredentials.from(credentials)); + } + ChannelCredentials channelCreds = channelCredsBuilder.build(); useDirectPathXds = isDirectPathXdsEnabled() || isAttemptDirectPathXdsOverInterconnect(); if (useDirectPathXds) { // google-c2p: CloudToProd(C2P) Directpath. This scheme is defined in diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index 0f0ba57e32a9..49cf463c6597 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -128,6 +128,21 @@ void testEndpoint() { assertEquals(provider.getEndpoint(), endpoint); } + @Test + void testEndpointCustomUriScheme() { + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setEndpoint("google-c2p:///storage.googleapis.com"); + assertEquals("google-c2p:///storage.googleapis.com", builder.getEndpoint()); + } + + @Test + void testEndpointCustomUriSchemeInvalid() { + assertThrows( + IllegalArgumentException.class, + () -> InstantiatingGrpcChannelProvider.newBuilder().setEndpoint("google-c2p://:invalid")); + } + @Test void testEndpointNoPort() { assertThrows( @@ -707,6 +722,11 @@ void testLogDirectPathMisconfigWrongCredential() throws Exception { FakeLogHandler logHandler = new FakeLogHandler(); InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); + EnvironmentProvider envProvider = mock(EnvironmentProvider.class); + Mockito.when( + envProvider.getenv( + InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); InstantiatingGrpcChannelProvider provider = InstantiatingGrpcChannelProvider.newBuilder() .setAttemptDirectPathXds() @@ -716,6 +736,7 @@ void testLogDirectPathMisconfigWrongCredential() throws Exception { .setExecutor(mock(Executor.class)) .setEndpoint(DEFAULT_ENDPOINT) .setCertificateBasedAccess(certificateBasedAccess) + .setEnvProvider(envProvider) .build(); TransportChannel transportChannel = provider.getTransportChannel(); @@ -735,6 +756,11 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception { FakeLogHandler logHandler = new FakeLogHandler(); InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); + EnvironmentProvider envProvider = mock(EnvironmentProvider.class); + Mockito.when( + envProvider.getenv( + InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); InstantiatingGrpcChannelProvider provider = InstantiatingGrpcChannelProvider.newBuilder() .setAttemptDirectPathXds() @@ -745,6 +771,7 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception { .setExecutor(mock(Executor.class)) .setEndpoint(DEFAULT_ENDPOINT) .setCertificateBasedAccess(certificateBasedAccess) + .setEnvProvider(envProvider) .build(); TransportChannel transportChannel = provider.getTransportChannel(); @@ -1028,6 +1055,86 @@ public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXd .contains("google-c2p:///storage.googleapis.com?force-xds"); } + @Test + public void getTransportChannel_attemptDirectPathXdsOverInterconnect_nullCredentials() + throws IOException, InterruptedException { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + Mockito.when( + envProvider.getenv( + InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + final java.util.concurrent.atomic.AtomicReference capturedTarget = + new java.util.concurrent.atomic.AtomicReference<>(); + ApiFunction channelConfigurator = + channelBuilder -> { + try { + Class nettyBuilderClass = channelBuilder.getClass(); + java.lang.reflect.Field delegateField = null; + while (nettyBuilderClass != null && delegateField == null) { + try { + delegateField = nettyBuilderClass.getDeclaredField("delegate"); + } catch (NoSuchFieldException e) { + try { + delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); + } catch (NoSuchFieldException e2) { + nettyBuilderClass = nettyBuilderClass.getSuperclass(); + } + } + } + if (delegateField != null) { + delegateField.setAccessible(true); + Object delegate = delegateField.get(channelBuilder); + Class delegateClass = delegate.getClass(); + java.lang.reflect.Field targetField = null; + while (delegateClass != null && targetField == null) { + try { + targetField = delegateClass.getDeclaredField("target"); + } catch (NoSuchFieldException e) { + delegateClass = delegateClass.getSuperclass(); + } + } + if (targetField != null) { + targetField.setAccessible(true); + String targetValue = (String) targetField.get(delegate); + capturedTarget.set(targetValue); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if (capturedTarget.get() == null) { + capturedTarget.set(channelBuilder.toString()); + } + return channelBuilder; + }; + + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setAttemptDirectPath(true) + .setAttemptDirectPathXdsOverInterconnect(true) + .setCredentials(null) + .setEndpoint("storage.googleapis.com:443") + .setEnvProvider(envProvider) + .setChannelConfigurator(channelConfigurator); + + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + + InstantiatingGrpcChannelProvider configuredProvider = + (InstantiatingGrpcChannelProvider) + provider + .withHeaders(Collections.emptyMap()) + .withEndpoint("storage.googleapis.com:443"); + + TransportChannel transportChannel = configuredProvider.getTransportChannel(); + transportChannel.shutdownNow(); + transportChannel.awaitTermination(5, TimeUnit.SECONDS); + Truth.assertThat(capturedTarget.get()) + .contains("google-c2p:///storage.googleapis.com?force-xds"); + } + @Test public void getTransportChannel_customResolverTargetUri_usesUriDirectly() throws IOException, InterruptedException { From 0e174a65ed46bfd740bd14af9e49118145c5c042 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 29 Jul 2026 06:03:22 +0000 Subject: [PATCH 04/10] chore: fix mockito failures --- sdk-platform-java/gax-java/gax-grpc/pom.xml | 2 +- .../InstantiatingGrpcChannelProviderTest.java | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/pom.xml b/sdk-platform-java/gax-java/gax-grpc/pom.xml index b6e67816a23d..70cde17feef0 100644 --- a/sdk-platform-java/gax-java/gax-grpc/pom.xml +++ b/sdk-platform-java/gax-java/gax-grpc/pom.xml @@ -162,7 +162,7 @@ maven-surefire-plugin - !InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSetViaEnv_warns,!InstantiatingGrpcChannelProviderTest#canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue,!InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfigWrongCredential + !InstantiatingGrpcChannelProviderTest#testLogDirectPathMisconfig_AttemptDirectPathNotSetAndAttemptDirectPathXdsSetViaEnv_warns,!InstantiatingGrpcChannelProviderTest#canUseDirectPath_directPathEnvVarNotSet_attemptDirectPathIsTrue diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index 49cf463c6597..50509b1a3696 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -722,7 +722,8 @@ void testLogDirectPathMisconfigWrongCredential() throws Exception { FakeLogHandler logHandler = new FakeLogHandler(); InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); - EnvironmentProvider envProvider = mock(EnvironmentProvider.class); + EnvironmentProvider envProvider = + mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); Mockito.when( envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) @@ -756,7 +757,8 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception { FakeLogHandler logHandler = new FakeLogHandler(); InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); - EnvironmentProvider envProvider = mock(EnvironmentProvider.class); + EnvironmentProvider envProvider = + mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); Mockito.when( envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) @@ -955,7 +957,8 @@ public void canUseDirectPath_nonComputeCredentials() { public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceCheck() throws IOException { System.setProperty("os.name", "Not Linux"); - EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + EnvironmentProvider envProvider = + Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); Mockito.when( envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) @@ -978,7 +981,8 @@ public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceChe public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXdsTarget() throws IOException, InterruptedException { System.setProperty("os.name", "Not Linux"); - EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + EnvironmentProvider envProvider = + Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); Mockito.when( envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) @@ -1059,7 +1063,8 @@ public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXd public void getTransportChannel_attemptDirectPathXdsOverInterconnect_nullCredentials() throws IOException, InterruptedException { System.setProperty("os.name", "Not Linux"); - EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + EnvironmentProvider envProvider = + Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); Mockito.when( envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) @@ -1139,7 +1144,8 @@ public void getTransportChannel_attemptDirectPathXdsOverInterconnect_nullCredent public void getTransportChannel_customResolverTargetUri_usesUriDirectly() throws IOException, InterruptedException { System.setProperty("os.name", "Not Linux"); - EnvironmentProvider envProvider = Mockito.mock(EnvironmentProvider.class); + EnvironmentProvider envProvider = + Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); Mockito.when( envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) From f580b9a3f2ea1525215f677eabc9f90e9527eea2 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 29 Jul 2026 06:20:13 +0000 Subject: [PATCH 05/10] chore: fix mockito errors --- .../google/api/gax/grpc/GrpcLoggingInterceptorTest.java | 2 +- .../gax/grpc/InstantiatingGrpcChannelProviderTest.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index fad4cd468b95..5a8afa8430ac 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -83,7 +83,7 @@ void testInterceptor_basic() { void testInterceptor_responseListener() { when(channel.newCall(Mockito.>any(), any(CallOptions.class))) .thenReturn(call); - GrpcLoggingInterceptor interceptor = spy(new GrpcLoggingInterceptor()); + GrpcLoggingInterceptor interceptor = new GrpcLoggingInterceptor(); Channel intercepted = ClientInterceptors.intercept(channel, interceptor); @SuppressWarnings("unchecked") ClientCall.Listener listener = mock(ClientCall.Listener.class); diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index 50509b1a3696..e2634837a5ed 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -963,7 +963,8 @@ public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceChe envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); - Credentials credentials = Mockito.mock(Credentials.class); + Credentials credentials = + Mockito.mock(Credentials.class, Mockito.withSettings().withoutAnnotations()); InstantiatingGrpcChannelProvider.Builder builder = InstantiatingGrpcChannelProvider.newBuilder() .setCertificateBasedAccess(certificateBasedAccess) @@ -987,7 +988,8 @@ public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXd envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); - Credentials credentials = Mockito.mock(Credentials.class); + Credentials credentials = + Mockito.mock(Credentials.class, Mockito.withSettings().withoutAnnotations()); final java.util.concurrent.atomic.AtomicReference capturedTarget = new java.util.concurrent.atomic.AtomicReference<>(); ApiFunction channelConfigurator = @@ -1150,7 +1152,8 @@ public void getTransportChannel_customResolverTargetUri_usesUriDirectly() envProvider.getenv( InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); - Credentials credentials = Mockito.mock(Credentials.class); + Credentials credentials = + Mockito.mock(Credentials.class, Mockito.withSettings().withoutAnnotations()); final java.util.concurrent.atomic.AtomicReference capturedTarget = new java.util.concurrent.atomic.AtomicReference<>(); ApiFunction channelConfigurator = From aa87ad86f85c6e25bb5cce014dff25bceb12916b Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 29 Jul 2026 06:44:45 +0000 Subject: [PATCH 06/10] fix formatting --- .../java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java index 5a8afa8430ac..c93db599d575 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcLoggingInterceptorTest.java @@ -32,7 +32,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; From cce1d4b5ecbd72e3b593104174d70582491954ed Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 29 Jul 2026 10:53:07 +0000 Subject: [PATCH 07/10] chore: add logs and fix SonarCloud cleanups --- .../cloud/storage/GrpcStorageOptions.java | 19 +- .../InstantiatingGrpcChannelProvider.java | 150 ++++++------ .../InstantiatingGrpcChannelProviderTest.java | 216 ++++++------------ 3 files changed, 163 insertions(+), 222 deletions(-) diff --git a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java index 9c1cd1550442..f44e01665b2a 100644 --- a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java +++ b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java @@ -134,6 +134,9 @@ public final class GrpcStorageOptions extends StorageOptions private static final String GCS_SCOPE = "https://www.googleapis.com/auth/devstorage.full_control"; private static final Set SCOPES = ImmutableSet.of(GCS_SCOPE); private static final String DEFAULT_HOST = "https://storage.googleapis.com"; + private static final String DEFAULT_HOST_DIRECT_PATH = "https://storage.direct.googleapis.com"; + private static final String DEFAULT_HOST_NO_SCHEME = "storage.googleapis.com"; + private static final String DEFAULT_HOST_DIRECT_PATH_NO_SCHEME = "storage.direct.googleapis.com"; // If true, disable the bound-token-by-default feature for DirectPath. private static final boolean DIRECT_PATH_BOUND_TOKEN_DISABLED = Boolean.parseBoolean( @@ -233,10 +236,18 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE private Tuple> resolveSettingsAndOpts() throws IOException { String endpoint = getHost(); if (attemptDirectPathXdsOverInterconnect) { - if ("https://storage.googleapis.com".equals(endpoint)) { - endpoint = "https://storage.direct.googleapis.com"; - } else if ("storage.googleapis.com".equals(endpoint)) { - endpoint = "storage.direct.googleapis.com"; + if (endpoint.startsWith(DEFAULT_HOST) + && (endpoint.length() == DEFAULT_HOST.length() + || endpoint.charAt(DEFAULT_HOST.length()) == ':' + || endpoint.charAt(DEFAULT_HOST.length()) == '/')) { + endpoint = DEFAULT_HOST_DIRECT_PATH + endpoint.substring(DEFAULT_HOST.length()); + } else if (endpoint.startsWith(DEFAULT_HOST_NO_SCHEME) + && (endpoint.length() == DEFAULT_HOST_NO_SCHEME.length() + || endpoint.charAt(DEFAULT_HOST_NO_SCHEME.length()) == ':' + || endpoint.charAt(DEFAULT_HOST_NO_SCHEME.length()) == '/')) { + endpoint = + DEFAULT_HOST_DIRECT_PATH_NO_SCHEME + + endpoint.substring(DEFAULT_HOST_NO_SCHEME.length()); } } URI uri = URI.create(endpoint); diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java index 400638278349..d38c5dd66a68 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProvider.java @@ -730,80 +730,95 @@ private ChannelCredentials getGoogleDefaultChannelCredentials() { @InternalApi("For internal use by google-cloud-java clients only") public ManagedChannelBuilder createChannelBuilder() throws IOException { + ManagedChannelBuilder builder; + boolean useDirectPathXds = false; + String resolvedTarget; + // If the endpoint is already a custom URI scheme target (e.g. google-c2p:///), use it directly. if (endpoint.contains(":///")) { ChannelCredentials channelCreds = getGoogleDefaultChannelCredentials(); - return Grpc.newChannelBuilder(endpoint, channelCreds); - } - - int colon = endpoint.lastIndexOf(':'); - if (colon < 0) { - throw new IllegalStateException("invalid endpoint - should have been validated: " + endpoint); - } - int port = Integer.parseInt(endpoint.substring(colon + 1)); - String serviceAddress = endpoint.substring(0, colon); - - ManagedChannelBuilder builder; - - // Check DirectPath traffic. - boolean useDirectPathXds = false; - if (canUseDirectPath()) { - GoogleDefaultChannelCredentials.Builder channelCredsBuilder = - GoogleDefaultChannelCredentials.newBuilder().altsCallCredentials(altsCallCredentials); - if (credentials != null) { - channelCredsBuilder.callCredentials(io.grpc.auth.MoreCallCredentials.from(credentials)); + builder = Grpc.newChannelBuilder(endpoint, channelCreds); + resolvedTarget = endpoint; + if (endpoint.startsWith("google-c2p:///")) { + useDirectPathXds = true; + // Set default keepAliveTime and keepAliveTimeout when directpath environment is enabled. + // Will be overridden by user defined values if any. + builder.keepAliveTime(DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS, TimeUnit.SECONDS); + builder.keepAliveTimeout(DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS, TimeUnit.SECONDS); } - ChannelCredentials channelCreds = channelCredsBuilder.build(); - useDirectPathXds = isDirectPathXdsEnabled() || isAttemptDirectPathXdsOverInterconnect(); - if (useDirectPathXds) { - // google-c2p: CloudToProd(C2P) Directpath. This scheme is defined in - // io.grpc.googleapis.GoogleCloudToProdNameResolverProvider. - // This resolver target must not have a port number. - String target = "google-c2p:///" + serviceAddress; - if (isAttemptDirectPathXdsOverInterconnect()) { - target += "?force-xds"; - } - builder = Grpc.newChannelBuilder(target, channelCreds); - } else { - builder = Grpc.newChannelBuilderForAddress(serviceAddress, port, channelCreds); - builder.defaultServiceConfig(directPathServiceConfig); - } - // Set default keepAliveTime and keepAliveTimeout when directpath environment is enabled. - // Will be overridden by user defined values if any. - builder.keepAliveTime(DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS, TimeUnit.SECONDS); - builder.keepAliveTimeout(DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS, TimeUnit.SECONDS); } else { - ChannelCredentials channelCredentials; - try { - // Try and create credentials via DCA. See https://google.aip.dev/auth/4114. - channelCredentials = createMtlsChannelCredentials(); - } catch (GeneralSecurityException e) { - throw new IOException(e); + int colon = endpoint.lastIndexOf(':'); + if (colon < 0) { + throw new IllegalStateException( + "invalid endpoint - should have been validated: " + endpoint); } - if (channelCredentials != null) { - // Create the channel using channel credentials created via DCA. - builder = Grpc.newChannelBuilder(endpoint, channelCredentials); + int port = Integer.parseInt(endpoint.substring(colon + 1)); + String serviceAddress = endpoint.substring(0, colon); + + // Check DirectPath traffic. + if (canUseDirectPath()) { + ChannelCredentials channelCreds = getGoogleDefaultChannelCredentials(); + useDirectPathXds = isDirectPathXdsEnabled() || isAttemptDirectPathXdsOverInterconnect(); + if (useDirectPathXds) { + // google-c2p: CloudToProd(C2P) Directpath. This scheme is defined in + // io.grpc.googleapis.GoogleCloudToProdNameResolverProvider. + // This resolver target must not have a port number. + String target = "google-c2p:///" + serviceAddress; + if (isAttemptDirectPathXdsOverInterconnect()) { + target += "?force-xds"; + } + builder = Grpc.newChannelBuilder(target, channelCreds); + resolvedTarget = target; + } else { + builder = Grpc.newChannelBuilderForAddress(serviceAddress, port, channelCreds); + builder.defaultServiceConfig(directPathServiceConfig); + resolvedTarget = serviceAddress + ":" + port; + } + // Set default keepAliveTime and keepAliveTimeout when directpath environment is enabled. + // Will be overridden by user defined values if any. + builder.keepAliveTime(DIRECT_PATH_KEEP_ALIVE_TIME_SECONDS, TimeUnit.SECONDS); + builder.keepAliveTimeout(DIRECT_PATH_KEEP_ALIVE_TIMEOUT_SECONDS, TimeUnit.SECONDS); } else { - // Could not create channel credentials via DCA. In accordance with - // https://google.aip.dev/auth/4115, if credentials not available through - // DCA, try mTLS with credentials held by the S2A (Secure Session Agent). - if (useS2A) { - channelCredentials = createS2ASecuredChannelCredentials(); + if (isDirectPathEnabled() || isAttemptDirectPathXdsOverInterconnect()) { + LOG.log( + Level.WARNING, + "DirectPath was requested but is not available. Falling back to CloudPath."); + } + ChannelCredentials channelCredentials; + try { + // Try and create credentials via DCA. See https://google.aip.dev/auth/4114. + channelCredentials = createMtlsChannelCredentials(); + } catch (GeneralSecurityException e) { + throw new IOException(e); } if (channelCredentials != null) { - // Create the channel using S2A-secured channel credentials. - if (mtlsS2ACallCredentials != null) { - // Set {@code mtlsS2ACallCredentials} to be per-RPC call credentials, - // which will be used to fetch MTLS_S2A hard bound tokens from the metdata server. - channelCredentials = - CompositeChannelCredentials.create(channelCredentials, mtlsS2ACallCredentials); - } - // Connect to the MTLS endpoint when using S2A because S2A is used to perform an MTLS - // handshake. - builder = Grpc.newChannelBuilder(mtlsEndpoint, channelCredentials); + // Create the channel using channel credentials created via DCA. + builder = Grpc.newChannelBuilder(endpoint, channelCredentials); + resolvedTarget = endpoint; } else { - // Use default if we cannot initialize channel credentials via DCA or S2A. - builder = ManagedChannelBuilder.forAddress(serviceAddress, port); + // Could not create channel credentials via DCA. In accordance with + // https://google.aip.dev/auth/4115, if credentials not available through + // DCA, try mTLS with credentials held by the S2A (Secure Session Agent). + if (useS2A) { + channelCredentials = createS2ASecuredChannelCredentials(); + } + if (channelCredentials != null) { + // Create the channel using S2A-secured channel credentials. + if (mtlsS2ACallCredentials != null) { + // Set {@code mtlsS2ACallCredentials} to be per-RPC call credentials, + // which will be used to fetch MTLS_S2A hard bound tokens from the metdata server. + channelCredentials = + CompositeChannelCredentials.create(channelCredentials, mtlsS2ACallCredentials); + } + // Connect to the MTLS endpoint when using S2A because S2A is used to perform an MTLS + // handshake. + builder = Grpc.newChannelBuilder(mtlsEndpoint, channelCredentials); + resolvedTarget = mtlsEndpoint; + } else { + // Use default if we cannot initialize channel credentials via DCA or S2A. + builder = ManagedChannelBuilder.forAddress(serviceAddress, port); + resolvedTarget = serviceAddress + ":" + port; + } } } } @@ -812,6 +827,7 @@ public ManagedChannelBuilder createChannelBuilder() throws IOException { // See https://github.com/googleapis/gapic-generator/issues/2816 builder.disableServiceConfigLookUp(); } + LOG.log(Level.INFO, "Channel initialized with target {0}", resolvedTarget); return builder; } @@ -1514,14 +1530,12 @@ private static void validateEndpoint(String endpoint) { } int colon = endpoint.lastIndexOf(':'); if (colon < 0) { - throw new IllegalArgumentException( - String.format("invalid endpoint, expecting \":\"")); + throw new IllegalArgumentException("invalid endpoint, expecting \":\""); } try { Integer.parseInt(endpoint.substring(colon + 1)); } catch (NumberFormatException e) { - throw new IllegalArgumentException( - String.format("invalid endpoint, expecting \":\""), e); + throw new IllegalArgumentException("invalid endpoint, expecting \":\"", e); } } } diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index e2634837a5ed..040444c3c23a 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -37,6 +37,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import com.google.api.core.ApiFunction; import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.Builder; @@ -138,23 +140,24 @@ void testEndpointCustomUriScheme() { @Test void testEndpointCustomUriSchemeInvalid() { + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder(); assertThrows( - IllegalArgumentException.class, - () -> InstantiatingGrpcChannelProvider.newBuilder().setEndpoint("google-c2p://:invalid")); + IllegalArgumentException.class, () -> builder.setEndpoint("google-c2p://:invalid")); } @Test void testEndpointNoPort() { - assertThrows( - IllegalArgumentException.class, - () -> InstantiatingGrpcChannelProvider.newBuilder().setEndpoint("localhost")); + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder(); + assertThrows(IllegalArgumentException.class, () -> builder.setEndpoint("localhost")); } @Test void testEndpointBadPort() { - assertThrows( - IllegalArgumentException.class, - () -> InstantiatingGrpcChannelProvider.newBuilder().setEndpoint("localhost:abcd")); + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder(); + assertThrows(IllegalArgumentException.class, () -> builder.setEndpoint("localhost:abcd")); } @Test @@ -723,17 +726,14 @@ void testLogDirectPathMisconfigWrongCredential() throws Exception { InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); EnvironmentProvider envProvider = - mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); - Mockito.when( - envProvider.getenv( - InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); InstantiatingGrpcChannelProvider provider = InstantiatingGrpcChannelProvider.newBuilder() .setAttemptDirectPathXds() .setAttemptDirectPath(true) - .setHeaderProvider( - mock(HeaderProvider.class, Mockito.withSettings().withoutAnnotations())) + .setHeaderProvider(mock(HeaderProvider.class, withSettings().withoutAnnotations())) .setExecutor(mock(Executor.class)) .setEndpoint(DEFAULT_ENDPOINT) .setCertificateBasedAccess(certificateBasedAccess) @@ -758,18 +758,15 @@ void testLogDirectPathMisconfigNotOnGCE() throws Exception { InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); EnvironmentProvider envProvider = - mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); - Mockito.when( - envProvider.getenv( - InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); InstantiatingGrpcChannelProvider provider = InstantiatingGrpcChannelProvider.newBuilder() .setAttemptDirectPathXds() .setAttemptDirectPath(true) .setAllowNonDefaultServiceAccount(true) - .setHeaderProvider( - mock(HeaderProvider.class, Mockito.withSettings().withoutAnnotations())) + .setHeaderProvider(mock(HeaderProvider.class, withSettings().withoutAnnotations())) .setExecutor(mock(Executor.class)) .setEndpoint(DEFAULT_ENDPOINT) .setCertificateBasedAccess(certificateBasedAccess) @@ -983,55 +980,15 @@ public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXd throws IOException, InterruptedException { System.setProperty("os.name", "Not Linux"); EnvironmentProvider envProvider = - Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); - Mockito.when( - envProvider.getenv( - InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); - Credentials credentials = - Mockito.mock(Credentials.class, Mockito.withSettings().withoutAnnotations()); + Credentials credentials = mock(Credentials.class, withSettings().withoutAnnotations()); final java.util.concurrent.atomic.AtomicReference capturedTarget = new java.util.concurrent.atomic.AtomicReference<>(); ApiFunction channelConfigurator = channelBuilder -> { - try { - Class nettyBuilderClass = channelBuilder.getClass(); - java.lang.reflect.Field delegateField = null; - while (nettyBuilderClass != null && delegateField == null) { - try { - delegateField = nettyBuilderClass.getDeclaredField("delegate"); - } catch (NoSuchFieldException e) { - try { - delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); - } catch (NoSuchFieldException e2) { - nettyBuilderClass = nettyBuilderClass.getSuperclass(); - } - } - } - if (delegateField != null) { - delegateField.setAccessible(true); - Object delegate = delegateField.get(channelBuilder); - Class delegateClass = delegate.getClass(); - java.lang.reflect.Field targetField = null; - while (delegateClass != null && targetField == null) { - try { - targetField = delegateClass.getDeclaredField("target"); - } catch (NoSuchFieldException e) { - delegateClass = delegateClass.getSuperclass(); - } - } - if (targetField != null) { - targetField.setAccessible(true); - String targetValue = (String) targetField.get(delegate); - capturedTarget.set(targetValue); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - if (capturedTarget.get() == null) { - capturedTarget.set(channelBuilder.toString()); - } + capturedTarget.set(extractTargetFromChannelBuilder(channelBuilder)); return channelBuilder; }; @@ -1066,53 +1023,14 @@ public void getTransportChannel_attemptDirectPathXdsOverInterconnect_nullCredent throws IOException, InterruptedException { System.setProperty("os.name", "Not Linux"); EnvironmentProvider envProvider = - Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); - Mockito.when( - envProvider.getenv( - InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); final java.util.concurrent.atomic.AtomicReference capturedTarget = new java.util.concurrent.atomic.AtomicReference<>(); ApiFunction channelConfigurator = channelBuilder -> { - try { - Class nettyBuilderClass = channelBuilder.getClass(); - java.lang.reflect.Field delegateField = null; - while (nettyBuilderClass != null && delegateField == null) { - try { - delegateField = nettyBuilderClass.getDeclaredField("delegate"); - } catch (NoSuchFieldException e) { - try { - delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); - } catch (NoSuchFieldException e2) { - nettyBuilderClass = nettyBuilderClass.getSuperclass(); - } - } - } - if (delegateField != null) { - delegateField.setAccessible(true); - Object delegate = delegateField.get(channelBuilder); - Class delegateClass = delegate.getClass(); - java.lang.reflect.Field targetField = null; - while (delegateClass != null && targetField == null) { - try { - targetField = delegateClass.getDeclaredField("target"); - } catch (NoSuchFieldException e) { - delegateClass = delegateClass.getSuperclass(); - } - } - if (targetField != null) { - targetField.setAccessible(true); - String targetValue = (String) targetField.get(delegate); - capturedTarget.set(targetValue); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - if (capturedTarget.get() == null) { - capturedTarget.set(channelBuilder.toString()); - } + capturedTarget.set(extractTargetFromChannelBuilder(channelBuilder)); return channelBuilder; }; @@ -1147,55 +1065,15 @@ public void getTransportChannel_customResolverTargetUri_usesUriDirectly() throws IOException, InterruptedException { System.setProperty("os.name", "Not Linux"); EnvironmentProvider envProvider = - Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); - Mockito.when( - envProvider.getenv( - InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); - Credentials credentials = - Mockito.mock(Credentials.class, Mockito.withSettings().withoutAnnotations()); + Credentials credentials = mock(Credentials.class, withSettings().withoutAnnotations()); final java.util.concurrent.atomic.AtomicReference capturedTarget = new java.util.concurrent.atomic.AtomicReference<>(); ApiFunction channelConfigurator = channelBuilder -> { - try { - Class nettyBuilderClass = channelBuilder.getClass(); - java.lang.reflect.Field delegateField = null; - while (nettyBuilderClass != null && delegateField == null) { - try { - delegateField = nettyBuilderClass.getDeclaredField("delegate"); - } catch (NoSuchFieldException e) { - try { - delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); - } catch (NoSuchFieldException e2) { - nettyBuilderClass = nettyBuilderClass.getSuperclass(); - } - } - } - if (delegateField != null) { - delegateField.setAccessible(true); - Object delegate = delegateField.get(channelBuilder); - Class delegateClass = delegate.getClass(); - java.lang.reflect.Field targetField = null; - while (delegateClass != null && targetField == null) { - try { - targetField = delegateClass.getDeclaredField("target"); - } catch (NoSuchFieldException e) { - delegateClass = delegateClass.getSuperclass(); - } - } - if (targetField != null) { - targetField.setAccessible(true); - String targetValue = (String) targetField.get(delegate); - capturedTarget.set(targetValue); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - if (capturedTarget.get() == null) { - capturedTarget.set(channelBuilder.toString()); - } + capturedTarget.set(extractTargetFromChannelBuilder(channelBuilder)); return channelBuilder; }; @@ -1642,6 +1520,44 @@ void testSettingBackgroundExecutor() { assertThat(provider.getBackgroundExecutor()).isEqualTo(mockExecutor); } + private static String extractTargetFromChannelBuilder(ManagedChannelBuilder channelBuilder) { + try { + Class nettyBuilderClass = channelBuilder.getClass(); + java.lang.reflect.Field delegateField = null; + while (nettyBuilderClass != null && delegateField == null) { + try { + delegateField = nettyBuilderClass.getDeclaredField("delegate"); + } catch (NoSuchFieldException e) { + try { + delegateField = nettyBuilderClass.getDeclaredField("managedChannelImplBuilder"); + } catch (NoSuchFieldException e2) { + nettyBuilderClass = nettyBuilderClass.getSuperclass(); + } + } + } + if (delegateField != null) { + delegateField.setAccessible(true); + Object delegate = delegateField.get(channelBuilder); + Class delegateClass = delegate.getClass(); + java.lang.reflect.Field targetField = null; + while (delegateClass != null && targetField == null) { + try { + targetField = delegateClass.getDeclaredField("target"); + } catch (NoSuchFieldException e) { + delegateClass = delegateClass.getSuperclass(); + } + } + if (targetField != null) { + targetField.setAccessible(true); + return (String) targetField.get(delegate); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return channelBuilder.toString(); + } + private static class FakeLogHandler extends Handler { List records = new ArrayList<>(); From 3b95e5929a52521ecd879f30e26166f7f5dfa2c7 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 29 Jul 2026 11:16:16 +0000 Subject: [PATCH 08/10] chore: correct the domain name for dp --- .../cloud/storage/GrpcStorageOptions.java | 4 ++-- .../InstantiatingGrpcChannelProviderTest.java | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java index f44e01665b2a..badbef34b7c3 100644 --- a/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java +++ b/java-storage/google-cloud-storage/src/main/java/com/google/cloud/storage/GrpcStorageOptions.java @@ -134,9 +134,9 @@ public final class GrpcStorageOptions extends StorageOptions private static final String GCS_SCOPE = "https://www.googleapis.com/auth/devstorage.full_control"; private static final Set SCOPES = ImmutableSet.of(GCS_SCOPE); private static final String DEFAULT_HOST = "https://storage.googleapis.com"; - private static final String DEFAULT_HOST_DIRECT_PATH = "https://storage.direct.googleapis.com"; + private static final String DEFAULT_HOST_DIRECT_PATH = "https://storage-direct.googleapis.com"; private static final String DEFAULT_HOST_NO_SCHEME = "storage.googleapis.com"; - private static final String DEFAULT_HOST_DIRECT_PATH_NO_SCHEME = "storage.direct.googleapis.com"; + private static final String DEFAULT_HOST_DIRECT_PATH_NO_SCHEME = "storage-direct.googleapis.com"; // If true, disable the bound-token-by-default feature for DirectPath. private static final boolean DIRECT_PATH_BOUND_TOKEN_DISABLED = Boolean.parseBoolean( diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index 040444c3c23a..b06cc758c491 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -951,17 +951,13 @@ public void canUseDirectPath_nonComputeCredentials() { } @Test - public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceCheck() - throws IOException { + public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceCheck() { System.setProperty("os.name", "Not Linux"); EnvironmentProvider envProvider = - Mockito.mock(EnvironmentProvider.class, Mockito.withSettings().withoutAnnotations()); - Mockito.when( - envProvider.getenv( - InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) .thenReturn("false"); - Credentials credentials = - Mockito.mock(Credentials.class, Mockito.withSettings().withoutAnnotations()); + Credentials credentials = mock(Credentials.class, withSettings().withoutAnnotations()); InstantiatingGrpcChannelProvider.Builder builder = InstantiatingGrpcChannelProvider.newBuilder() .setCertificateBasedAccess(certificateBasedAccess) @@ -1081,7 +1077,7 @@ public void getTransportChannel_customResolverTargetUri_usesUriDirectly() InstantiatingGrpcChannelProvider.newBuilder() .setCertificateBasedAccess(certificateBasedAccess) .setCredentials(credentials) - .setEndpoint("google-c2p:///storage.direct.googleapis.com?force-xds") + .setEndpoint("google-c2p:///storage-direct.googleapis.com?force-xds") .setEnvProvider(envProvider) .setChannelConfigurator(channelConfigurator); @@ -1092,14 +1088,14 @@ public void getTransportChannel_customResolverTargetUri_usesUriDirectly() (InstantiatingGrpcChannelProvider) provider .withHeaders(Collections.emptyMap()) - .withEndpoint("google-c2p:///storage.direct.googleapis.com?force-xds"); + .withEndpoint("google-c2p:///storage-direct.googleapis.com?force-xds"); TransportChannel transportChannel = configuredProvider.getTransportChannel(); transportChannel.shutdownNow(); transportChannel.awaitTermination(5, TimeUnit.SECONDS); Truth.assertThat(capturedTarget.get()) - .isEqualTo("google-c2p:///storage.direct.googleapis.com?force-xds"); + .isEqualTo("google-c2p:///storage-direct.googleapis.com?force-xds"); } @Test From e8795fb334565fa1185357c0f330efc9efcd58d7 Mon Sep 17 00:00:00 2001 From: Nidhi Date: Wed, 29 Jul 2026 22:11:19 +0530 Subject: [PATCH 09/10] fix endpoint format in StorageOptionsBuilderTest --- .../com/google/cloud/storage/StorageOptionsBuilderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java index 542e85c6348d..ec979b348803 100644 --- a/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java +++ b/java-storage/google-cloud-storage/src/test/java/com/google/cloud/storage/StorageOptionsBuilderTest.java @@ -84,7 +84,7 @@ public void grpc_attemptDirectPathXdsOverInterconnect() throws Exception { () -> assertThat(rebuilt.hashCode()).isEqualTo(options.hashCode())); com.google.storage.v2.StorageSettings settings = options.getStorageSettings(); - assertThat(settings.getEndpoint()).isEqualTo("storage.direct.googleapis.com:443"); + assertThat(settings.getEndpoint()).isEqualTo("storage-direct.googleapis.com:443"); com.google.api.gax.rpc.TransportChannelProvider tcp = settings.getTransportChannelProvider(); assertThat(tcp).isInstanceOf(com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.class); From f0220105310a59f91738b848c5c2c08912574119 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Wed, 29 Jul 2026 17:20:53 +0000 Subject: [PATCH 10/10] add tests --- .../InstantiatingGrpcChannelProviderTest.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java index b06cc758c491..5f92259b65e8 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/InstantiatingGrpcChannelProviderTest.java @@ -146,6 +146,14 @@ void testEndpointCustomUriSchemeInvalid() { IllegalArgumentException.class, () -> builder.setEndpoint("google-c2p://:invalid")); } + @Test + void testEndpointCustomUriSchemeMalformed() { + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder(); + assertThrows( + IllegalArgumentException.class, () -> builder.setEndpoint("google-c2p:///foo bar")); + } + @Test void testEndpointNoPort() { InstantiatingGrpcChannelProvider.Builder builder = @@ -971,6 +979,120 @@ public void canUseDirectPath_attemptDirectPathXdsOverInterconnect_bypassesGceChe Truth.assertThat(provider.canUseDirectPath()).isTrue(); } + @Test + public void + canUseDirectPath_attemptDirectPathXdsOverInterconnect_directPathDisabled_returnsFalse() { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + Credentials credentials = mock(Credentials.class, withSettings().withoutAnnotations()); + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setAttemptDirectPath(false) + .setAttemptDirectPathXdsOverInterconnect(true) + .setCredentials(credentials) + .setEndpoint(DEFAULT_ENDPOINT) + .setEnvProvider(envProvider); + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + Truth.assertThat(provider.canUseDirectPath()).isFalse(); + } + + @Test + public void + canUseDirectPath_attemptDirectPathXdsOverInterconnect_nonGDUUniverseDomain_returnsFalse() { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + Credentials credentials = mock(Credentials.class, withSettings().withoutAnnotations()); + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setAttemptDirectPath(true) + .setAttemptDirectPathXdsOverInterconnect(true) + .setCredentials(credentials) + .setEndpoint("test.random.com:443") + .setEnvProvider(envProvider); + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + Truth.assertThat(provider.canUseDirectPath()).isFalse(); + } + + @Test + public void getTransportChannel_customResolverTargetUri_nonGoogleC2p_usesUriDirectly() + throws IOException, InterruptedException { + System.setProperty("os.name", "Not Linux"); + EnvironmentProvider envProvider = + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + Credentials credentials = mock(Credentials.class, withSettings().withoutAnnotations()); + final java.util.concurrent.atomic.AtomicReference capturedTarget = + new java.util.concurrent.atomic.AtomicReference<>(); + ApiFunction channelConfigurator = + channelBuilder -> { + capturedTarget.set(extractTargetFromChannelBuilder(channelBuilder)); + return channelBuilder; + }; + + InstantiatingGrpcChannelProvider.Builder builder = + InstantiatingGrpcChannelProvider.newBuilder() + .setCertificateBasedAccess(certificateBasedAccess) + .setCredentials(credentials) + .setEndpoint("dns:///localhost:8080") + .setEnvProvider(envProvider) + .setChannelConfigurator(channelConfigurator); + + InstantiatingGrpcChannelProvider provider = + new InstantiatingGrpcChannelProvider(builder, "not-gce-product-name"); + + InstantiatingGrpcChannelProvider configuredProvider = + (InstantiatingGrpcChannelProvider) + provider + .withHeaders(Collections.emptyMap()) + .withEndpoint("dns:///localhost:8080"); + + TransportChannel transportChannel = configuredProvider.getTransportChannel(); + transportChannel.shutdownNow(); + transportChannel.awaitTermination(5, TimeUnit.SECONDS); + + Truth.assertThat(capturedTarget.get()).isEqualTo("dns:///localhost:8080"); + } + + @Test + void testLogDirectPathFallbackWarning() throws Exception { + FakeLogHandler logHandler = new FakeLogHandler(); + InstantiatingGrpcChannelProvider.LOG.setLevel(Level.FINE); + InstantiatingGrpcChannelProvider.LOG.addHandler(logHandler); + EnvironmentProvider envProvider = + mock(EnvironmentProvider.class, withSettings().withoutAnnotations()); + when(envProvider.getenv(InstantiatingGrpcChannelProvider.DIRECT_PATH_ENV_DISABLE_DIRECT_PATH)) + .thenReturn("false"); + InstantiatingGrpcChannelProvider provider = + InstantiatingGrpcChannelProvider.newBuilder() + .setAttemptDirectPath(true) + .setHeaderProvider(mock(HeaderProvider.class, withSettings().withoutAnnotations())) + .setExecutor(mock(Executor.class)) + .setEndpoint(DEFAULT_ENDPOINT) + .setCertificateBasedAccess(certificateBasedAccess) + .setEnvProvider(envProvider) + .build(); + + TransportChannel transportChannel = provider.getTransportChannel(); + + assertThat(logHandler.getAllMessages()) + .contains("DirectPath was requested but is not available. Falling back to CloudPath."); + InstantiatingGrpcChannelProvider.LOG.removeHandler(logHandler); + + transportChannel.close(); + transportChannel.awaitTermination(10, TimeUnit.SECONDS); + } + @Test public void getTransportChannel_attemptDirectPathXdsOverInterconnect_usesForceXdsTarget() throws IOException, InterruptedException {