Skip to content

Commit 43489fe

Browse files
authored
Merge pull request #487 from splitio/Feature/CustomAuth
Feature/custom auth
2 parents 7e192fb + 02b802f commit 43489fe

32 files changed

+697
-192
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
4.12.0 (XXX XX, 2024)
2+
- Added support for injecting user-defined custom headers for all outgoing HTTP calls, typically useful for proxy authentication purposes.
3+
14
4.11.1 (Feb 29, 2024)
25
- Fixed deadlock in UniqueKeysTracker when sending Unique Keys.
36

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>io.split.client</groupId>
77
<artifactId>java-client-parent</artifactId>
8-
<version>4.11.1</version>
8+
<version>4.12.0-rc1</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>

client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,23 @@
22

33
import com.google.common.annotations.VisibleForTesting;
44
import io.split.client.dtos.SegmentChange;
5+
import io.split.client.dtos.SplitHttpResponse;
56
import io.split.client.utils.Json;
67
import io.split.client.utils.Utils;
78
import io.split.engine.common.FetchOptions;
89
import io.split.engine.segments.SegmentChangeFetcher;
10+
import io.split.service.SplitHttpClient;
911
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
1012
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
1113
import io.split.telemetry.domain.enums.ResourceEnum;
1214
import io.split.telemetry.storage.TelemetryRuntimeProducer;
13-
import org.apache.hc.client5.http.classic.methods.HttpGet;
14-
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
15-
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
1615
import org.apache.hc.core5.http.HttpStatus;
17-
import org.apache.hc.core5.http.io.entity.EntityUtils;
1816
import org.apache.hc.core5.net.URIBuilder;
1917
import org.slf4j.Logger;
2018
import org.slf4j.LoggerFactory;
2119

2220
import java.net.URI;
2321
import java.net.URISyntaxException;
24-
import java.nio.charset.StandardCharsets;
2522

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

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

3431
private static final String SINCE = "since";
3532
private static final String TILL = "till";
36-
private static final String PREFIX = "segmentChangeFetcher";
37-
private static final String CACHE_CONTROL_HEADER_NAME = "Cache-Control";
38-
private static final String CACHE_CONTROL_HEADER_VALUE = "no-cache";
3933

40-
private final CloseableHttpClient _client;
34+
private final SplitHttpClient _client;
4135
private final URI _target;
4236
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
4337

44-
public static HttpSegmentChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
38+
public static HttpSegmentChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
4539
throws URISyntaxException {
4640
return new HttpSegmentChangeFetcher(client, Utils.appendPath(root, "api/segmentChanges"), telemetryRuntimeProducer);
4741
}
4842

49-
private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
43+
private HttpSegmentChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
5044
_client = client;
5145
_target = uri;
5246
checkNotNull(_target);
@@ -57,7 +51,7 @@ private HttpSegmentChangeFetcher(CloseableHttpClient client, URI uri, TelemetryR
5751
public SegmentChange fetch(String segmentName, long since, FetchOptions options) {
5852
long start = System.currentTimeMillis();
5953

60-
CloseableHttpResponse response = null;
54+
SplitHttpResponse response = null;
6155

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

7165
URI uri = uriBuilder.build();
72-
HttpGet request = new HttpGet(uri);
7366

74-
if(options.cacheControlHeadersEnabled()) {
75-
request.setHeader(CACHE_CONTROL_HEADER_NAME, CACHE_CONTROL_HEADER_VALUE);
76-
}
77-
78-
response = _client.execute(request);
79-
80-
int statusCode = response.getCode();
81-
82-
if (_log.isDebugEnabled()) {
83-
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
84-
}
67+
response = _client.get(uri, options);
68+
int statusCode = response.statusCode;
8569

8670
if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
8771
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SEGMENT_SYNC, statusCode);
88-
_log.error(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
8972
if (statusCode == HttpStatus.SC_FORBIDDEN) {
9073
_log.error("factory instantiation: you passed a client side type sdkKey, " +
9174
"please grab an sdk key from the Split user interface that is of type server side");
9275
}
9376
throw new IllegalStateException(String.format("Could not retrieve segment changes for %s, since %s; http return code %s",
9477
segmentName, since, statusCode));
9578
}
96-
9779
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.SEGMENTS, System.currentTimeMillis());
9880

