2424import com .google .api .gax .rpc .TransportChannelProvider ;
2525import com .google .auth .Credentials ;
2626import com .google .cloud .bigtable .data .v2 .internal .csm .tracers .ChannelPoolMetricsTracer ;
27+ import com .google .cloud .bigtable .data .v2 .stub .EnhancedBigtableStubSettings ;
2728import com .google .common .base .Preconditions ;
29+ import io .grpc .Channel ;
2830import io .grpc .ManagedChannel ;
2931import java .io .IOException ;
3032import java .util .Map ;
3133import java .util .concurrent .Executor ;
3234import java .util .concurrent .ScheduledExecutorService ;
35+ import java .util .logging .Level ;
36+ import java .util .logging .Logger ;
3337import javax .annotation .Nullable ;
3438
3539/**
3842 */
3943@ InternalApi
4044public final class BigtableTransportChannelProvider implements TransportChannelProvider {
45+ private static final Logger LOG =
46+ Logger .getLogger (BigtableTransportChannelProvider .class .getName ());
4147 private final InstantiatingGrpcChannelProvider delegate ;
4248 private final ChannelPrimer channelPrimer ;
4349 @ Nullable private final ChannelPoolMetricsTracer channelPoolMetricsTracer ;
4450 @ Nullable private final ScheduledExecutorService backgroundExecutor ;
51+ @ Nullable private final Map <String , String > headers ;
4552
4653 private BigtableTransportChannelProvider (
4754 InstantiatingGrpcChannelProvider instantiatingGrpcChannelProvider ,
4855 ChannelPrimer channelPrimer ,
4956 ChannelPoolMetricsTracer channelPoolMetricsTracer ,
50- ScheduledExecutorService backgroundExecutor ) {
57+ ScheduledExecutorService backgroundExecutor ,
58+ @ Nullable Map <String , String > headers ) {
5159 delegate = Preconditions .checkNotNull (instantiatingGrpcChannelProvider );
5260 this .channelPrimer = channelPrimer ;
5361 this .channelPoolMetricsTracer = channelPoolMetricsTracer ;
5462 this .backgroundExecutor = backgroundExecutor ;
63+ this .headers = headers ;
5564 }
5665
5766 @ Override
@@ -76,7 +85,7 @@ public BigtableTransportChannelProvider withExecutor(Executor executor) {
7685 InstantiatingGrpcChannelProvider newChannelProvider =
7786 (InstantiatingGrpcChannelProvider ) delegate .withExecutor (executor );
7887 return new BigtableTransportChannelProvider (
79- newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor );
88+ newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor , headers );
8089 }
8190
8291 @ Override
@@ -89,7 +98,7 @@ public TransportChannelProvider withBackgroundExecutor(ScheduledExecutorService
8998 InstantiatingGrpcChannelProvider newChannelProvider =
9099 (InstantiatingGrpcChannelProvider ) delegate .withBackgroundExecutor (executor );
91100 return new BigtableTransportChannelProvider (
92- newChannelProvider , channelPrimer , channelPoolMetricsTracer , executor );
101+ newChannelProvider , channelPrimer , channelPoolMetricsTracer , executor , headers );
93102 }
94103
95104 @ Override
@@ -102,7 +111,7 @@ public BigtableTransportChannelProvider withHeaders(Map<String, String> headers)
102111 InstantiatingGrpcChannelProvider newChannelProvider =
103112 (InstantiatingGrpcChannelProvider ) delegate .withHeaders (headers );
104113 return new BigtableTransportChannelProvider (
105- newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor );
114+ newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor , headers );
106115 }
107116
108117 @ Override
@@ -115,7 +124,7 @@ public TransportChannelProvider withEndpoint(String endpoint) {
115124 InstantiatingGrpcChannelProvider newChannelProvider =
116125 (InstantiatingGrpcChannelProvider ) delegate .withEndpoint (endpoint );
117126 return new BigtableTransportChannelProvider (
118- newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor );
127+ newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor , headers );
119128 }
120129
121130 @ Deprecated
@@ -130,20 +139,83 @@ public TransportChannelProvider withPoolSize(int size) {
130139 InstantiatingGrpcChannelProvider newChannelProvider =
131140 (InstantiatingGrpcChannelProvider ) delegate .withPoolSize (size );
132141 return new BigtableTransportChannelProvider (
133- newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor );
142+ newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor , headers );
143+ }
144+
145+ // We need this for direct access checker.
146+ private Map <String , String > updateFeatureFlags (
147+ Map <String , String > originalHeaders , boolean isDirectAccessEligible ) {
148+ if (originalHeaders == null ) {
149+ return java .util .Collections .emptyMap ();
150+ }
151+ java .util .Map <String , String > newHeaders = new java .util .HashMap <>(originalHeaders );
152+ String encodedFlags = newHeaders .get ("bigtable-features" );
153+
154+ if (encodedFlags != null ) {
155+ try {
156+ byte [] decoded = java .util .Base64 .getUrlDecoder ().decode (encodedFlags );
157+ com .google .bigtable .v2 .FeatureFlags flags =
158+ com .google .bigtable .v2 .FeatureFlags .parseFrom (decoded );
159+
160+ com .google .bigtable .v2 .FeatureFlags updatedFlags =
161+ flags .toBuilder ()
162+ .setDirectAccessRequested (isDirectAccessEligible )
163+ .setTrafficDirectorEnabled (isDirectAccessEligible )
164+ .build ();
165+
166+ newHeaders .put (
167+ "bigtable-features" ,
168+ java .util .Base64 .getUrlEncoder ().encodeToString (updatedFlags .toByteArray ()));
169+ } catch (Exception e ) {
170+ // use original headers
171+ }
172+ }
173+ return newHeaders ;
134174 }
135175
136176 /** Expected to only be called once when BigtableClientContext is created */
137177 @ Override
138178 public TransportChannel getTransportChannel () throws IOException {
179+ Map <String , String > directAccessEligibleHeaders = updateFeatureFlags (this .headers , true );
180+
181+ InstantiatingGrpcChannelProvider .Builder directAccessProvider =
182+ EnhancedBigtableStubSettings .applyDirectAccessTraits (delegate .toBuilder ())
183+ .setChannelPoolSettings (ChannelPoolSettings .staticallySized (1 ));
184+
185+ InstantiatingGrpcChannelProvider directAccessProviderWithHeaders =
186+ (InstantiatingGrpcChannelProvider )
187+ directAccessProvider .build ().withHeaders (directAccessEligibleHeaders );
188+ GrpcTransportChannel directAccessTransportChannel =
189+ (GrpcTransportChannel ) directAccessProviderWithHeaders .getTransportChannel ();
190+ Channel maybeDirectAccessChannel = directAccessTransportChannel .getChannel ();
191+ DirectAccessChecker directAccessChecker = UnaryDirectAccessChecker .create (channelPrimer );
192+ boolean isDirectAccessEligible = false ;
193+
194+ try {
195+ isDirectAccessEligible = directAccessChecker .check (maybeDirectAccessChannel );
196+ } catch (Exception e ) {
197+ LOG .log (Level .INFO , "Client is not direct access eligible, using standard transport." , e );
198+ }
199+
200+ InstantiatingGrpcChannelProvider selectedProvider ;
201+
202+ if (isDirectAccessEligible ) {
203+ selectedProvider = directAccessProviderWithHeaders ;
204+ } else {
205+ Map <String , String > fallbackHeaders = updateFeatureFlags (this .headers , false );
206+ selectedProvider = (InstantiatingGrpcChannelProvider ) delegate .withHeaders (fallbackHeaders );
207+ }
208+
139209 // This provider's main purpose is to replace the default GAX ChannelPool
140210 // with a custom BigtableChannelPool, reusing the delegate's configuration.
141211
142212 // To create our pool, we need a factory for raw gRPC channels.
143213 // We achieve this by configuring our delegate to not use its own pooling
144214 // (by setting pool size to 1) and then calling getTransportChannel() on it.
145215 InstantiatingGrpcChannelProvider singleChannelProvider =
146- delegate .toBuilder ().setChannelPoolSettings (ChannelPoolSettings .staticallySized (1 )).build ();
216+ selectedProvider .toBuilder ()
217+ .setChannelPoolSettings (ChannelPoolSettings .staticallySized (1 ))
218+ .build ();
147219
148220 ChannelFactory channelFactory =
149221 () -> {
@@ -187,7 +259,7 @@ public TransportChannelProvider withCredentials(Credentials credentials) {
187259 InstantiatingGrpcChannelProvider newChannelProvider =
188260 (InstantiatingGrpcChannelProvider ) delegate .withCredentials (credentials );
189261 return new BigtableTransportChannelProvider (
190- newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor );
262+ newChannelProvider , channelPrimer , channelPoolMetricsTracer , backgroundExecutor , );
191263 }
192264
193265 /** Creates a BigtableTransportChannelProvider. */
@@ -200,6 +272,6 @@ public static BigtableTransportChannelProvider create(
200272 instantiatingGrpcChannelProvider ,
201273 channelPrimer ,
202274 outstandingRpcsMetricTracker ,
203- backgroundExecutor );
275+ backgroundExecutor , null );
204276 }
205277}
0 commit comments