Skip to content

Commit e84af2d

Browse files
authored
Merge pull request #506 from splitio/development
Semver release
2 parents 63c29cf + 426ea5a commit e84af2d

File tree

116 files changed

+3369
-957
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+3369
-957
lines changed

CHANGES.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
4.12.0 (May 15, 2024)
2+
- Added support for targeting rules based on semantic versions (https://semver.org/).
3+
- Added the logic to handle correctly when the SDK receives an unsupported Matcher type.
4+
- Enhanced SDK Headers for Authorization Frameworks
5+
- Cleaned unused imports and renaming some methods
6+
- Fixed empty token handler thanks to @hpark-miovision
7+
18
4.11.1 (Feb 29, 2024)
29
- Fixed deadlock in UniqueKeysTracker when sending Unique Keys.
310

@@ -9,7 +16,7 @@
916
- Added getTreatmentsByFlagSets without attributes.
1017
- Fixed some issues for flag sets: Not logging a warning when using flag sets that don't contain cached feature flags.
1118

12-
4.10.1 (Nov 9, 2023)
19+
4.10.1 (Nov 8, 2023)
1320
- Fixed handler for response http headers.
1421

1522
4.10.0 (Nov 2, 2023)

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</version>
99
</parent>
1010
<artifactId>java-client</artifactId>
1111
<packaging>jar</packaging>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.split;
2+
3+
public final class Spec {
4+
5+
private Spec() {
6+
// restrict instantiation
7+
}
8+
9+
public static final String SPEC_VERSION = "1.1";
10+
}
11+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.split.client;
2+
3+
import io.split.client.dtos.RequestContext;
4+
5+
import java.util.Map;
6+
import java.util.List;
7+
8+
public interface CustomHeaderDecorator
9+
{
10+
/**
11+
* Get the additional headers needed for all http operations
12+
* @return HashMap of addition headers
13+
*/
14+
Map<String, List<String>> getHeaderOverrides(RequestContext context);
15+
}

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

Lines changed: 11 additions & 37 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,8 +51,6 @@ 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;
61-
6254
try {
6355
String path = _target.getPath() + "/" + segmentName;
6456
URIBuilder uriBuilder = new URIBuilder(_target)
@@ -69,45 +61,27 @@ public SegmentChange fetch(String segmentName, long since, FetchOptions options)
6961
}
7062

7163
URI uri = uriBuilder.build();
72-
HttpGet request = new HttpGet(uri);
73-
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();
8164

82-
if (_log.isDebugEnabled()) {
83-
_log.debug(String.format("[%s] %s. Status code: %s", request.getMethod(), uri.toURL(), statusCode));
84-
}
65+
SplitHttpResponse response = _client.get(uri, options, null);
8566

86-
if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
87-
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SEGMENT_SYNC, statusCode);
88-
_log.error(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
89-
if (statusCode == HttpStatus.SC_FORBIDDEN) {
67+
if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
68+
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SEGMENT_SYNC, response.statusCode());
69+
if (response.statusCode() == HttpStatus.SC_FORBIDDEN) {
9070
_log.error("factory instantiation: you passed a client side type sdkKey, " +
9171
"please grab an sdk key from the Split user interface that is of type server side");
9272
}
9373
throw new IllegalStateException(String.format("Could not retrieve segment changes for %s, since %s; http return code %s",
94-
segmentName, since, statusCode));
74+
segmentName, since, response.statusCode()));
9575
}
96-
9776
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.SEGMENTS, System.currentTimeMillis());
9877

99-
String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
100-
101-
return Json.fromJson(json, SegmentChange.class);
78+
return Json.fromJson(response.body(), SegmentChange.class);
10279
} catch (Exception e) {
10380
throw new IllegalStateException(String.format("Error occurred when trying to sync segment: %s, since: %s. Details: %s",
10481
segmentName, since, e), e);
10582
} finally {
10683
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SEGMENTS, System.currentTimeMillis()-start);
107-
Utils.forceClose(response);
10884
}
109-
110-
11185
}
11286

11387
@VisibleForTesting

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

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
package io.split.client;
22

33
import com.google.common.annotations.VisibleForTesting;
4+
45
import io.split.client.dtos.SplitChange;
6+
import io.split.client.dtos.SplitHttpResponse;
57
import io.split.client.exceptions.UriTooLongException;
68
import io.split.client.utils.Json;
79
import io.split.client.utils.Utils;
810
import io.split.engine.common.FetchOptions;
911
import io.split.engine.experiments.SplitChangeFetcher;
12+
import io.split.service.SplitHttpClient;
1013
import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
1114
import io.split.telemetry.domain.enums.ResourceEnum;
1215
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;
1616
import org.apache.hc.core5.http.HttpStatus;
17-
import org.apache.hc.core5.http.io.entity.EntityUtils;
1817
import org.apache.hc.core5.net.URIBuilder;
1918
import org.slf4j.Logger;
2019
import org.slf4j.LoggerFactory;
2120

2221
import java.net.URI;
2322
import java.net.URISyntaxException;
24-
import java.nio.charset.StandardCharsets;
2523

2624
import static com.google.common.base.Preconditions.checkNotNull;
25+
import static io.split.Spec.SPEC_VERSION;
2726