99-
String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
100-
101-
return Json.fromJson(json, SegmentChange.class);
81+
return Json.fromJson(response.body, SegmentChange.class);
10282
} catch (Exception e) {
10383
throw new IllegalStateException(String.format("Error occurred when trying to sync segment: %s, since: %s. Details: %s",
10484
segmentName, since, e), e);
10585
} finally {
10686
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SEGMENTS, System.currentTimeMillis()-start);
107-
Utils.forceClose(response);
10887
}
10988

11089

client/src/main/java/io/split/client/HttpSplitChangeFetcher.java

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,23 @@
22

33
import com.google.common.annotations.VisibleForTesting;
44
import io.split.client.dtos.SplitChange;
5+
import io.split.client.dtos.SplitHttpResponse;
56
import io.split.client.exceptions.UriTooLongException;
67
import io.split.client.utils.Json;
78
import io.split.client.utils.Utils;
89
import io.split.engine.common.FetchOptions;
910
import io.split.engine.experiments.SplitChangeFetcher;
11+
import io.split.service.SplitHttpClient;
1012
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
1113
import io.split.telemetry.domain.enums.ResourceEnum;
1214
import io.split.telemetry.storage.TelemetryRuntimeProducer;
13-
import org.apache.hc.client5.http.classic.methods.HttpGet;
14-
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
15-
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
1615
import org.apache.hc.core5.http.HttpStatus;
17-
import org.apache.hc.core5.http.io.entity.EntityUtils;
1816
import org.apache.hc.core5.net.URIBuilder;
1917
import org.slf4j.Logger;
2018
import org.slf4j.LoggerFactory;
2119

2220
import java.net.URI;
2321
import java.net.URISyntaxException;
24-
import java.nio.charset.StandardCharsets;
2522

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

@@ -34,20 +31,16 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
3431
private static final String SINCE = "since";
3532
private static final String TILL = "till";
3633
private static final String SETS = "sets";
37-
38-
private static final String HEADER_CACHE_CONTROL_NAME = "Cache-Control";
39-
private static final String HEADER_CACHE_CONTROL_VALUE = "no-cache";
40-
41-
private final CloseableHttpClient _client;
34+
private final SplitHttpClient _client;
4235
private final URI _target;
4336
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
4437

45-
public static HttpSplitChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
38+
public static HttpSplitChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
4639
throws URISyntaxException {
4740
return new HttpSplitChangeFetcher(client, Utils.appendPath(root, "api/splitChanges"), telemetryRuntimeProducer);
4841
}
4942

50-
private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
43+
private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
5144
_client = client;
5245
_target = uri;
5346
checkNotNull(_target);
@@ -64,7 +57,7 @@ public SplitChange fetch(long since, FetchOptions options) {
6457

6558
long start = System.currentTimeMillis();
6659

67-
CloseableHttpResponse response = null;
60+
SplitHttpResponse response = null;
6861

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

79-
HttpGet request = new HttpGet(uri);
80-
if(options.cacheControlHeadersEnabled()) {
81-
request.setHeader(HEADER_CACHE_CONTROL_NAME, HEADER_CACHE_CONTROL_VALUE);
82-
}
83-
84-
response = _client.execute(request);
85-
86-
int statusCode = response.getCode();
87-
88-
if (_log.isDebugEnabled()) {
89-
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
90-
}
73+
int statusCode = response.statusCode;
9174

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

102-
String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
103-
104-
return Json.fromJson(json, SplitChange.class);
84+
return Json.fromJson(response.body, SplitChange.class);
10585
} catch (Exception e) {
10686
throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e);
10787
} finally {
10888
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis()-start);
109-
Utils.forceClose(response);
11089
}
11190
}
11291

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package io.split.client;
2+
3+
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
4+
import java.util.HashSet;
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
import java.util.Arrays;
8+
import java.util.Set;
9+
10+
class NoOpHeaderDecorator implements UserCustomHeaderDecorator {
11+
public NoOpHeaderDecorator() {}
12+
@Override
13+
public Map<String, String> getHeaderOverrides() {
14+
return new HashMap<>();
15+
}
16+
}
17+
18+
public final class RequestDecorator {
19+
UserCustomHeaderDecorator _headerDecorator;
20+
21+
private static final Set<String> forbiddenHeaders = new HashSet<>(Arrays.asList(
22+
"SplitSDKVersion",
23+
"SplitMachineIp",
24+
"SplitMachineName",
25+
"SplitImpressionsMode",
26+
"Host",
27+
"Referrer",
28+
"Content-Type",
29+
"Content-Length",
30+
"Content-Encoding",
31+
"Accept",
32+
"Keep-Alive",
33+
"X-Fastly-Debug"
34+
));
35+
36+
public RequestDecorator(UserCustomHeaderDecorator headerDecorator) {
37+
_headerDecorator = (headerDecorator == null)
38+
? new NoOpHeaderDecorator()
39+
: headerDecorator;
40+
}
41+
42+
public HttpUriRequestBase decorateHeaders(HttpUriRequestBase request) {
43+
try {
44+
Map<String, String> headers = _headerDecorator.getHeaderOverrides();
45+
for (Map.Entry entry : headers.entrySet()) {
46+
if (isHeaderAllowed(entry.getKey().toString())) {
47+
request.addHeader(entry.getKey().toString(), entry.getValue());
48+
}
49+
}
50+
} catch (Exception e) {
51+
throw new IllegalArgumentException(String.format("Problem adding custom headers to request decorator: %s", e), e);
52+
}
53+
54+
return request;
55+
}
56+
57+
private boolean isHeaderAllowed(String headerName) {
58+
return !forbiddenHeaders.contains(headerName);
59+
}
60+
}

