@@ -134,6 +134,9 @@ public final class GrpcStorageOptions extends StorageOptions
134134 private static final String GCS_SCOPE = "https://www.googleapis.com/auth/devstorage.full_control" ;
135135 private static final Set <String > SCOPES = ImmutableSet .of (GCS_SCOPE );
136136 private static final String DEFAULT_HOST = "https://storage.googleapis.com" ;
137+ private static final String DEFAULT_HOST_DIRECT_PATH = "https://storage-direct.googleapis.com" ;
138+ private static final String DEFAULT_HOST_NO_SCHEME = "storage.googleapis.com" ;
139+ private static final String DEFAULT_HOST_DIRECT_PATH_NO_SCHEME = "storage-direct.googleapis.com" ;
137140 // If true, disable the bound-token-by-default feature for DirectPath.
138141 private static final boolean DIRECT_PATH_BOUND_TOKEN_DISABLED =
139142 Boolean .parseBoolean (
@@ -142,6 +145,7 @@ public final class GrpcStorageOptions extends StorageOptions
142145 private final GrpcRetryAlgorithmManager retryAlgorithmManager ;
143146 private final java .time .Duration terminationAwaitDuration ;
144147 private final boolean attemptDirectPath ;
148+ private final boolean attemptDirectPathXdsOverInterconnect ;
145149 private final boolean enableGrpcClientMetrics ;
146150
147151 private final boolean grpcClientMetricsManuallyEnabled ;
@@ -160,6 +164,7 @@ private GrpcStorageOptions(Builder builder, GrpcStorageDefaults serviceDefaults)
160164 builder .terminationAwaitDuration ,
161165 serviceDefaults .getTerminationAwaitDurationJavaTime ());
162166 this .attemptDirectPath = builder .attemptDirectPath ;
167+ this .attemptDirectPathXdsOverInterconnect = builder .attemptDirectPathXdsOverInterconnect ;
163168 this .enableGrpcClientMetrics = builder .enableGrpcClientMetrics ;
164169 this .grpcClientMetricsManuallyEnabled = builder .grpcMetricsManuallyEnabled ;
165170 this .grpcInterceptorProvider = builder .grpcInterceptorProvider ;
@@ -230,6 +235,21 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE
230235 */
231236 private Tuple <StorageSettings , Opts <UserProject >> resolveSettingsAndOpts () throws IOException {
232237 String endpoint = getHost ();
238+ if (attemptDirectPathXdsOverInterconnect ) {
239+ if (endpoint .startsWith (DEFAULT_HOST )
240+ && (endpoint .length () == DEFAULT_HOST .length ()
241+ || endpoint .charAt (DEFAULT_HOST .length ()) == ':'
242+ || endpoint .charAt (DEFAULT_HOST .length ()) == '/' )) {
243+ endpoint = DEFAULT_HOST_DIRECT_PATH + endpoint .substring (DEFAULT_HOST .length ());
244+ } else if (endpoint .startsWith (DEFAULT_HOST_NO_SCHEME )
245+ && (endpoint .length () == DEFAULT_HOST_NO_SCHEME .length ()
246+ || endpoint .charAt (DEFAULT_HOST_NO_SCHEME .length ()) == ':'
247+ || endpoint .charAt (DEFAULT_HOST_NO_SCHEME .length ()) == '/' )) {
248+ endpoint =
249+ DEFAULT_HOST_DIRECT_PATH_NO_SCHEME
250+ + endpoint .substring (DEFAULT_HOST_NO_SCHEME .length ());
251+ }
252+ }
233253 URI uri = URI .create (endpoint );
234254 String scheme = uri .getScheme ();
235255 int port = uri .getPort ();
@@ -322,7 +342,8 @@ private Tuple<StorageSettings, Opts<UserProject>> resolveSettingsAndOpts() throw
322342 InstantiatingGrpcChannelProvider .newBuilder ()
323343 .setEndpoint (endpoint )
324344 .setAllowNonDefaultServiceAccount (true )
325- .setAttemptDirectPath (attemptDirectPath );
345+ .setAttemptDirectPath (attemptDirectPath || attemptDirectPathXdsOverInterconnect )
346+ .setAttemptDirectPathXdsOverInterconnect (attemptDirectPathXdsOverInterconnect );
326347
327348 if (!DIRECT_PATH_BOUND_TOKEN_DISABLED ) {
328349 channelProviderBuilder .setAllowHardBoundTokenTypes (
@@ -337,6 +358,18 @@ private Tuple<StorageSettings, Opts<UserProject>> resolveSettingsAndOpts() throw
337358 channelProviderBuilder .setAttemptDirectPathXds ();
338359 }
339360
361+ if (attemptDirectPathXdsOverInterconnect ) {
362+ com .google .api .core .ApiFunction <ManagedChannelBuilder , ManagedChannelBuilder >
363+ existingConfigurator = channelProviderBuilder .getChannelConfigurator ();
364+ channelProviderBuilder .setChannelConfigurator (
365+ channelBuilder -> {
366+ if (existingConfigurator != null ) {
367+ channelBuilder = existingConfigurator .apply (channelBuilder );
368+ }
369+ return channelBuilder .overrideAuthority ("storage.googleapis.com" );
370+ });
371+ }
372+
340373 if (scheme .equals ("http" )) {
341374 channelProviderBuilder .setChannelConfigurator (ManagedChannelBuilder ::usePlaintext );
342375 }
@@ -428,6 +461,7 @@ public int hashCode() {
428461 retryAlgorithmManager ,
429462 terminationAwaitDuration ,
430463 attemptDirectPath ,
464+ attemptDirectPathXdsOverInterconnect ,
431465 enableGrpcClientMetrics ,
432466 grpcInterceptorProvider ,
433467 blobWriteSessionConfig ,
@@ -445,6 +479,7 @@ public boolean equals(Object o) {
445479 }
446480 GrpcStorageOptions that = (GrpcStorageOptions ) o ;
447481 return attemptDirectPath == that .attemptDirectPath
482+ && attemptDirectPathXdsOverInterconnect == that .attemptDirectPathXdsOverInterconnect
448483 && enableGrpcClientMetrics == that .enableGrpcClientMetrics
449484 && Objects .equals (retryAlgorithmManager , that .retryAlgorithmManager )
450485 && Objects .equals (terminationAwaitDuration , that .terminationAwaitDuration )
@@ -494,6 +529,7 @@ public static final class Builder extends StorageOptions.Builder {
494529 private StorageRetryStrategy storageRetryStrategy ;
495530 private java .time .Duration terminationAwaitDuration ;
496531 private boolean attemptDirectPath = GrpcStorageDefaults .INSTANCE .isAttemptDirectPath ();
532+ private boolean attemptDirectPathXdsOverInterconnect = false ;
497533 private boolean enableGrpcClientMetrics =
498534 GrpcStorageDefaults .INSTANCE .isEnableGrpcClientMetrics ();
499535 private GrpcInterceptorProvider grpcInterceptorProvider =
@@ -512,6 +548,7 @@ public static final class Builder extends StorageOptions.Builder {
512548 this .storageRetryStrategy = gso .getRetryAlgorithmManager ().retryStrategy ;
513549 this .terminationAwaitDuration = gso .getTerminationAwaitDuration ();
514550 this .attemptDirectPath = gso .attemptDirectPath ;
551+ this .attemptDirectPathXdsOverInterconnect = gso .attemptDirectPathXdsOverInterconnect ;
515552 this .enableGrpcClientMetrics = gso .enableGrpcClientMetrics ;
516553 this .grpcInterceptorProvider = gso .grpcInterceptorProvider ;
517554 this .blobWriteSessionConfig = gso .blobWriteSessionConfig ;
@@ -556,6 +593,18 @@ public GrpcStorageOptions.Builder setAttemptDirectPath(boolean attemptDirectPath
556593 return this ;
557594 }
558595
596+ /**
597+ * Option for whether this client should attempt to use DirectPath over Interconnect (on-premise
598+ * xDS name resolution).
599+ *
600+ * @since 2.45.0
601+ */
602+ public GrpcStorageOptions .Builder setAttemptDirectPathXdsOverInterconnect (
603+ boolean attemptDirectPathXdsOverInterconnect ) {
604+ this .attemptDirectPathXdsOverInterconnect = attemptDirectPathXdsOverInterconnect ;
605+ return this ;
606+ }
607+
559608 /**
560609 * Option for whether this client should emit internal gRPC client internal metrics to Cloud
561610 * Monitoring. To disable metric reporting, set this to false. True by default. Emitting metrics
0 commit comments