diff --git a/client/pom.xml b/client/pom.xml
index 1d4b32f4..5310cfea 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,9 +5,9 @@
io.split.client
java-client-parent
- 4.16.0
+ 4.17.0-rc2
- 4.16.0
+ 4.17.0-rc2
java-client
jar
Java Client
@@ -24,7 +24,7 @@
0.8.0
true
- true
+ false
central
false
published
diff --git a/client/src/main/java/io/split/client/HttpClientDynamicCredentials.java b/client/src/main/java/io/split/client/HttpClientDynamicCredentials.java
new file mode 100644
index 00000000..01a32362
--- /dev/null
+++ b/client/src/main/java/io/split/client/HttpClientDynamicCredentials.java
@@ -0,0 +1,25 @@
+package io.split.client;
+
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.BearerToken;
+import org.apache.hc.client5.http.auth.Credentials;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+class HttpClientDynamicCredentials implements org.apache.hc.client5.http.auth.CredentialsProvider {
+
+ private final ProxyCredentialsProvider _proxyCredentialsProvider;
+
+ public HttpClientDynamicCredentials (ProxyCredentialsProvider proxyCredentialsProvider) {
+ _proxyCredentialsProvider = proxyCredentialsProvider;
+ }
+
+ @Override
+ public Credentials getCredentials(AuthScope authScope, HttpContext context) {
+
+ // This Provider is invoked every time a request is made.
+ // This should invoke a user-custom provider responsible for:
+ return new BearerToken(_proxyCredentialsProvider.getJwtToken());
+ }
+
+}
+
diff --git a/client/src/main/java/io/split/client/ProxyCredentialsProvider.java b/client/src/main/java/io/split/client/ProxyCredentialsProvider.java
new file mode 100644
index 00000000..6fb595bc
--- /dev/null
+++ b/client/src/main/java/io/split/client/ProxyCredentialsProvider.java
@@ -0,0 +1,10 @@
+package io.split.client;
+
+public interface ProxyCredentialsProvider
+{
+ /**
+ * Get the additional headers needed for all http operations
+ * @return HashMap of addition headers
+ */
+ String getJwtToken();
+}
diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java
index fd312c3b..d314baa7 100644
--- a/client/src/main/java/io/split/client/SplitClientConfig.java
+++ b/client/src/main/java/io/split/client/SplitClientConfig.java
@@ -1,5 +1,6 @@
package io.split.client;
+import io.split.client.dtos.ProxyMTLSAuth;
import io.split.client.impressions.ImpressionListener;
import io.split.client.impressions.ImpressionsManager;
import io.split.client.utils.FileTypeEnum;
@@ -34,6 +35,14 @@ public class SplitClientConfig {
public static final String STREAMING_ENDPOINT = "https://streaming.split.io/sse";
public static final String TELEMETRY_ENDPOINT = "https://telemetry.split.io/api/v1";
+ public static class HttpScheme {
+ private HttpScheme() {
+ throw new IllegalStateException("Utility class");
+ }
+ public static final String HTTP = "http";
+ public static final String HTTPS = "https";
+ }
+
private final String _endpoint;
private final String _eventsEndpoint;
@@ -85,6 +94,8 @@ public class SplitClientConfig {
private final HttpHost _proxy;
private final String _proxyUsername;
private final String _proxyPassword;
+ private final ProxyCredentialsProvider _proxyCredentialsProvider;
+ private final ProxyMTLSAuth _proxyMtlsAuth;
// To be set during startup
public static String splitSdkVersion;
@@ -118,6 +129,8 @@ private SplitClientConfig(String endpoint,
HttpHost proxy,
String proxyUsername,
String proxyPassword,
+ ProxyCredentialsProvider proxyCredentialsProvider,
+ ProxyMTLSAuth proxyMtlsAuth,
int eventsQueueSize,
long eventSendIntervalInMillis,
int maxStringLength,
@@ -171,6 +184,8 @@ private SplitClientConfig(String endpoint,
_proxy = proxy;
_proxyUsername = proxyUsername;
_proxyPassword = proxyPassword;
+ _proxyCredentialsProvider = proxyCredentialsProvider;
+ _proxyMtlsAuth = proxyMtlsAuth;
_eventsQueueSize = eventsQueueSize;
_eventSendIntervalInMillis = eventSendIntervalInMillis;
_maxStringLength = maxStringLength;
@@ -302,6 +317,14 @@ public String proxyPassword() {
return _proxyPassword;
}
+ public ProxyCredentialsProvider proxyCredentialsProvider() {
+ return _proxyCredentialsProvider;
+ }
+
+ public ProxyMTLSAuth proxyMTLSAuth() {
+ return _proxyMtlsAuth;
+ }
+
public long eventSendIntervalInMillis() {
return _eventSendIntervalInMillis;
}
@@ -417,8 +440,8 @@ public boolean isSdkEndpointOverridden() {
}
public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; }
- public static final class Builder {
+ public static final class Builder {
private String _endpoint = SDK_ENDPOINT;
private boolean _endpointSet = false;
private String _eventsEndpoint = EVENTS_ENDPOINT;
@@ -440,8 +463,11 @@ public static final class Builder {
private int _waitBeforeShutdown = 5000;
private String _proxyHost = "localhost";
private int _proxyPort = -1;
+ private String _proxyScheme = HttpScheme.HTTP;
private String _proxyUsername;
private String _proxyPassword;
+ private ProxyCredentialsProvider _proxyCredentialsProvider;
+ private ProxyMTLSAuth _proxyMtlsAuth;
private int _eventsQueueSize = 500;
private long _eventSendIntervalInMillis = 30 * (long)1000;
private int _maxStringLength = 250;
@@ -754,6 +780,17 @@ public Builder proxyPort(int proxyPort) {
return this;
}
+ /**
+ * The http scheme of the proxy. Default is http.
+ *
+ * @param proxyScheme protocol for the proxy
+ * @return this builder
+ */
+ public Builder proxyScheme(String proxyScheme) {
+ _proxyScheme = proxyScheme;
+ return this;
+ }
+
/**
* Set the username for authentication against the proxy (if proxy settings are enabled). (Optional).
*
@@ -776,6 +813,28 @@ public Builder proxyPassword(String proxyPassword) {
return this;
}
+ /**
+ * Set the token for authentication against the proxy (if proxy settings are enabled). (Optional).
+ *
+ * @param proxyCredentialsProvider
+ * @return this builder
+ */
+ public Builder proxyCredentialsProvider(ProxyCredentialsProvider proxyCredentialsProvider) {
+ _proxyCredentialsProvider = proxyCredentialsProvider;
+ return this;
+ }
+
+ /**
+ * Set the mtls authentication against the proxy (if proxy settings are enabled). (Optional).
+ *
+ * @param proxyMtlsAuth
+ * @return this builder
+ */
+ public Builder proxyMtlsAuth(ProxyMTLSAuth proxyMtlsAuth) {
+ _proxyMtlsAuth = proxyMtlsAuth;
+ return this;
+ }
+
/**
* Disables running destroy() on shutdown by default.
*
@@ -788,7 +847,7 @@ public Builder disableDestroyOnShutDown() {
HttpHost proxy() {
if (_proxyPort != -1) {
- return new HttpHost(_proxyHost, _proxyPort);
+ return new HttpHost(_proxyScheme, _proxyHost, _proxyPort);
}
// Default is no proxy.
return null;
@@ -1096,6 +1155,36 @@ private void verifyAlternativeClient() {
}
}
+ private void verifyProxy() {
+ if (_proxyPort == -1) {
+ return;
+ }
+
+ if (!(_proxyScheme.equals(HttpScheme.HTTP) || _proxyScheme.equals(HttpScheme.HTTPS))) {
+ throw new IllegalArgumentException("Proxy scheme must be either http or https.");
+ }
+
+ if (_proxyUsername == null && _proxyCredentialsProvider == null && _proxyMtlsAuth == null) {
+ return;
+ }
+
+ if (_proxyUsername != null && _proxyCredentialsProvider != null) {
+ throw new IllegalArgumentException("Proxy user and Proxy token params are updated, set only one param.");
+ }
+
+ if (_proxyUsername != null && _proxyMtlsAuth != null) {
+ throw new IllegalArgumentException("Proxy user and Proxy mTLS params are updated, set only one param.");
+ }
+
+ if (_proxyCredentialsProvider != null && _proxyMtlsAuth != null) {
+ throw new IllegalArgumentException("Proxy token and Proxy mTLS params are updated, set only one param.");
+ }
+
+ if (_proxyMtlsAuth != null && (_proxyMtlsAuth.getP12File() == null || _proxyMtlsAuth.getP12FilePassKey() == null)) {
+ throw new IllegalArgumentException("Proxy mTLS must have p12 file path and name, and pass phrase.");
+ }
+ }
+
public SplitClientConfig build() {
verifyRates();
@@ -1108,6 +1197,8 @@ public SplitClientConfig build() {
verifyAlternativeClient();
+ verifyProxy();
+
if (_numThreadsForSegmentFetch <= 0) {
throw new IllegalArgumentException("Number of threads for fetching segments MUST be greater than zero");
}
@@ -1133,6 +1224,8 @@ public SplitClientConfig build() {
proxy(),
_proxyUsername,
_proxyPassword,
+ _proxyCredentialsProvider,
+ _proxyMtlsAuth,
_eventsQueueSize,
_eventSendIntervalInMillis,
_maxStringLength,
diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java
index 9932cbf8..6e9d9913 100644
--- a/client/src/main/java/io/split/client/SplitFactoryImpl.java
+++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java
@@ -113,11 +113,14 @@
import org.slf4j.LoggerFactory;
import pluggable.CustomStorageWrapper;
+import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.file.Paths;
+import java.security.KeyStore;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import java.util.HashSet;
@@ -517,9 +520,12 @@ public boolean isDestroyed() {
protected static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClientConfig config,
SDKMetadata sdkMetadata, RequestDecorator requestDecorator)
- throws URISyntaxException {
+ throws URISyntaxException, IOException {
+
+ SSLContext sslContext = buildSSLContext(config);
+
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(sslContext)
.setTlsVersions(TLS.V_1_1, TLS.V_1_2)
.build();
@@ -556,13 +562,15 @@ protected static SplitHttpClient buildSplitHttpClient(String apiToken, SplitClie
}
private static CloseableHttpClient buildSSEdHttpClient(String apiToken, SplitClientConfig config,
- SDKMetadata sdkMetadata) {
+ SDKMetadata sdkMetadata) throws IOException {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(Timeout.ofMilliseconds(SSE_CONNECT_TIMEOUT))
.build();
+ SSLContext sslContext = buildSSLContext(config);
+
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(sslContext)
.setTlsVersions(TLS.V_1_1, TLS.V_1_2)
.build();
@@ -589,6 +597,33 @@ private static CloseableHttpClient buildSSEdHttpClient(String apiToken, SplitCli
return httpClientbuilder.build();
}
+ private static SSLContext buildSSLContext(SplitClientConfig config) throws IOException, NullPointerException {
+ SSLContext sslContext;
+ if (config.proxyMTLSAuth() != null) {
+ _log.debug("Proxy setup using mTLS");
+ InputStream keystoreStream = null;
+ try {
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+ keystoreStream = java.nio.file.Files.newInputStream(Paths.get(config.proxyMTLSAuth().getP12File()));
+ keyStore.load(keystoreStream, config.proxyMTLSAuth().getP12FilePassKey().toCharArray());
+ sslContext = SSLContexts.custom()
+ .loadKeyMaterial(keyStore, config.proxyMTLSAuth().getP12FilePassKey().toCharArray())
+ .build();
+ } catch (Exception e) {
+ _log.error("Exception caught while processing p12 file for Proxy mTLS auth: ", e);
+ _log.warn("Ignoring p12 mTLS config and switching to default context");
+ sslContext = SSLContexts.createSystemDefault();
+ } finally {
+ if (keystoreStream != null) {
+ keystoreStream.close();
+ }
+ }
+ } else {
+ sslContext = SSLContexts.createSystemDefault();
+ }
+ return sslContext;
+ }
+
private static HttpClientBuilder setupProxy(HttpClientBuilder httpClientbuilder, SplitClientConfig config) {
_log.info("Initializing Split SDK with proxy settings");
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(config.proxy());
@@ -604,6 +639,11 @@ private static HttpClientBuilder setupProxy(HttpClientBuilder httpClientbuilder,
httpClientbuilder.setDefaultCredentialsProvider(credsProvider);
}
+ if (config.proxyCredentialsProvider() != null) {
+ _log.debug("Proxy setup using token");
+ httpClientbuilder.setDefaultCredentialsProvider(new HttpClientDynamicCredentials(config.proxyCredentialsProvider()));
+ }
+
return httpClientbuilder;
}
diff --git a/client/src/main/java/io/split/client/dtos/ProxyMTLSAuth.java b/client/src/main/java/io/split/client/dtos/ProxyMTLSAuth.java
new file mode 100644
index 00000000..5cca5abd
--- /dev/null
+++ b/client/src/main/java/io/split/client/dtos/ProxyMTLSAuth.java
@@ -0,0 +1,38 @@
+package io.split.client.dtos;
+
+public class ProxyMTLSAuth {
+ private final String _proxyP12File;
+ private final String _proxyP12FilePassKey;
+
+ private ProxyMTLSAuth(String proxyP12File, String proxyP12FilePassKey) {
+ _proxyP12File = proxyP12File;
+ _proxyP12FilePassKey = proxyP12FilePassKey;
+ }
+
+ public String getP12File() { return _proxyP12File; }
+
+ public String getP12FilePassKey() { return _proxyP12FilePassKey; }
+
+ public static ProxyMTLSAuth.Builder builder() {
+ return new ProxyMTLSAuth.Builder();
+ }
+
+ public static class Builder {
+ private String _p12File;
+ private String _p12FilePassKey;
+
+ public ProxyMTLSAuth.Builder proxyP12File(String p12File) {
+ _p12File = p12File;
+ return this;
+ }
+
+ public ProxyMTLSAuth.Builder proxyP12FilePassKey(String p12FilePassKey) {
+ _p12FilePassKey = p12FilePassKey;
+ return this;
+ }
+
+ public ProxyMTLSAuth build() {
+ return new ProxyMTLSAuth(_p12File, _p12FilePassKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java b/client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java
index bd2ff918..80b3703d 100644
--- a/client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java
+++ b/client/src/main/java/io/split/client/impressions/UniqueKeysTrackerImp.java
@@ -117,7 +117,7 @@ private void sendUniqueKeys(){
}
try {
if (uniqueKeysTracker.size() == 0) {
- _log.warn("The Unique Keys Tracker is empty");
+ _log.debug("The Unique Keys Tracker is empty");
return;
}
HashMap> uniqueKeysHashMap = popAll();
diff --git a/client/src/test/java/io/split/client/SplitClientConfigTest.java b/client/src/test/java/io/split/client/SplitClientConfigTest.java
index 1b640071..81ad9913 100644
--- a/client/src/test/java/io/split/client/SplitClientConfigTest.java
+++ b/client/src/test/java/io/split/client/SplitClientConfigTest.java
@@ -1,6 +1,7 @@
package io.split.client;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import io.split.client.dtos.ProxyMTLSAuth;
import io.split.client.impressions.Impression;
import io.split.client.impressions.ImpressionListener;
import io.split.client.impressions.ImpressionsManager;
@@ -252,6 +253,119 @@ public Map> getHeaderOverrides(RequestContext context) {
SplitClientConfig config2 = SplitClientConfig.builder().build();
Assert.assertNull(config2.customHeaderDecorator());
+ }
+
+ @Test
+ public void checkProxyParams() {
+ SplitClientConfig config = SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888).build();
+ Assert.assertEquals("proxy-host", config.proxy().getHostName());
+ Assert.assertEquals(8888, config.proxy().getPort());
+
+ config = SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyScheme(SplitClientConfig.HttpScheme.HTTPS)
+ .proxyUsername("user")
+ .proxyPassword("pass")
+ .build();
+ Assert.assertEquals("user", config.proxyUsername());
+ Assert.assertEquals("pass", config.proxyPassword());
+
+ ProxyCredentialsProvider proxyCredentialsProvider = new ProxyCredentialsProvider() {
+ @Override
+ public String getJwtToken() {
+ return "my-token";
+ }
+ };
+
+ config = SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyCredentialsProvider(proxyCredentialsProvider)
+ .build();
+ Assert.assertEquals(proxyCredentialsProvider, config.proxyCredentialsProvider());
+
+ config = SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12File("path/to/file").proxyP12FilePassKey("pass-key").build())
+ .build();
+ Assert.assertEquals("path/to/file", config.proxyMTLSAuth().getP12File());
+ Assert.assertEquals("pass-key", config.proxyMTLSAuth().getP12FilePassKey());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotUseInvalidHttpScheme() {
+ SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyScheme("ftp")
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotUseProxyTokenAndProxyUsername() {
+ ProxyCredentialsProvider proxyCredentialsProvider = new ProxyCredentialsProvider() {
+ @Override
+ public String getJwtToken() {
+ return "my-token";
+ }
+ };
+
+ SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyUsername("user")
+ .proxyPassword("pass")
+ .proxyCredentialsProvider(proxyCredentialsProvider)
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotUseProxyUserAndProxyMtls() {
+ SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyUsername("user")
+ .proxyPassword("pass")
+ .proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12File("path/to/file").proxyP12FilePassKey("pass-key").build())
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void cannotUseProxyTokenAndProxyMtls() {
+ ProxyCredentialsProvider proxyCredentialsProvider = new ProxyCredentialsProvider() {
+ @Override
+ public String getJwtToken() {
+ return "my-token";
+ }
+ };
+ SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyCredentialsProvider(proxyCredentialsProvider)
+ .proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12File("path/to/file").proxyP12FilePassKey("pass-key").build())
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void mustUseP12FileWithProxyMtls() {
+ SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12FilePassKey("pass-key").build())
+ .build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void mustUseP12PassKeyWithProxyMtls() {
+ SplitClientConfig.builder()
+ .proxyHost("proxy-host")
+ .proxyPort(8888)
+ .proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12File("path/to/file").build())
+ .build();
}
}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/client/SplitFactoryImplTest.java b/client/src/test/java/io/split/client/SplitFactoryImplTest.java
index a6da1069..1214b246 100644
--- a/client/src/test/java/io/split/client/SplitFactoryImplTest.java
+++ b/client/src/test/java/io/split/client/SplitFactoryImplTest.java
@@ -1,16 +1,25 @@
package io.split.client;
+import io.split.client.dtos.ProxyMTLSAuth;
import io.split.client.impressions.ImpressionsManager;
import io.split.client.utils.FileTypeEnum;
import io.split.integrations.IntegrationsConfig;
+import io.split.service.SplitHttpClientImpl;
import io.split.storages.enums.OperationMode;
import io.split.storages.pluggable.domain.UserStorageWrapper;
import io.split.telemetry.storage.TelemetryStorage;
import io.split.telemetry.synchronizer.TelemetrySynchronizer;
import junit.framework.TestCase;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.config.Registry;
import org.awaitility.Awaitility;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.when;
@@ -24,6 +33,8 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@@ -87,12 +98,12 @@ public void testFactoryInstantiationIntegrationsConfig() throws Exception {
}
@Test
- public void testFactoryInstantiationWithProxy() throws Exception {
+ public void testFactoryInstantiationWithProxyCredentials() throws Exception {
SplitClientConfig splitClientConfig = SplitClientConfig.builder()
.enableDebug()
.impressionsMode(ImpressionsManager.Mode.DEBUG)
.impressionsRefreshRate(1)
- .endpoint(ENDPOINT,EVENTS_ENDPOINT)
+ .endpoint(ENDPOINT, EVENTS_ENDPOINT)
.telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT)
.authServiceURL(AUTH_SERVICE)
.setBlockUntilReadyTimeout(1000)
@@ -102,9 +113,157 @@ public void testFactoryInstantiationWithProxy() throws Exception {
.proxyHost(ENDPOINT)
.build();
SplitFactoryImpl splitFactory = new SplitFactoryImpl(API_KEY, splitClientConfig);
-
assertNotNull(splitFactory.client());
assertNotNull(splitFactory.manager());
+
+ Field splitHttpClientField = SplitFactoryImpl.class.getDeclaredField("_splitHttpClient");
+ splitHttpClientField.setAccessible(true);
+ SplitHttpClientImpl client = (SplitHttpClientImpl) splitHttpClientField.get(splitFactory);
+
+ Field httpClientField = SplitHttpClientImpl.class.getDeclaredField("_client");
+ httpClientField.setAccessible(true);
+ Class> InternalHttp = Class.forName("org.apache.hc.client5.http.impl.classic.InternalHttpClient");
+
+ Field routePlannerField = InternalHttp.getDeclaredField("routePlanner");
+ routePlannerField.setAccessible(true);
+ DefaultProxyRoutePlanner routePlanner = (DefaultProxyRoutePlanner) routePlannerField.get(InternalHttp.cast(httpClientField.get(client)));
+
+ Field proxyField = DefaultProxyRoutePlanner.class.getDeclaredField("proxy");
+ proxyField.setAccessible(true);
+ HttpHost proxy = (HttpHost) proxyField.get(routePlanner);
+
+ Assert.assertEquals("http", proxy.getSchemeName());
+ Assert.assertEquals(ENDPOINT, proxy.getHostName());
+ Assert.assertEquals(6060, proxy.getPort());
+
+ Field credentialsProviderField = InternalHttp.getDeclaredField("credentialsProvider");
+ credentialsProviderField.setAccessible(true);
+ BasicCredentialsProvider credentialsProvider = (BasicCredentialsProvider) credentialsProviderField.get(InternalHttp.cast(httpClientField.get(client)));
+
+ Field credMapField = BasicCredentialsProvider.class.getDeclaredField("credMap");
+ credMapField.setAccessible(true);
+ ConcurrentHashMap credMap = (ConcurrentHashMap) credMapField.get(credentialsProvider);
+
+ Assert.assertEquals("test", credMap.entrySet().stream().iterator().next().getValue().getUserName());
+ assertNotNull(credMap.entrySet().stream().iterator().next().getValue().getUserPassword());
+
+ splitFactory.destroy();
+ }
+
+ @Test
+ public void testFactoryInstantiationWithProxyToken() throws Exception {
+ class MyProxyCredentialsProvider implements ProxyCredentialsProvider {
+ @Override
+ public String getJwtToken() {
+ return "123456789";
+ }
+ };
+
+ SplitClientConfig splitClientConfig = SplitClientConfig.builder()
+ .enableDebug()
+ .impressionsMode(ImpressionsManager.Mode.DEBUG)
+ .impressionsRefreshRate(1)
+ .endpoint(ENDPOINT, EVENTS_ENDPOINT)
+ .telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT)
+ .authServiceURL(AUTH_SERVICE)
+ .setBlockUntilReadyTimeout(1000)
+ .proxyPort(6060)
+ .proxyCredentialsProvider(new MyProxyCredentialsProvider())
+ .proxyHost(ENDPOINT)
+ .build();
+ SplitFactoryImpl splitFactory2 = new SplitFactoryImpl(API_KEY, splitClientConfig);
+ assertNotNull(splitFactory2.client());
+ assertNotNull(splitFactory2.manager());
+
+ Field splitHttpClientField2 = SplitFactoryImpl.class.getDeclaredField("_splitHttpClient");
+ splitHttpClientField2.setAccessible(true);
+ SplitHttpClientImpl client2 = (SplitHttpClientImpl) splitHttpClientField2.get(splitFactory2);
+
+ Field httpClientField2 = SplitHttpClientImpl.class.getDeclaredField("_client");
+ httpClientField2.setAccessible(true);
+ Class> InternalHttp2 = Class.forName("org.apache.hc.client5.http.impl.classic.InternalHttpClient");
+
+ Field credentialsProviderField2 = InternalHttp2.getDeclaredField("credentialsProvider");
+ credentialsProviderField2.setAccessible(true);
+ HttpClientDynamicCredentials credentialsProvider2 = (HttpClientDynamicCredentials) credentialsProviderField2.get(InternalHttp2.cast(httpClientField2.get(client2)));
+
+ Field proxyRuntimeField = HttpClientDynamicCredentials.class.getDeclaredField("_proxyCredentialsProvider");
+ proxyRuntimeField.setAccessible(true);
+ MyProxyCredentialsProvider proxyRuntime = (MyProxyCredentialsProvider) proxyRuntimeField.get(credentialsProvider2);
+
+ assertNotNull("123456789", proxyRuntime.getJwtToken());
+
+ splitFactory2.destroy();
+ }
+
+ @Test
+ public void testFactoryInstantiationWithProxyMtls() throws Exception {
+ SplitClientConfig splitClientConfig = SplitClientConfig.builder()
+ .enableDebug()
+ .impressionsMode(ImpressionsManager.Mode.DEBUG)
+ .impressionsRefreshRate(1)
+ .endpoint(ENDPOINT,EVENTS_ENDPOINT)
+ .telemetryURL(SplitClientConfig.TELEMETRY_ENDPOINT)
+ .authServiceURL(AUTH_SERVICE)
+ .setBlockUntilReadyTimeout(1000)
+ .proxyPort(6060)
+ .proxyScheme("https")
+ .proxyMtlsAuth(new ProxyMTLSAuth.Builder().proxyP12File("src/test/resources/keyStore.p12").proxyP12FilePassKey("split").build())
+ .proxyHost(ENDPOINT)
+ .build();
+ SplitFactoryImpl splitFactory3 = new SplitFactoryImpl(API_KEY, splitClientConfig);
+ assertNotNull(splitFactory3.client());
+ assertNotNull(splitFactory3.manager());
+
+ Field splitHttpClientField3 = SplitFactoryImpl.class.getDeclaredField("_splitHttpClient");
+ splitHttpClientField3.setAccessible(true);
+ SplitHttpClientImpl client3 = (SplitHttpClientImpl) splitHttpClientField3.get(splitFactory3);
+
+ Field httpClientField3 = SplitHttpClientImpl.class.getDeclaredField("_client");
+ httpClientField3.setAccessible(true);
+ Class> InternalHttp3 = Class.forName("org.apache.hc.client5.http.impl.classic.InternalHttpClient");
+
+ Field connManagerField = InternalHttp3.getDeclaredField("connManager");
+ connManagerField.setAccessible(true);
+ PoolingHttpClientConnectionManager connManager = (PoolingHttpClientConnectionManager) connManagerField.get(InternalHttp3.cast(httpClientField3.get(client3)));
+
+ Field connectionOperatorField = PoolingHttpClientConnectionManager.class.getDeclaredField("connectionOperator");
+ connectionOperatorField.setAccessible(true);
+ DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) connectionOperatorField.get(connManager);
+
+ Field tlsSocketStrategyLookupField = DefaultHttpClientConnectionOperator.class.getDeclaredField("tlsSocketStrategyLookup");
+ tlsSocketStrategyLookupField.setAccessible(true);
+ Registry tlsSocketStrategyLookup = (Registry) tlsSocketStrategyLookupField.get(connectionOperator);
+
+ Field mapField = Registry.class.getDeclaredField("map");
+ mapField.setAccessible(true);
+ Class> map = mapField.get(tlsSocketStrategyLookup).getClass();
+
+ Class> value = ((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https").getClass();
+
+ Field arg1Field = value.getDeclaredField("arg$1");
+ arg1Field.setAccessible(true);
+ Class> sslConnectionSocketFactory = arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https")).getClass();
+
+ Field socketFactoryField = sslConnectionSocketFactory.getDeclaredField("socketFactory");
+ socketFactoryField.setAccessible(true);
+ Class> socketFactory = socketFactoryField.get(arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https"))).getClass();
+
+ Field contextField = socketFactory.getDeclaredField("context");
+ contextField.setAccessible(true);
+ Class> context = Class.forName("sun.security.ssl.SSLContextImpl");
+
+ Field keyManagerField = context.getDeclaredField("keyManager");
+ keyManagerField.setAccessible(true);
+ Class> keyManager = keyManagerField.get(contextField.get(socketFactoryField.get(arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https"))))).getClass();
+
+ Field credentialsMapField = keyManager.getDeclaredField("credentialsMap");
+ credentialsMapField.setAccessible(true);
+ HashMap credentialsMap = (HashMap) credentialsMapField.get(keyManagerField.get(contextField.get(socketFactoryField.get(arg1Field.get(((ConcurrentHashMap) map.cast(mapField.get(tlsSocketStrategyLookup))).get("https"))))));
+
+ assertNotNull(credentialsMap.get("1"));
+
+ splitFactory3.destroy();
}
@Test
diff --git a/client/src/test/resources/keyStore.p12 b/client/src/test/resources/keyStore.p12
new file mode 100644
index 00000000..ce2b3417
Binary files /dev/null and b/client/src/test/resources/keyStore.p12 differ
diff --git a/okhttp-modules/pom.xml b/okhttp-modules/pom.xml
index 3a842c81..ff0bac2e 100644
--- a/okhttp-modules/pom.xml
+++ b/okhttp-modules/pom.xml
@@ -5,10 +5,10 @@
java-client-parent
io.split.client
- 4.16.0
+ 4.17.0-rc2
4.0.0
- 4.16.0
+ 4.17.0-rc2
okhttp-modules
jar
http-modules
@@ -25,7 +25,7 @@
0.8.0
true
- true
+ false
central
false
published
diff --git a/pluggable-storage/pom.xml b/pluggable-storage/pom.xml
index d5b1a995..0353d0c5 100644
--- a/pluggable-storage/pom.xml
+++ b/pluggable-storage/pom.xml
@@ -6,7 +6,7 @@
java-client-parent
io.split.client
- 4.16.0
+ 4.17.0-rc2
2.1.0
diff --git a/pom.xml b/pom.xml
index fe966220..cabb7d91 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.split.client
java-client-parent
- 4.16.0
+ 4.17.0-rc2
diff --git a/redis-wrapper/pom.xml b/redis-wrapper/pom.xml
index 64c5a115..0da0c71f 100644
--- a/redis-wrapper/pom.xml
+++ b/redis-wrapper/pom.xml
@@ -6,7 +6,7 @@
java-client-parent
io.split.client
- 4.16.0
+ 4.17.0-rc2
redis-wrapper
3.1.1
diff --git a/testing/pom.xml b/testing/pom.xml
index de58f526..f7316b7b 100644
--- a/testing/pom.xml
+++ b/testing/pom.xml
@@ -5,11 +5,11 @@
io.split.client
java-client-parent
- 4.16.0
+ 4.17.0-rc2
java-client-testing
jar
- 4.16.0
+ 4.17.0-rc2
Java Client For Testing
Testing suite for Java SDK for Split