Skip to content

Updated SplitClientConfig #574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 94 additions & 2 deletions client/src/main/java/io/split/client/SplitClientConfig.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,6 +35,11 @@ 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 {
public static final String HTTP = "http";
public static final String HTTPS = "https";
}

private final String _endpoint;
private final String _eventsEndpoint;

Expand Down Expand Up @@ -85,6 +91,8 @@ public class SplitClientConfig {
private final HttpHost _proxy;
private final String _proxyUsername;
private final String _proxyPassword;
private final String _proxyToken;
private final ProxyMTLSAuth _proxyMtlsAuth;

// To be set during startup
public static String splitSdkVersion;
Expand Down Expand Up @@ -118,6 +126,8 @@ private SplitClientConfig(String endpoint,
HttpHost proxy,
String proxyUsername,
String proxyPassword,
String proxyToken,
ProxyMTLSAuth proxyMtlsAuth,
int eventsQueueSize,
long eventSendIntervalInMillis,
int maxStringLength,
Expand Down Expand Up @@ -171,6 +181,8 @@ private SplitClientConfig(String endpoint,
_proxy = proxy;
_proxyUsername = proxyUsername;
_proxyPassword = proxyPassword;
_proxyToken = proxyToken;
_proxyMtlsAuth = proxyMtlsAuth;
_eventsQueueSize = eventsQueueSize;
_eventSendIntervalInMillis = eventSendIntervalInMillis;
_maxStringLength = maxStringLength;
Expand Down Expand Up @@ -302,6 +314,14 @@ public String proxyPassword() {
return _proxyPassword;
}

public String proxyToken() {
return _proxyToken;
}

public ProxyMTLSAuth proxyMTLSAuth() {
return _proxyMtlsAuth;
}

public long eventSendIntervalInMillis() {
return _eventSendIntervalInMillis;
}
Expand Down Expand Up @@ -417,8 +437,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;
Expand All @@ -440,8 +460,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 String _proxyToken;
private ProxyMTLSAuth _proxyMtlsAuth;
private int _eventsQueueSize = 500;
private long _eventSendIntervalInMillis = 30 * (long)1000;
private int _maxStringLength = 250;
Expand Down Expand Up @@ -754,6 +777,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).
*
Expand All @@ -776,6 +810,28 @@ public Builder proxyPassword(String proxyPassword) {
return this;
}

/**
* Set the token for authentication against the proxy (if proxy settings are enabled). (Optional).
*
* @param proxyToken
* @return this builder
*/
public Builder proxyToken(String proxyToken) {
_proxyToken = proxyToken;
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.
*
Expand All @@ -788,7 +844,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;
Expand Down Expand Up @@ -1096,6 +1152,38 @@ 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 && _proxyToken == null && _proxyMtlsAuth == null) {
return;
}

if (_proxyUsername != null && _proxyToken != 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 (_proxyToken != null && _proxyMtlsAuth != null) {
throw new IllegalArgumentException("Proxy token and Proxy mTLS params are updated, set only one param.");
}

if (_proxyMtlsAuth != null) {
if (_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();
Expand All @@ -1108,6 +1196,8 @@ public SplitClientConfig build() {

verifyAlternativeClient();

verifyProxy();

if (_numThreadsForSegmentFetch <= 0) {
throw new IllegalArgumentException("Number of threads for fetching segments MUST be greater than zero");
}
Expand All @@ -1133,6 +1223,8 @@ public SplitClientConfig build() {
proxy(),
_proxyUsername,
_proxyPassword,
_proxyToken,
_proxyMtlsAuth,
_eventsQueueSize,
_eventSendIntervalInMillis,
_maxStringLength,
Expand Down
41 changes: 41 additions & 0 deletions client/src/main/java/io/split/client/dtos/ProxyMTLSAuth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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 Builder() {
}

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);
}
}
}
93 changes: 93 additions & 0 deletions client/src/test/java/io/split/client/SplitClientConfigTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -252,6 +253,98 @@ public Map<String, List<String>> 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());

config = SplitClientConfig.builder()
.proxyHost("proxy-host")
.proxyPort(8888)
.proxyToken("my-token")
.build();
Assert.assertEquals("my-token", config.proxyToken());

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() {
SplitClientConfig.builder()
.proxyHost("proxy-host")
.proxyPort(8888)
.proxyUsername("user")
.proxyPassword("pass")
.proxyToken("my-token")
.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() {
SplitClientConfig.builder()
.proxyHost("proxy-host")
.proxyPort(8888)
.proxyToken("my-token")
.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();
}
}