Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3554e17
Merge pull request #471 from splitio/development
nmayorsplit Jan 9, 2024
63c29cf
Merge pull request #476 from splitio/development
nmayorsplit Feb 29, 2024
9d58e00
added request decorator class
Mar 12, 2024
0d41c4f
polish
Mar 12, 2024
a335a4a
Merge pull request #477 from splitio/custom-auth-decorator
chillaq Mar 12, 2024
cf1bd48
added httpclient wrapper
Mar 12, 2024
a372032
polish
Mar 12, 2024
5ae5405
fixed tests and added splithttpclient interface
Mar 12, 2024
1958b82
added splithttpresponse dto
Mar 13, 2024
33e0454
removed exception when status is not 200
Mar 13, 2024
f9a6e94
fixed test
Mar 13, 2024
a9c3eab
updated split change fetcher
Mar 13, 2024
12633b1
Merge pull request #478 from splitio/custom-auth-httpclient
chillaq Mar 13, 2024
8041204
moved flagset warning out of splithttp client and moved the client cl…
Mar 13, 2024
ab42b94
updated segment fetcher class
Mar 14, 2024
7dd45c2
polish
Mar 14, 2024
0675dc4
polish
Mar 14, 2024
a6d725c
updated impression sender class
Mar 14, 2024
d10e3dc
polish
Mar 14, 2024
6e0c64b
polishMerge branch 'Feature/CustomAuth' of github.com:splitio/java-cl…
Mar 14, 2024
827e501
Merge pull request #480 from splitio/custom-auth-segmentfetcher
chillaq Mar 14, 2024
3b4147a
Merge pull request #481 from splitio/custom-auth-impressionsender
chillaq Mar 18, 2024
b8fdfdd
updated Telemetry submitter and sender classes
Mar 18, 2024
61446c8
fixed tests
Mar 18, 2024
67a4f16
Merge pull request #483 from splitio/custom-auth-telemetrysender
chillaq Mar 18, 2024
7ca4ed8
updated factory and config
Mar 18, 2024
19013dc
fixed tests
Mar 18, 2024
106ad38
Merge branch 'Feature/CustomAuth' into custom-auth-factory
chillaq Mar 18, 2024
50ba346
polish
Mar 18, 2024
dd5004b
Merge pull request #484 from splitio/custom-auth-factory
chillaq Mar 19, 2024
6baef40
updated sse classes
Mar 19, 2024
feadd1a
Merge pull request #485 from splitio/custom-auth-sse
chillaq Mar 19, 2024
c04dbfc
Updated version and changes
Mar 20, 2024
77eaedc
Merge pull request #486 from splitio/custom-auth-dev
chillaq Mar 20, 2024
58dcfad
polishing
Mar 20, 2024
72679b9
polish
Mar 20, 2024
02b802f
polish
Mar 20, 2024
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
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
4.12.0 (XXX XX, 2024)
- Added support for injecting user-defined custom headers for all outgoing HTTP calls, typically useful for proxy authentication purposes.

4.11.1 (Feb 29, 2024)
- Fixed deadlock in UniqueKeysTracker when sending Unique Keys.

Expand Down
2 changes: 1 addition & 1 deletion client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>io.split.client</groupId>
<artifactId>java-client-parent</artifactId>
<version>4.11.1</version>
<version>4.12.0-rc1</version>
</parent>
<artifactId>java-client</artifactId>
<packaging>jar</packaging>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,23 @@

import com.google.common.annotations.VisibleForTesting;
import io.split.client.dtos.SegmentChange;
import io.split.client.dtos.SplitHttpResponse;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
import io.split.engine.common.FetchOptions;
import io.split.engine.segments.SegmentChangeFetcher;
import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.domain.enums.ResourceEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;

import static com.google.common.base.Preconditions.checkNotNull;