client/src/main/java/io/split/client/SplitClientConfig.java

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
import java.util.HashSet;
1414
import java.util.LinkedHashSet;
1515
import java.util.List;
16-
import java.io.InputStream;
1716
import java.util.Properties;
1817
import java.util.concurrent.ThreadFactory;
18+
import java.io.InputStream;
1919

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

@@ -90,6 +90,8 @@ public class SplitClientConfig {
9090
private final long _lastSeenCacheSize;
9191
private final HashSet<String> _flagSetsFilter;
9292
private final int _invalidSets;
93+
private final UserCustomHeaderDecorator _userCustomHeaderDecorator;
94+
9395

9496
public static Builder builder() {
9597
return new Builder();
@@ -145,7 +147,8 @@ private SplitClientConfig(String endpoint,
145147
long lastSeenCacheSize,
146148
ThreadFactory threadFactory,
147149
HashSet<String> flagSetsFilter,
148-
int invalidSets) {
150+
int invalidSets,
151+
UserCustomHeaderDecorator userCustomHeaderDecorator) {
149152
_endpoint = endpoint;
150153
_eventsEndpoint = eventsEndpoint;
151154
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
@@ -197,6 +200,7 @@ private SplitClientConfig(String endpoint,
197200
_threadFactory = threadFactory;
198201
_flagSetsFilter = flagSetsFilter;
199202
_invalidSets = invalidSets;
203+
_userCustomHeaderDecorator = userCustomHeaderDecorator;
200204

201205
Properties props = new Properties();
202206
try {
@@ -393,6 +397,10 @@ public int getInvalidSets() {
393397
return _invalidSets;
394398
}
395399

400+
public UserCustomHeaderDecorator userCustomHeaderDecorator() {
401+
return _userCustomHeaderDecorator;
402+
}
403+
396404
public static final class Builder {
397405

398406
private String _endpoint = SDK_ENDPOINT;
@@ -449,6 +457,7 @@ public static final class Builder {
449457
private ThreadFactory _threadFactory;
450458
private HashSet<String> _flagSetsFilter = new HashSet<>();
451459
private int _invalidSetsCount = 0;
460+
private UserCustomHeaderDecorator _userCustomHeaderDecorator = null;
452461

453462
public Builder() {
454463
}
@@ -932,6 +941,17 @@ public Builder flagSetsFilter(List<String> flagSetsFilter) {
932941
return this;
933942
}
934943

944+
/**
945+
* User Custom Header Decorator
946+
*
947+
* @param userCustomHeaderDecorator
948+
* @return this builder
949+
*/
950+
public Builder userCustomHeaderDecorator(UserCustomHeaderDecorator userCustomHeaderDecorator) {
951+
_userCustomHeaderDecorator = userCustomHeaderDecorator;
952+
return this;
953+
}
954+
935955
/**
936956
* Thread Factory
937957
*
@@ -1091,7 +1111,8 @@ public SplitClientConfig build() {
10911111
_lastSeenCacheSize,
10921112
_threadFactory,
10931113
_flagSetsFilter,
1094-
_invalidSetsCount);
1114+
_invalidSetsCount,
1115+
_userCustomHeaderDecorator);
10951116
}
10961117
}
10971118
}

0 commit comments

Comments
 (0)