Skip to content
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
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
5.4.0 (Sep 12, 2025)
- Added new configuration for Fallback Treatments, which allows setting a treatment value and optional config to be returned in place of "control", either globally or by flag. Read more in our docs.
- Added ProxyConfiguration parameter to support proxies, including Harness Forward Proxy, allowing also for more secured authentication options: MTLS, Bearer token and user/password authentication. Read more in our docs.

5.3.2 (Aug 20, 2025)
- Fixed issue with uncaught exception on flags preloading.

Expand Down
5 changes: 1 addition & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ apply from: 'spec.gradle'
apply from: 'jacoco.gradle'

ext {
splitVersion = '5.4.0-rc1'
splitVersion = '5.4.0'
jacocoVersion = '0.8.8'
}

Expand Down Expand Up @@ -284,6 +284,3 @@ tasks.withType(Test) {
forkEvery = 100
maxHeapSize = "1024m"
}



Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ public class HttpClientImpl implements HttpClient {
mSslSocketFactory = sslSocketFactory;
mUrlSanitizer = urlSanitizer;
mCertificateChecker = certificateChecker;
mConnectionHandler = mHttpProxy != null && mSslSocketFactory != null &&
(mHttpProxy.getCaCertStream() != null || mHttpProxy.getClientCertStream() != null) ?
mConnectionHandler = mHttpProxy != null && mSslSocketFactory != null ?
new ProxyCacertConnectionHandler() : null;
}

Expand Down Expand Up @@ -339,6 +338,9 @@ private SSLSocketFactory createSslSocketFactoryFromProxy(HttpProxy proxyParams)
if (caCertBytes != null) {
return factoryProvider.create(new ByteArrayInputStream(caCertBytes));
}
} else {
// No custom auth path
return factoryProvider.create(null);
}
} catch (Exception e) {
Logger.e("Failed to create SSLSocketFactory for proxy: " + proxyParams.getHost() + ", error: " + e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ static HttpURLConnection createConnection(@NonNull URL url,
@Nullable ProxyCredentialsProvider proxyCredentialsProvider,
@Nullable String body) throws IOException {

if (httpProxy != null && sslSocketFactory != null && (httpProxy.getCaCertStream() != null || httpProxy.getClientCertStream() != null)) {
// Use the new tunnel path only when there is no legacy authenticator present.
// If a legacy authenticator proxy, we prefer the legacy path to preserve 407 retry behavior.
if (httpProxy != null && sslSocketFactory != null && !httpProxy.isLegacy()) {
try {
HttpResponse response = mConnectionHandler.executeRequest(
httpProxy,
Expand Down Expand Up @@ -60,12 +62,11 @@ private static HttpURLConnection openConnection(@Nullable Proxy proxy,
@NonNull HttpMethod method,
@NonNull Map<String, String> headers,
boolean useProxyAuthentication) throws IOException {

// Check if we need custom SSL proxy handling
if (httpProxy != null && (httpProxy.getCaCertStream() != null || httpProxy.getClientCertStream() != null)) {

if (httpProxy != null && !httpProxy.isLegacy() && (httpProxy.getCaCertStream() != null || httpProxy.getClientCertStream() != null)) {
throw new IOException("SSL proxy scenarios require custom handling - use executeRequest method instead");
}

// Standard HttpURLConnection proxy handling
HttpURLConnection connection;
if (proxy != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private void closeBufferedReader() {
private HttpStreamResponse getRequest() throws HttpException, IOException {
HttpStreamResponse response;
try {
if (mConnectionHandler != null && mHttpProxy != null && mSslSocketFactory != null && (mHttpProxy.getCaCertStream() != null || mHttpProxy.getClientCertStream() != null)) {
if (mConnectionHandler != null && mHttpProxy != null && mSslSocketFactory != null) {
response = mConnectionHandler.executeStreamRequest(mHttpProxy, getUrl(), mHttpMethod, mHeaders, mSslSocketFactory, mProxyCredentialsProvider);
} else {
mConnection = setUpConnection(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ public MockResponse dispatch(RecordedRequest request) {
HttpClient client = new HttpClientImpl.Builder()
.setContext(mock(Context.class))
.setUrlSanitizer(mUrlSanitizerMock)
.setProxy(HttpProxy.newBuilder(mProxyServer.getHostName(), mProxyServer.getPort()).build())
.setProxy(HttpProxy.newBuilder(mProxyServer.getHostName(), mProxyServer.getPort()).buildLegacy())
.build();

HttpRequest request = client.request(mWebServer.url("/test1/").uri(), HttpMethod.GET);
Expand Down Expand Up @@ -325,7 +325,7 @@ public SplitAuthenticatedRequest authenticate(@NonNull SplitAuthenticatedRequest
return request;
}
})
.setProxy(HttpProxy.newBuilder(mProxyServer.getHostName(), mProxyServer.getPort()).build())
.setProxy(HttpProxy.newBuilder(mProxyServer.getHostName(), mProxyServer.getPort()).buildLegacy())
.build();

HttpRequest request = client.request(mWebServer.url("/test1/").uri(), HttpMethod.GET);
Expand Down Expand Up @@ -380,7 +380,7 @@ public SplitAuthenticatedRequest authenticate(@NonNull SplitAuthenticatedRequest
return request;
}
})
.setProxy(HttpProxy.newBuilder(mProxyServer.getHostName(), mProxyServer.getPort()).build())
.setProxy(HttpProxy.newBuilder(mProxyServer.getHostName(), mProxyServer.getPort()).buildLegacy())
.build();

HttpRequest request = client.request(mWebServer.url("/test1/").uri(), HttpMethod.POST, "{}");
Expand Down
Loading