2827
/**
2928
* Created by adilaijaz on 5/30/15.
@@ -34,20 +33,17 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
3433
private static final String SINCE = "since";
3534
private static final String TILL = "till";
3635
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;
36+
private static final String SPEC = "s";
37+
private final SplitHttpClient _client;
4238
private final URI _target;
4339
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
4440

45-
public static HttpSplitChangeFetcher create(CloseableHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
41+
public static HttpSplitChangeFetcher create(SplitHttpClient client, URI root, TelemetryRuntimeProducer telemetryRuntimeProducer)
4642
throws URISyntaxException {
4743
return new HttpSplitChangeFetcher(client, Utils.appendPath(root, "api/splitChanges"), telemetryRuntimeProducer);
4844
}
4945

50-
private HttpSplitChangeFetcher(CloseableHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
46+
private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
5147
_client = client;
5248
_target = uri;
5349
checkNotNull(_target);
@@ -64,49 +60,34 @@ public SplitChange fetch(long since, FetchOptions options) {
6460

6561
long start = System.currentTimeMillis();
6662

67-
CloseableHttpResponse response = null;
68-
6963
try {
70-
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SINCE, "" + since);
71-
if (options.hasCustomCN()) {
72-
uriBuilder.addParameter(TILL, "" + options.targetCN());
73-
}
64+
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION);
65+
uriBuilder.addParameter(SINCE, "" + since);
7466
if (!options.flagSetsFilter().isEmpty()) {
7567
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
7668
}
77-
URI uri = uriBuilder.build();
78-
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));
69+
if (options.hasCustomCN()) {
70+
uriBuilder.addParameter(TILL, "" + options.targetCN());
9071
}
72+
URI uri = uriBuilder.build();
73+
SplitHttpResponse response = _client.get(uri, options, null);
9174

92-
if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
93-
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, statusCode);
94-
if (statusCode == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
75+
if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
76+
if (response.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", response.statusCode(), response.statusMessage()));
9779
}
98-
_log.warn(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
99-
throw new IllegalStateException(String.format("Could not retrieve splitChanges since %s; http return code %s", since, statusCode));
80+
_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, response.statusCode());
81+
throw new IllegalStateException(
82+
String.format("Could not retrieve splitChanges since %s; http return code %s", since, response.statusCode())
83+
);
10084
}
10185

102-
String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
103-
104-
return Json.fromJson(json, SplitChange.class);
86+
return Json.fromJson(response.body(), SplitChange.class);
10587
} catch (Exception e) {
10688
throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e);
10789
} finally {
10890
_telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis()-start);
109-
Utils.forceClose(response);
11091
}
11192
}
11293

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.split.client;
2+
3+
import io.split.client.CustomHeaderDecorator;
4+
import io.split.client.dtos.RequestContext;
5+
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
class NoOpHeaderDecorator implements CustomHeaderDecorator {
11+
public NoOpHeaderDecorator() {
12+
}
13+
14+
@Override
15+
public Map<String, List<String>> getHeaderOverrides(RequestContext context) {
16+
return new HashMap<>();
17+
}
18+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.split.client;
2+
3+
import io.split.client.dtos.RequestContext;
4+
5+
import org.apache.hc.core5.http.HttpRequest;
6+
import org.apache.hc.core5.http.Header;
7+
8+
import java.util.HashSet;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.Arrays;
12+
import java.util.ArrayList;
13+
import java.util.Set;
14+
import java.util.List;
15+
16+
public final class RequestDecorator {
17+
CustomHeaderDecorator _headerDecorator;
18+
19+
private static final Set<String> forbiddenHeaders = new HashSet<>(Arrays.asList(
20+
"splitsdkversion",
21+
"splitmachineip",
22+
"splitmachinename",
23+
"splitimpressionsmode",
24+
"host",
25+
"referrer",
26+
"content-type",
27+
"content-length",
28+
"content-encoding",
29+
"accept",
30+
"keep-alive",
31+
"x-fastly-debug"));
32+
33+
public RequestDecorator(CustomHeaderDecorator headerDecorator) {
34+
_headerDecorator = (headerDecorator == null)
35+
? new NoOpHeaderDecorator()
36+
: headerDecorator;
37+
}
38+
39+
public HttpRequest decorateHeaders(HttpRequest request) {
40+
try {
41+
Map<String, List<String>> headers = _headerDecorator
42+
.getHeaderOverrides(new RequestContext(convertToMap(request.getHeaders())));
43+
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
44+
if (isHeaderAllowed(entry.getKey())) {
45+
List<String> values = entry.getValue();
46+
for (int i = 0; i < values.size(); i++) {
47+
if (i == 0) {
48+
request.setHeader(entry.getKey(), values.get(i));
49+
} else {
50+
request.addHeader(entry.getKey(), values.get(i));
51+
}
52+
}
53+
}
54+
}
55+
} catch (Exception e) {
56+
throw new IllegalArgumentException(
57+
String.format("Problem adding custom headers to request decorator: %s", e), e);
58+
}
59+
60+
return request;
61+
}
62+
63+
private boolean isHeaderAllowed(String headerName) {
64+
return !forbiddenHeaders.contains(headerName.toLowerCase());
65+
}
66+
67+
private Map<String, List<String>> convertToMap(Header[] to_convert) {
68+
Map<String, List<String>> to_return = new HashMap<String, List<String>>();
69+
for (Integer i = 0; i < to_convert.length; i++) {
70+
if (!to_return.containsKey(to_convert[i].getName())) {
71+
to_return.put(to_convert[i].getName(), new ArrayList<String>());
72+
}
73+
to_return.get(to_convert[i].getName()).add(to_convert[i].getValue());
74+
}
75+
return to_return;
76+
}
77+
}

0 commit comments

Comments
 (0)