Expand All @@ -33,20 +30,17 @@ public final class HttpSegmentChangeFetcher implements SegmentChangeFetcher {

private static final String SINCE = "since";
private static final String TILL = "till";
private static final String PREFIX = "segmentChangeFetcher";
private static final String CACHE_CONTROL_HEADER_NAME = "Cache-Control";
private static final String CACHE_CONTROL_HEADER_VALUE = "no-cache";

private final CloseableHttpClient _client;
private final SplitHttpClient _client;
private final URI _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;

public static HttpSegmentChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
public static HttpSegmentChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
throws URISyntaxException {
return new HttpSegmentChangeFetcher(client, Utils.appendPath(root, "api/segmentChanges"), telemetryRuntimeProducer);
}

private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
private HttpSegmentChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_target = uri;
checkNotNull(_target);
Expand All @@ -57,7 +51,7 @@ private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, TelemetryR
public SegmentChange fetch(String segmentName, long since, FetchOptions options) {
long start = System.currentTimeMillis();

CloseableHttpResponse response = null;
SplitHttpResponse response = null;

try {
String path = _target.getPath() + "/" + segmentName;
Expand All @@ -69,42 +63,27 @@ public SegmentChange fetch(String segmentName, long since, FetchOptions options)
}

URI uri = uriBuilder.build();
HttpGet request = new HttpGet(uri);

if(options.cacheControlHeadersEnabled()) {
request.setHeader(CACHE_CONTROL_HEADER_NAME, CACHE_CONTROL_HEADER_VALUE);
}

response = _client.execute(request);

int statusCode = response.getCode();

if (_log.isDebugEnabled()) {
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
}
response = _client.get(uri, options);
int statusCode = response.statusCode;

if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SEGMENT_SYNC, statusCode);
_log.error(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
if (statusCode == HttpStatus.SC_FORBIDDEN) {
_log.error("factory instantiation: you passed a client side type sdkKey, " +
"please grab an sdk key from the Split user interface that is of type server side");
}
throw new IllegalStateException(String.format("Could not retrieve segment changes for %s, since %s; http return code %s",
segmentName, since, statusCode));
}

_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.SEGMENTS, System.currentTimeMillis());

String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

return Json.fromJson(json, SegmentChange.class);
return Json.fromJson(response.body, SegmentChange.class);
} catch (Exception e) {
throw new IllegalStateException(String.format("Error occurred when trying to sync segment: %s, since: %s. Details: %s",
segmentName, since, e), e);
} finally {
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SEGMENTS, System.currentTimeMillis()-start);
Utils.forceClose(response);
}


Expand Down
43 changes: 11 additions & 32 deletions client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,23 @@

import com.google.common.annotations.VisibleForTesting;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.SplitHttpResponse;
import io.split.client.exceptions.UriTooLongException;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
import io.split.engine.common.FetchOptions;
import io.split.engine.experiments.SplitChangeFetcher;
import io.split.service.SplitHttpClient;
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
import io.split.telemetry.domain.enums.ResourceEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;

import static com.google.common.base.Preconditions.checkNotNull;

Expand All @@ -34,20 +31,16 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
private static final String SINCE = "since";
private static final String TILL = "till";
private static final String SETS = "sets";

private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";

private final CloseableHttpClient _client;
private final SplitHttpClient _client;
private final URI _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;

public static HttpSplitChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
public static HttpSplitChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
throws URISyntaxException {
return new HttpSplitChangeFetcher(client, Utils.appendPath(root, "api/splitChanges"), telemetryRuntimeProducer);
}

private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_client = client;
_target = uri;
checkNotNull(_target);
Expand All @@ -64,7 +57,7 @@ public SplitChange fetch(long since, FetchOptions options) {

long start = System.currentTimeMillis();

CloseableHttpResponse response = null;
SplitHttpResponse response = null;

try {
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SINCE, "" + since);
Expand All @@ -75,38 +68,24 @@ public SplitChange fetch(long since, FetchOptions options) {
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
}
URI uri = uriBuilder.build();
response = _client.get(uri, options);

HttpGet request = new HttpGet(uri);
if(options.cacheControlHeadersEnabled()) {
request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
}

response = _client.execute(request);

int statusCode = response.getCode();

if (_log.isDebugEnabled()) {
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
}
int statusCode = response.statusCode;

if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, statusCode);
if (statusCode == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
_log.error("The amount of flag sets provided are big causing uri length error.");
throw new UriTooLongException(String.format("Status code: %s. Message: %s", statusCode, response.getReasonPhrase()));
throw new UriTooLongException(String.format("Status code: %s. Message: %s", statusCode, response.statusMessage));
}
_log.warn(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, statusCode);
throw new IllegalStateException(String.format("Could not retrieve splitChanges since %s; http return code %s", since, statusCode));
}

String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);

return Json.fromJson(json, SplitChange.class);
return Json.fromJson(response.body, SplitChange.class);
} catch (Exception e) {
throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e);
} finally {
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis()-start);
Utils.forceClose(response);
}
}

