Skip to content

Commit 0e6459d

Browse files
committed
make enableHttps a Boolean
1 parent 36fae11 commit 0e6459d

File tree

2 files changed

+118
-38
lines changed

2 files changed

+118
-38
lines changed

temporal-serviceclient/src/main/java/io/temporal/serviceclient/ServiceStubsOptions.java

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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,24 @@ 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} (auto-enabled)
220+
* <li>Otherwise returns {@code false}
221+
* </ul>
222+
*
223+
* <p>Note: When {@link #getSslContext()} is set, TLS is handled by the SSL context regardless of
224+
* this value.
225+
*
226+
* @return if gRPC should use SSL/TLS
207227
*/
208228
public boolean getEnableHttps() {
209-
return enableHttps;
229+
return (enableHttps != null)
230+
? enableHttps
231+
: (apiKeyProvided && sslContext == null && channel == null);
210232
}
211233

212234
/**
@@ -325,12 +347,13 @@ public boolean equals(Object o) {
325347
if (this == o) return true;
326348
if (o == null || getClass() != o.getClass()) return false;
327349
ServiceStubsOptions that = (ServiceStubsOptions) o;
328-
return enableHttps == that.enableHttps
350+
return apiKeyProvided == that.apiKeyProvided
329351
&& enableKeepAlive == that.enableKeepAlive
330352
&& keepAlivePermitWithoutStream == that.keepAlivePermitWithoutStream
331353
&& Objects.equals(channel, that.channel)
332354
&& Objects.equals(target, that.target)
333355
&& Objects.equals(channelInitializer, that.channelInitializer)
356+
&& Objects.equals(enableHttps, that.enableHttps)
334357
&& Objects.equals(sslContext, that.sslContext)
335358
&& Objects.equals(healthCheckAttemptTimeout, that.healthCheckAttemptTimeout)
336359
&& Objects.equals(healthCheckTimeout, that.healthCheckTimeout)
@@ -353,6 +376,7 @@ public int hashCode() {
353376
target,
354377
channelInitializer,
355378
enableHttps,
379+
apiKeyProvided,
356380
sslContext,
357381
healthCheckAttemptTimeout,
358382
healthCheckTimeout,
@@ -444,6 +468,7 @@ protected Builder(ServiceStubsOptions options) {
444468
this.target = options.target;
445469
this.channelInitializer = options.channelInitializer;
446470
this.enableHttps = options.enableHttps;
471+
this.apiKeyProvided = options.apiKeyProvided;
447472
this.sslContext = options.sslContext;
448473
this.healthCheckAttemptTimeout = options.healthCheckAttemptTimeout;
449474
this.healthCheckTimeout = options.healthCheckTimeout;
@@ -456,8 +481,15 @@ protected Builder(ServiceStubsOptions options) {
456481
this.connectionBackoffResetFrequency = options.connectionBackoffResetFrequency;
457482
this.grpcReconnectFrequency = options.grpcReconnectFrequency;
458483
this.headers = options.headers;
459-
this.grpcMetadataProviders = options.grpcMetadataProviders;
460-
this.grpcClientInterceptors = options.grpcClientInterceptors;
484+
// Make mutable copies of collections to allow adding more items
485+
this.grpcMetadataProviders =
486+
options.grpcMetadataProviders != null && !options.grpcMetadataProviders.isEmpty()
487+
? new ArrayList<>(options.grpcMetadataProviders)
488+
: null;
489+
this.grpcClientInterceptors =
490+
options.grpcClientInterceptors != null && !options.grpcClientInterceptors.isEmpty()
491+
? new ArrayList<>(options.grpcClientInterceptors)
492+
: null;
461493
this.metricsScope = options.metricsScope;
462494
}
463495

@@ -805,7 +837,8 @@ public ServiceStubsOptions build() {
805837
this.channel,
806838
this.target,
807839
this.channelInitializer,
808-
this.enableHttps != null ? this.enableHttps : false,
840+
this.enableHttps,
841+
this.apiKeyProvided,
809842
this.sslContext,
810843
this.healthCheckAttemptTimeout,
811844
this.healthCheckTimeout,
@@ -853,14 +886,6 @@ public ServiceStubsOptions validateAndBuildWithDefaults() {
853886
Collection<ClientInterceptor> grpcClientInterceptors =
854887
MoreObjects.firstNonNull(this.grpcClientInterceptors, Collections.emptyList());
855888

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-
864889
Scope metricsScope = this.metricsScope != null ? this.metricsScope : new NoopScope();
865890
Duration healthCheckAttemptTimeout =
866891
this.healthCheckAttemptTimeout != null
@@ -875,7 +900,8 @@ public ServiceStubsOptions validateAndBuildWithDefaults() {
875900
this.channel,
876901
target,
877902
this.channelInitializer,
878-
enableHttps,
903+
this.enableHttps,
904+
this.apiKeyProvided,
879905
this.sslContext,
880906
healthCheckAttemptTimeout,
881907
healthCheckTimeout,

temporal-serviceclient/src/test/java/io/temporal/serviceclient/ServiceStubsOptionsTest.java

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package io.temporal.serviceclient;
22

33
import static org.junit.Assert.*;
4-
import static org.mockito.Mockito.mock;
54

6-
import io.grpc.ManagedChannel;
7-
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
85
import org.junit.Test;
96

107
public class ServiceStubsOptionsTest {
@@ -68,33 +65,90 @@ public void testExplicitTLSEnableWithoutAPIKey() {
6865
}
6966

7067
@Test
71-
public void testTLSNotAutoEnabledWhenSslContextProvided() {
72-
// When user provides custom sslContext, they're handling TLS themselves
73-
// so enableHttps should not be auto-enabled
74-
SslContext sslContext = mock(SslContext.class);
75-
ServiceStubsOptions options =
68+
public void testBuilderFromOptionsPreservesDefaultTLSBehavior() {
69+
ServiceStubsOptions options1 =
7670
WorkflowServiceStubsOptions.newBuilder()
7771
.setTarget("localhost:7233")
72+
.validateAndBuildWithDefaults();
73+
74+
assertFalse(options1.getEnableHttps());
75+
76+
ServiceStubsOptions options2 =
77+
WorkflowServiceStubsOptions.newBuilder(options1)
7878
.addApiKey(() -> "test-api-key")
79-
.setSslContext(sslContext)
8079
.validateAndBuildWithDefaults();
8180

82-
// enableHttps stays false because sslContext handles TLS
83-
assertFalse(options.getEnableHttps());
84-
assertNotNull(options.getSslContext());
81+
assertTrue(
82+
"TLS should auto-enable when API key is added to builder from options that had default TLS behavior",
83+
options2.getEnableHttps());
8584
}
8685

8786
@Test
88-
public void testTLSNotAutoEnabledWhenCustomChannelProvided() {
89-
// When user provides custom channel, they're managing connection themselves
90-
// so enableHttps should not be auto-enabled
91-
ManagedChannel channel = mock(ManagedChannel.class);
92-
ServiceStubsOptions options =
87+
public void testBuilderFromOptionsWithExplicitTLSDisableStaysDisabled() {
88+
ServiceStubsOptions options1 =
9389
WorkflowServiceStubsOptions.newBuilder()
94-
.setChannel(channel)
90+
.setTarget("localhost:7233")
91+
.setEnableHttps(false)
92+
.validateAndBuildWithDefaults();
93+
94+
assertFalse(options1.getEnableHttps());
95+
96+
ServiceStubsOptions options2 =
97+
WorkflowServiceStubsOptions.newBuilder(options1)
9598
.addApiKey(() -> "test-api-key")
9699
.validateAndBuildWithDefaults();
97100

98-
assertFalse(options.getEnableHttps());
101+
assertFalse(
102+
"TLS should stay disabled when explicitly set to false, even with API key",
103+
options2.getEnableHttps());
104+
}
105+
106+
@Test
107+
public void testBuilderFromOptionsWithExplicitTLSEnableStaysEnabled() {
108+
ServiceStubsOptions options1 =
109+
WorkflowServiceStubsOptions.newBuilder()
110+
.setTarget("localhost:7233")
111+
.setEnableHttps(true)
112+
.validateAndBuildWithDefaults();
113+
114+
assertTrue(options1.getEnableHttps());
115+
116+
ServiceStubsOptions options2 =
117+
WorkflowServiceStubsOptions.newBuilder(options1).validateAndBuildWithDefaults();
118+
119+
assertTrue("TLS should stay enabled when explicitly set to true", options2.getEnableHttps());
120+
}
121+
122+
@Test
123+
public void testSpringBootStyleAutoTLSWithApiKey() {
124+
ServiceStubsOptions options1 =
125+
WorkflowServiceStubsOptions.newBuilder()
126+
.setTarget("my-namespace.tmprl.cloud:7233")
127+
.addApiKey(() -> "my-api-key")
128+
.validateAndBuildWithDefaults();
129+
130+
assertTrue(
131+
"TLS should auto-enable when API key is provided without explicit TLS setting",
132+
options1.getEnableHttps());
133+
134+
ServiceStubsOptions options2 =
135+
WorkflowServiceStubsOptions.newBuilder()
136+
.setTarget("localhost:7233")
137+
.setEnableHttps(false)
138+
.addApiKey(() -> "my-api-key")
139+
.validateAndBuildWithDefaults();
140+
141+
assertFalse(
142+
"TLS should stay disabled when explicitly set to false, even with API key",
143+
options2.getEnableHttps());
144+
145+
ServiceStubsOptions options3 =
146+
WorkflowServiceStubsOptions.newBuilder()
147+
.setTarget("localhost:7233")
148+
.validateAndBuildWithDefaults();
149+
150+
assertFalse(
151+
"TLS should be disabled when no API key and no explicit TLS setting",
152+
options3.getEnableHttps());
99153
}
100154
}

0 commit comments

Comments
 (0)