@@ -39,8 +39,14 @@ public class ServiceStubsOptions {
3939
4040 protected final @ Nullable Consumer <ManagedChannelBuilder <?>> channelInitializer ;
4141
42- /** Indicates whether basic HTTPS/SSL/TLS should be enabled * */
43- protected final boolean enableHttps ;
42+ /**
43+ * Indicates whether basic HTTPS/SSL/TLS should be enabled. Null means not explicitly set by the
44+ * user, allowing runtime derivation of the effective value.
45+ */
46+ protected final Boolean enableHttps ;
47+
48+ /** Indicates whether an API key was provided, used for automatic TLS enablement */
49+ protected final boolean apiKeyProvided ;
4450
4551 /** The user provided context for SSL/TLS over gRPC * */
4652 protected final SslContext sslContext ;
@@ -113,6 +119,7 @@ public class ServiceStubsOptions {
113119 this .target = that .target ;
114120 this .channelInitializer = that .channelInitializer ;
115121 this .enableHttps = that .enableHttps ;
122+ this .apiKeyProvided = that .apiKeyProvided ;
116123 this .sslContext = that .sslContext ;
117124 this .healthCheckAttemptTimeout = that .healthCheckAttemptTimeout ;
118125 this .systemInfoTimeout = that .systemInfoTimeout ;
@@ -134,7 +141,8 @@ public class ServiceStubsOptions {
134141 ManagedChannel channel ,
135142 String target ,
136143 @ Nullable Consumer <ManagedChannelBuilder <?>> channelInitializer ,
137- boolean enableHttps ,
144+ Boolean enableHttps ,
145+ boolean apiKeyProvided ,
138146 SslContext sslContext ,
139147 Duration healthCheckAttemptTimeout ,
140148 Duration healthCheckTimeout ,
@@ -154,6 +162,7 @@ public class ServiceStubsOptions {
154162 this .target = target ;
155163 this .channelInitializer = channelInitializer ;
156164 this .enableHttps = enableHttps ;
165+ this .apiKeyProvided = apiKeyProvided ;
157166 this .sslContext = sslContext ;
158167 this .healthCheckAttemptTimeout = healthCheckAttemptTimeout ;
159168 this .healthCheckTimeout = healthCheckTimeout ;
@@ -202,11 +211,23 @@ public Consumer<ManagedChannelBuilder<?>> getChannelInitializer() {
202211 }
203212
204213 /**
205- * @return if gRPC should use SSL/TLS; Ignored and assumed {@code true} if {@link
206- * #getSslContext()} is set
214+ * Returns whether gRPC should use SSL/TLS. This method computes the effective value based on:
215+ *
216+ * <ul>
217+ * <li>If explicitly set via {@link Builder#setEnableHttps(boolean)}, returns that value
218+ * <li>If an API key was provided and no custom SSL context or channel is set, returns {@code
219+ * true}
220+ * <li>Otherwise returns {@code false}
221+ * </ul>
222+ *
223+ * <p>Note: This is ignored and assumed {@code true} if {@link #getSslContext()} is set.
224+ *
225+ * @return if gRPC should use SSL/TLS
207226 */
208227 public boolean getEnableHttps () {
209- return enableHttps ;
228+ return (enableHttps != null )
229+ ? enableHttps
230+ : (apiKeyProvided && sslContext == null && channel == null );
210231 }
211232
212233 /**
@@ -325,12 +346,13 @@ public boolean equals(Object o) {
325346 if (this == o ) return true ;
326347 if (o == null || getClass () != o .getClass ()) return false ;
327348 ServiceStubsOptions that = (ServiceStubsOptions ) o ;
328- return enableHttps == that .enableHttps
349+ return apiKeyProvided == that .apiKeyProvided
329350 && enableKeepAlive == that .enableKeepAlive
330351 && keepAlivePermitWithoutStream == that .keepAlivePermitWithoutStream
331352 && Objects .equals (channel , that .channel )
332353 && Objects .equals (target , that .target )
333354 && Objects .equals (channelInitializer , that .channelInitializer )
355+ && Objects .equals (enableHttps , that .enableHttps )
334356 && Objects .equals (sslContext , that .sslContext )
335357 && Objects .equals (healthCheckAttemptTimeout , that .healthCheckAttemptTimeout )
336358 && Objects .equals (healthCheckTimeout , that .healthCheckTimeout )
@@ -353,6 +375,7 @@ public int hashCode() {
353375 target ,
354376 channelInitializer ,
355377 enableHttps ,
378+ apiKeyProvided ,
356379 sslContext ,
357380 healthCheckAttemptTimeout ,
358381 healthCheckTimeout ,
@@ -382,6 +405,8 @@ public String toString() {
382405 + channelInitializer
383406 + ", enableHttps="
384407 + enableHttps
408+ + ", apiKeyProvided="
409+ + apiKeyProvided
385410 + ", sslContext="
386411 + sslContext
387412 + ", healthCheckAttemptTimeout="
@@ -444,6 +469,7 @@ protected Builder(ServiceStubsOptions options) {
444469 this .target = options .target ;
445470 this .channelInitializer = options .channelInitializer ;
446471 this .enableHttps = options .enableHttps ;
472+ this .apiKeyProvided = options .apiKeyProvided ;
447473 this .sslContext = options .sslContext ;
448474 this .healthCheckAttemptTimeout = options .healthCheckAttemptTimeout ;
449475 this .healthCheckTimeout = options .healthCheckTimeout ;
@@ -456,8 +482,15 @@ protected Builder(ServiceStubsOptions options) {
456482 this .connectionBackoffResetFrequency = options .connectionBackoffResetFrequency ;
457483 this .grpcReconnectFrequency = options .grpcReconnectFrequency ;
458484 this .headers = options .headers ;
459- this .grpcMetadataProviders = options .grpcMetadataProviders ;
460- this .grpcClientInterceptors = options .grpcClientInterceptors ;
485+ // Make mutable copies of collections to allow adding more items
486+ this .grpcMetadataProviders =
487+ options .grpcMetadataProviders != null && !options .grpcMetadataProviders .isEmpty ()
488+ ? new ArrayList <>(options .grpcMetadataProviders )
489+ : null ;
490+ this .grpcClientInterceptors =
491+ options .grpcClientInterceptors != null && !options .grpcClientInterceptors .isEmpty ()
492+ ? new ArrayList <>(options .grpcClientInterceptors )
493+ : null ;
461494 this .metricsScope = options .metricsScope ;
462495 }
463496
@@ -805,7 +838,8 @@ public ServiceStubsOptions build() {
805838 this .channel ,
806839 this .target ,
807840 this .channelInitializer ,
808- this .enableHttps != null ? this .enableHttps : false ,
841+ this .enableHttps ,
842+ this .apiKeyProvided ,
809843 this .sslContext ,
810844 this .healthCheckAttemptTimeout ,
811845 this .healthCheckTimeout ,
@@ -853,14 +887,6 @@ public ServiceStubsOptions validateAndBuildWithDefaults() {
853887 Collection <ClientInterceptor > grpcClientInterceptors =
854888 MoreObjects .firstNonNull (this .grpcClientInterceptors , Collections .emptyList ());
855889
856- // Resolve enableHttps: explicit value, auto-enable with API key, or default false
857- boolean enableHttps = false ;
858- if (this .enableHttps != null ) {
859- enableHttps = this .enableHttps ;
860- } else if (this .apiKeyProvided && this .sslContext == null && this .channel == null ) {
861- enableHttps = true ;
862- }
863-
864890 Scope metricsScope = this .metricsScope != null ? this .metricsScope : new NoopScope ();
865891 Duration healthCheckAttemptTimeout =
866892 this .healthCheckAttemptTimeout != null
@@ -875,7 +901,8 @@ public ServiceStubsOptions validateAndBuildWithDefaults() {
875901 this .channel ,
876902 target ,
877903 this .channelInitializer ,
878- enableHttps ,
904+ this .enableHttps ,
905+ this .apiKeyProvided ,
879906 this .sslContext ,
880907 healthCheckAttemptTimeout ,
881908 healthCheckTimeout ,
0 commit comments