Expand Down
60 changes: 60 additions & 0 deletions client/src/main/java/io/split/client/RequestDecorator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.split.client;

import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.Set;

class NoOpHeaderDecorator implements UserCustomHeaderDecorator {
public NoOpHeaderDecorator() {}
@Override
public Map<String, String> getHeaderOverrides() {
return new HashMap<>();
}
}

public final class RequestDecorator {
UserCustomHeaderDecorator _headerDecorator;

private static final Set<String> forbiddenHeaders = new HashSet<>(Arrays.asList(
"SplitSDKVersion",
"SplitMachineIp",
"SplitMachineName",
"SplitImpressionsMode",
"Host",
"Referrer",
"Content-Type",
"Content-Length",
"Content-Encoding",
"Accept",
"Keep-Alive",
"X-Fastly-Debug"
));

public RequestDecorator(UserCustomHeaderDecorator headerDecorator) {
_headerDecorator = (headerDecorator == null)
? new NoOpHeaderDecorator()
: headerDecorator;
}

public HttpUriRequestBase decorateHeaders(HttpUriRequestBase request) {
try {
Map<String, String> headers = _headerDecorator.getHeaderOverrides();
for (Map.Entry entry : headers.entrySet()) {
if (isHeaderAllowed(entry.getKey().toString())) {
request.addHeader(entry.getKey().toString(), entry.getValue());
}
}
} catch (Exception e) {
throw new IllegalArgumentException(String.format("Problem adding custom headers to request decorator: %s", e), e);
}

return request;
}

private boolean isHeaderAllowed(String headerName) {
return !forbiddenHeaders.contains(headerName);
}
}
27 changes: 24 additions & 3 deletions client/src/main/java/io/split/client/SplitClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.ThreadFactory;
import java.io.InputStream;

import static io.split.inputValidation.FlagSetsValidator.cleanup;

Expand Down Expand Up @@ -90,6 +90,8 @@ public class SplitClientConfig {
private final long _lastSeenCacheSize;
private final HashSet<String> _flagSetsFilter;
private final int _invalidSets;
private final UserCustomHeaderDecorator _userCustomHeaderDecorator;


public static Builder builder() {
return new Builder();
Expand Down Expand Up @@ -145,7 +147,8 @@ private SplitClientConfig(String endpoint,
long lastSeenCacheSize,
ThreadFactory threadFactory,
HashSet<String> flagSetsFilter,
int invalidSets) {
int invalidSets,
UserCustomHeaderDecorator userCustomHeaderDecorator) {
_endpoint = endpoint;
_eventsEndpoint = eventsEndpoint;
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
Expand Down Expand Up @@ -197,6 +200,7 @@ private SplitClientConfig(String endpoint,
_threadFactory = threadFactory;
_flagSetsFilter = flagSetsFilter;
_invalidSets = invalidSets;
_userCustomHeaderDecorator = userCustomHeaderDecorator;

Properties props = new Properties();
try {
Expand Down Expand Up @@ -393,6 +397,10 @@ public int getInvalidSets() {
return _invalidSets;
}

public UserCustomHeaderDecorator userCustomHeaderDecorator() {
return _userCustomHeaderDecorator;
}

public static final class Builder {

private String _endpoint = SDK_ENDPOINT;
Expand Down Expand Up @@ -449,6 +457,7 @@ public static final class Builder {
private ThreadFactory _threadFactory;
private HashSet<String> _flagSetsFilter = new HashSet<>();
private int _invalidSetsCount = 0;
private UserCustomHeaderDecorator _userCustomHeaderDecorator = null;

public Builder() {
}
Expand Down Expand Up @@ -932,6 +941,17 @@ public Builder flagSetsFilter(List<String> flagSetsFilter) {
return this;
}

/**
* User Custom Header Decorator
*
* @param userCustomHeaderDecorator
* @return this builder
*/
public Builder userCustomHeaderDecorator(UserCustomHeaderDecorator userCustomHeaderDecorator) {
_userCustomHeaderDecorator = userCustomHeaderDecorator;
return this;
}

/**
* Thread Factory
*
Expand Down Expand Up @@ -1091,7 +1111,8 @@ public SplitClientConfig build() {
_lastSeenCacheSize,
_threadFactory,
_flagSetsFilter,
_invalidSetsCount);
_invalidSetsCount,
_userCustomHeaderDecorator);
}
}
}
Loading