diff --git a/src/main/java/com/ibm/cloud/sdk/core/http/HttpClientSingleton.java b/src/main/java/com/ibm/cloud/sdk/core/http/HttpClientSingleton.java
index 0245555f7..778d8a86a 100644
--- a/src/main/java/com/ibm/cloud/sdk/core/http/HttpClientSingleton.java
+++ b/src/main/java/com/ibm/cloud/sdk/core/http/HttpClientSingleton.java
@@ -153,7 +153,25 @@ protected HttpClientSingleton() {
}
/**
- * Configures the HTTP client.
+ * Returns the current {@link OkHttpClient} instance held by this singleton.
+ * This is the client instance that is used as a default configuration from which other client instances are built.
+ * @return the current OkHttpClient instance
+ */
+ public OkHttpClient getHttpClient() {
+ return this.okHttpClient;
+ }
+
+ /**
+ * Sets the current {@link OkHttpClient} instance held by this singleton.
+ * This is the client instance that is used as a default configuration from which other client instances are built.
+ * @param client the new OkHttpClient instance to use as a default client configuration
+ */
+ public void setHttpClient(OkHttpClient client) {
+ this.okHttpClient = client;
+ }
+
+ /**
+ * Configures a new HTTP client instance.
*
* @return the HTTP client
*/
@@ -290,7 +308,7 @@ private OkHttpClient setLoggingLevel(OkHttpClient client, LoggingLevel loggingLe
*
* @param builder the {@link OkHttpClient} builder.
*/
- private void setupTLSProtocol(final OkHttpClient.Builder builder) {
+ public static void setupTLSProtocol(final OkHttpClient.Builder builder) {
try {
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
diff --git a/src/main/java/com/ibm/cloud/sdk/core/security/TokenRequestBasedAuthenticator.java b/src/main/java/com/ibm/cloud/sdk/core/security/TokenRequestBasedAuthenticator.java
index 5c8b95f24..e59092a19 100644
--- a/src/main/java/com/ibm/cloud/sdk/core/security/TokenRequestBasedAuthenticator.java
+++ b/src/main/java/com/ibm/cloud/sdk/core/security/TokenRequestBasedAuthenticator.java
@@ -1,5 +1,5 @@
/**
- * (C) Copyright IBM Corp. 2019, 2021.
+ * (C) Copyright IBM Corp. 2019, 2022.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
@@ -40,9 +40,11 @@
*
* - disableSSLVerification - a flag that indicates whether or not client-side SSL verification should be disabled.
*
- headers - a Map of keys/values that will be set as HTTP headers on requests sent to the token service.
- *
- proxy - a java.net.Proxy instance that will be set on the Client object used to interact with the token service.
- *
- proxyAuthenticator - an okhttp3.Authenticator instance to be set on the Client object used to interact wth
- * the token service.
+ *
- proxy - a java.net.Proxy instance that will be set on the OkHttpClient instance used to
+ * interact with the token service.
+ *
- proxyAuthenticator - an okhttp3.Authenticator instance to be set on the OkHttpClient instance used to
+ * interact with the token service.
+ *
- client - a fully-configured OkHttpClient instance to be used to interact with the token service.
*
*/
public abstract class TokenRequestBasedAuthenticator
@@ -50,6 +52,8 @@ public abstract class TokenRequestBasedAuthenticator headers;
@@ -67,6 +71,47 @@ private void setTokenData(T tokenData) {
this.tokenData = tokenData;
}
+ /**
+ * Sets the OkHttpClient instance to be used when interacting with the token service.
+ * @param client the OkHttpClient instance to use
+ */
+ public void setClient(OkHttpClient client) {
+ this.client = client;
+ }
+
+ /**
+ * Returns the OkHttpClient instance to be used when interacting with the token service.
+ * @return the client instance or null if a client insance has not yet been set
+ */
+ public OkHttpClient getClient() {
+ return this.client;
+ }
+
+ /**
+ * Returns a properly-configured OkHttpClient instance to use when interacting with the token service.
+ * This function is different from "getClient()" in that it will configure and save
+ * a client instance if one has not yet been setup for "this".
+ * @return a non-null, configured OkHttpClient instance
+ */
+ protected synchronized OkHttpClient getConfiguredClient() {
+ if (this.client == null) {
+ OkHttpClient defaultClient = HttpClientSingleton.getInstance().getHttpClient();
+
+ HttpConfigOptions.Builder clientOptions = new HttpConfigOptions.Builder()
+ .disableSslVerification(this.disableSSLVerification)
+ .proxy(this.proxy)
+ .proxyAuthenticator(this.proxyAuthenticator);
+
+ if (logger.isLoggable(Level.FINE)) {
+ clientOptions.loggingLevel(LoggingLevel.BODY);
+ }
+
+ this.client = HttpClientSingleton.getInstance().configureClient(defaultClient, clientOptions.build());
+ }
+
+ return this.client;
+ }
+
/**
* Validates the configuration properties associated with the Authenticator.
* Each concrete subclass must implement this method.
@@ -136,7 +181,7 @@ public Proxy getProxy() {
/**
* Sets a Proxy object on this Authenticator.
- * @param proxy the proxy object to be associated with the Client used to interact wth the token service.
+ * @param proxy the proxy object to be associated with the Client used to interact with the token service.
*/
public void setProxy(Proxy proxy) {
this.proxy = proxy;
@@ -256,18 +301,7 @@ protected R invokeRequest(final RequestBuilder requestBuilder, final Class ext
// Allocate the response.
final Object[] responseObj = new Object[1];
- // Set up the Client we'll use to invoke the request.
- final HttpConfigOptions.Builder clientOptions = new HttpConfigOptions.Builder()
- .disableSslVerification(this.disableSSLVerification)
- .proxy(this.proxy)
- .proxyAuthenticator(this.proxyAuthenticator);
-
- // Enable request/response logging.
- if (logger.isLoggable(Level.FINE)) {
- clientOptions.loggingLevel(LoggingLevel.BODY);
- }
-
- final OkHttpClient client = HttpClientSingleton.getInstance().configureClient(clientOptions.build());
+ final OkHttpClient client = getConfiguredClient();
final Request request = requestBuilder.build();
diff --git a/src/test/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticatorTest.java
index 2752c9ed6..c38ce9abd 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticatorTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/security/VpcInstanceAuthenticatorTest.java
@@ -20,6 +20,7 @@
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -38,7 +39,9 @@
import com.ibm.cloud.sdk.core.test.BaseServiceUnitTest;
import com.ibm.cloud.sdk.core.util.Clock;
+import okhttp3.ConnectionSpec;
import okhttp3.Headers;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.mockwebserver.RecordedRequest;
@@ -404,6 +407,19 @@ public void testAuthenticateNewAndCachedToken() throws Throwable {
.url(url)
.build();
+ // Create a custom client and set it on the authenticator.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ authenticator.setClient(client);
+ assertEquals(authenticator.getClient(), client);
+
Request.Builder requestBuilder = new Request.Builder().url("https://test.com");
// Set mock server responses.
@@ -422,6 +438,9 @@ public void testAuthenticateNewAndCachedToken() throws Throwable {
requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + vpcIamAccessTokenResponse1.getAccessToken());
+
+ // Verify that the authenticator is still using the same client instance that we set before.
+ assertEquals(authenticator.getClient(), client);
}
@Test
diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/http/HttpClientSingletonTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/http/HttpClientSingletonTest.java
index 06d06437e..68aa9bda7 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/test/http/HttpClientSingletonTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/test/http/HttpClientSingletonTest.java
@@ -14,6 +14,8 @@
package com.ibm.cloud.sdk.core.test.http;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
@@ -21,6 +23,7 @@
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLSocket;
@@ -126,4 +129,45 @@ public void testSetRetryStrategy() {
assertEquals(testRetryInterceptors, 1);
assertEquals(otherRetryInterceptors, 0);
}
+
+ @Test
+ public void testSetClient() {
+
+ // Create a custom client and set it on the HttpClientSingleton
+ // as the default for configuring other clients.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ HttpClientSingleton cs = HttpClientSingleton.getInstance();
+ assertNotNull(cs.getHttpClient());
+
+ // Verify set/get.
+ cs.setHttpClient(client);
+ assertEquals(cs.getHttpClient(), client);
+
+ // Verify that "client" is used to configure other clients.
+ // To verify this, we'll just configure a new client with SSL verification disabled,
+ // and its timeout properties should be the same as what we set above on our original client.
+ HttpConfigOptions options = new HttpConfigOptions.Builder()
+ .disableSslVerification(true)
+ .build();
+ OkHttpClient client2 = cs.configureClient(options);
+
+ // Verify that the "configureClient()" call above set a new default client instance in the singleton.
+ assertNotEquals(cs.getHttpClient(), client);
+
+ // Verify that the new client is a different instance than our original client.
+ assertNotEquals(client2, client);
+
+ // Verify that the new client "inherits" the config from our original client.
+ assertEquals(client2.connectTimeoutMillis(), 30 * 1000);
+ assertEquals(client2.writeTimeoutMillis(), 120 * 1000);
+ assertEquals(client2.readTimeoutMillis(), 120 * 1000);
+ }
}
diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/security/ContainerAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/security/ContainerAuthenticatorTest.java
index dcf1cb571..f65eedfb5 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/test/security/ContainerAuthenticatorTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/test/security/ContainerAuthenticatorTest.java
@@ -22,6 +22,7 @@
import static org.testng.Assert.fail;
import java.nio.file.NoSuchFileException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -45,7 +46,9 @@
import com.ibm.cloud.sdk.core.test.BaseServiceUnitTest;
import com.ibm.cloud.sdk.core.util.Clock;
+import okhttp3.ConnectionSpec;
import okhttp3.Headers;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.mockwebserver.RecordedRequest;
@@ -249,6 +252,19 @@ public void testAuthenticateNewAndStoredToken() throws Throwable {
.url(url)
.build();
+ // Create a custom client and set it on the authenticator.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ authenticator.setClient(client);
+ assertEquals(authenticator.getClient(), client);
+
Request.Builder requestBuilder = new Request.Builder().url("https://test.com");
// Set mock server response.
@@ -280,6 +296,9 @@ public void testAuthenticateNewAndStoredToken() throws Throwable {
requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData1.getAccessToken());
+
+ // Verify that the authenticator is still using the same client instance that we set before.
+ assertEquals(authenticator.getClient(), client);
}
@Test
diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dAuthenticatorTest.java
index 31ebfa4a0..d051cdaa4 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dAuthenticatorTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dAuthenticatorTest.java
@@ -21,6 +21,7 @@
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -39,7 +40,9 @@
import com.ibm.cloud.sdk.core.test.BaseServiceUnitTest;
import com.ibm.cloud.sdk.core.util.Clock;
+import okhttp3.ConnectionSpec;
import okhttp3.Headers;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.mockwebserver.RecordedRequest;
@@ -312,10 +315,22 @@ public void testAuthenticateNewAndStoredToken() {
.password(testPassword)
.disableSSLVerification(true)
.build();
- Request.Builder requestBuilder;
+
+ // Create a custom client and set it on the authenticator.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ authenticator.setClient(client);
+ assertEquals(authenticator.getClient(), client);
// Authenticator should request new, valid token.
- requestBuilder = new Request.Builder().url("https://test.com");
+ Request.Builder requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getToken());
@@ -323,6 +338,9 @@ public void testAuthenticateNewAndStoredToken() {
requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getToken());
+
+ // Verify that the authenticator is still using the same client instance that we set before.
+ assertEquals(authenticator.getClient(), client);
}
@Test
diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceAuthenticatorTest.java
index f9ccb308f..0e3af009e 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceAuthenticatorTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceAuthenticatorTest.java
@@ -21,6 +21,7 @@
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -38,7 +39,9 @@
import com.ibm.cloud.sdk.core.test.BaseServiceUnitTest;
import com.ibm.cloud.sdk.core.util.Clock;
+import okhttp3.ConnectionSpec;
import okhttp3.Headers;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.mockwebserver.RecordedRequest;
@@ -439,10 +442,22 @@ public void testAuthenticateNewAndStoredToken() {
.permissions(testPermissions)
.expirationTime(testExpirationTime)
.build();
- Request.Builder requestBuilder;
+
+ // Create a custom client and set it on the authenticator.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ authenticator.setClient(client);
+ assertEquals(authenticator.getClient(), client);
// Authenticator should request new, valid token.
- requestBuilder = new Request.Builder().url("https://test.com");
+ Request.Builder requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getToken());
@@ -450,6 +465,9 @@ public void testAuthenticateNewAndStoredToken() {
requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getToken());
+
+ // Verify that the authenticator is still using the same client instance that we set before.
+ assertEquals(authenticator.getClient(), client);
}
@Test
diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceInstanceAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceInstanceAuthenticatorTest.java
index 4b2209495..6f407ab1b 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceInstanceAuthenticatorTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/test/security/Cp4dServiceInstanceAuthenticatorTest.java
@@ -21,6 +21,7 @@
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -38,7 +39,9 @@
import com.ibm.cloud.sdk.core.test.BaseServiceUnitTest;
import com.ibm.cloud.sdk.core.util.Clock;
+import okhttp3.ConnectionSpec;
import okhttp3.Headers;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.mockwebserver.RecordedRequest;
@@ -345,10 +348,22 @@ public void testAuthenticateNewAndStoredToken() {
.serviceInstanceId(testServiceInstanceId)
.disableSSLVerification(true)
.build();
- Request.Builder requestBuilder;
+
+ // Create a custom client and set it on the authenticator.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ authenticator.setClient(client);
+ assertEquals(authenticator.getClient(), client);
// Authenticator should request new, valid token.
- requestBuilder = new Request.Builder().url("https://test.com");
+ Request.Builder requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getData().getToken());
@@ -356,6 +371,9 @@ public void testAuthenticateNewAndStoredToken() {
requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getData().getToken());
+
+ // Verify that the authenticator is still using the same client instance that we set before.
+ assertEquals(authenticator.getClient(), client);
}
@Test
diff --git a/src/test/java/com/ibm/cloud/sdk/core/test/security/IamAuthenticatorTest.java b/src/test/java/com/ibm/cloud/sdk/core/test/security/IamAuthenticatorTest.java
index e07177a6c..14f2d4525 100644
--- a/src/test/java/com/ibm/cloud/sdk/core/test/security/IamAuthenticatorTest.java
+++ b/src/test/java/com/ibm/cloud/sdk/core/test/security/IamAuthenticatorTest.java
@@ -21,6 +21,7 @@
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -44,7 +45,9 @@
import com.ibm.cloud.sdk.core.test.BaseServiceUnitTest;
import com.ibm.cloud.sdk.core.util.Clock;
+import okhttp3.ConnectionSpec;
import okhttp3.Headers;
+import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.mockwebserver.RecordedRequest;
@@ -279,6 +282,19 @@ public void testAuthenticateNewAndStoredToken() throws Throwable {
.disableSSLVerification(true)
.build();
+ // Create a custom client and set it on the authenticator.
+ ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledCipherSuites()
+ .build();
+ OkHttpClient client = new OkHttpClient.Builder()
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(120, TimeUnit.SECONDS)
+ .readTimeout(120, TimeUnit.SECONDS)
+ .connectionSpecs(Arrays.asList(spec, ConnectionSpec.CLEARTEXT))
+ .build();
+ authenticator.setClient(client);
+ assertEquals(authenticator.getClient(), client);
+
Request.Builder requestBuilder = new Request.Builder().url("https://test.com");
// Authenticator should request new, valid token.
@@ -297,6 +313,9 @@ public void testAuthenticateNewAndStoredToken() throws Throwable {
requestBuilder = new Request.Builder().url("https://test.com");
authenticator.authenticate(requestBuilder);
verifyAuthHeader(requestBuilder, "Bearer " + tokenData.getAccessToken());
+
+ // Verify that the authenticator is still using the same client instance that we set before.
+ assertEquals(authenticator.getClient(), client);
}
@Test