diff --git a/client/LICENSE b/LICENSE
similarity index 93%
rename from client/LICENSE
rename to LICENSE
index e83b0b46c..fb8a05ab1 100644
--- a/client/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2018 Split Software
+Copyright © 2020 Split Software, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/client/CHANGES.txt b/client/CHANGES.txt
index 36cc2ff53..4a07f1ce7 100644
--- a/client/CHANGES.txt
+++ b/client/CHANGES.txt
@@ -1,5 +1,8 @@
CHANGES
+3.3.3 (Apr 7, 2020)
+ - Fix issue regarding special characters come from split/segments fetchers.
+
3.3.2 (Jan 24, 2020)
- Shade com.google.guava as well
diff --git a/client/pom.xml b/client/pom.xml
index e89303180..0349c631e 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,7 +5,7 @@
io.split.client
java-client-parent
- 3.3.2
+ 3.3.3
java-client
jar
diff --git a/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java b/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java
index a04027c87..956a12efc 100644
--- a/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java
+++ b/client/src/main/java/io/split/client/HttpSegmentChangeFetcher.java
@@ -16,6 +16,7 @@
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -73,7 +74,7 @@ public SegmentChange fetch(String segmentName, long since) {
- String json = EntityUtils.toString(response.getEntity());
+ String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
if (_log.isDebugEnabled()) {
_log.debug("Received json: " + json);
}
diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
index e70085e54..2ca8819b2 100644
--- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
+++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
@@ -16,6 +16,7 @@
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -68,7 +69,7 @@ public SplitChange fetch(long since) {
}
- String json = EntityUtils.toString(response.getEntity());
+ String json = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
if (_log.isDebugEnabled()) {
_log.debug("Received json: " + json);
}
diff --git a/client/src/test/java/io/split/client/HttpSegmentChangeFetcherTest.java b/client/src/test/java/io/split/client/HttpSegmentChangeFetcherTest.java
index 23e957bad..4d8531764 100644
--- a/client/src/test/java/io/split/client/HttpSegmentChangeFetcherTest.java
+++ b/client/src/test/java/io/split/client/HttpSegmentChangeFetcherTest.java
@@ -1,12 +1,19 @@
package io.split.client;
+import io.split.client.dtos.SegmentChange;
import io.split.engine.metrics.Metrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
+import org.mockito.Mockito;
+import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -48,4 +55,31 @@ public void testCustomURLAppendingPathNoBackslash() throws URISyntaxException {
Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/segmentChanges")));
}
+ @Test
+ public void testFetcherWithSpecialCharacters() throws URISyntaxException, IOException {
+ URI rootTarget = URI.create("https://api.split.io/api/segmentChanges");
+
+ CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
+ CloseableHttpResponse httpResponseMock = Mockito.mock(CloseableHttpResponse.class, Mockito.RETURNS_DEEP_STUBS);
+ StatusLine statusLineMock = Mockito.mock(StatusLine.class);
+ HttpEntity entityMock = Mockito.mock(HttpEntity.class);
+
+ Mockito.when(statusLineMock.getStatusCode()).thenReturn(200);
+ Mockito.when(httpResponseMock.getStatusLine()).thenReturn(statusLineMock);
+ Mockito.when(entityMock.getContent()).thenReturn(getClass().getClassLoader().getResourceAsStream("segment-change-special-chatacters.json"));
+ Mockito.when(httpResponseMock.getEntity()).thenReturn(entityMock);
+
+ Mockito.when(httpClientMock.execute((HttpUriRequest) Mockito.anyObject())).thenReturn(httpResponseMock);
+
+ Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
+ HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClientMock, rootTarget, metrics);
+
+ SegmentChange change = fetcher.fetch("some_segment", 1234567);
+
+ Assert.assertNotNull(change);
+ Assert.assertEquals(1, change.added.size());
+ Assert.assertEquals("grüne_Straße", change.added.get(0));
+ Assert.assertEquals(1, change.removed.size());
+ Assert.assertEquals("other_user", change.removed.get(0));
+ }
}
diff --git a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java
index d797ff37d..2ee6de304 100644
--- a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java
+++ b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java
@@ -1,14 +1,23 @@
package io.split.client;
+import io.split.client.dtos.Split;
+import io.split.client.dtos.SplitChange;
import io.split.engine.metrics.Metrics;
+import org.apache.http.HttpEntity;
+import org.apache.http.StatusLine;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
+import org.mockito.Mockito;
+import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Map;
public class HttpSplitChangeFetcherTest {
@@ -48,4 +57,35 @@ public void testCustomURLAppendingPathNoBackslash() throws URISyntaxException {
Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/splitChanges")));
}
+ @Test
+ public void testFetcherWithSpecialCharacters() throws URISyntaxException, IOException {
+ URI rootTarget = URI.create("https://api.split.io");
+
+ CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
+ CloseableHttpResponse httpResponseMock = Mockito.mock(CloseableHttpResponse.class, Mockito.RETURNS_DEEP_STUBS);
+ StatusLine statusLineMock = Mockito.mock(StatusLine.class);
+ HttpEntity entityMock = Mockito.mock(HttpEntity.class);
+
+ Mockito.when(statusLineMock.getStatusCode()).thenReturn(200);
+ Mockito.when(httpResponseMock.getStatusLine()).thenReturn(statusLineMock);
+ Mockito.when(entityMock.getContent()).thenReturn(getClass().getClassLoader().getResourceAsStream("split-change-special-characters.json"));
+ Mockito.when(httpResponseMock.getEntity()).thenReturn(entityMock);
+
+ Mockito.when(httpClientMock.execute((HttpUriRequest) Mockito.anyObject())).thenReturn(httpResponseMock);
+
+ Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
+ HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(httpClientMock, rootTarget, metrics);
+
+ SplitChange change = fetcher.fetch(1234567);
+
+ Assert.assertNotNull(change);
+ Assert.assertEquals(1, change.splits.size());
+ Assert.assertNotNull(change.splits.get(0));
+
+ Split split = change.splits.get(0);
+ Map configs = split.configurations;
+ Assert.assertEquals(2, configs.size());
+ Assert.assertEquals("{\"test\": \"blue\",\"grüne Straße\": 13}", configs.get("on"));
+ Assert.assertEquals("{\"test\": \"blue\",\"size\": 15}", configs.get("off"));
+ }
}
diff --git a/client/src/test/resources/segment-change-special-chatacters.json b/client/src/test/resources/segment-change-special-chatacters.json
new file mode 100644
index 000000000..f8a2ccb06
--- /dev/null
+++ b/client/src/test/resources/segment-change-special-chatacters.json
@@ -0,0 +1,11 @@
+{
+ "name": "employees",
+ "added": [
+ "grüne_Straße"
+ ],
+ "removed": [
+ "other_user"
+ ],
+ "since": -1,
+ "till": 1489542661161
+}
diff --git a/client/src/test/resources/split-change-special-characters.json b/client/src/test/resources/split-change-special-characters.json
new file mode 100644
index 000000000..276ab19ee
--- /dev/null
+++ b/client/src/test/resources/split-change-special-characters.json
@@ -0,0 +1,55 @@
+{
+ "splits": [
+ {
+ "trafficTypeName": "user",
+ "name": "DEMO_MURMUR2",
+ "trafficAllocation": 100,
+ "trafficAllocationSeed": 1314112417,
+ "seed": -2059033614,
+ "status": "ACTIVE",
+ "killed": false,
+ "defaultTreatment": "of",
+ "changeNumber": 1491244291288,
+ "algo": 2,
+ "configurations": {
+ "on": "{\"test\": \"blue\",\"grüne Straße\": 13}",
+ "off": "{\"test\": \"blue\",\"size\": 15}"
+ },
+ "conditions": [
+ {
+ "conditionType": "ROLLOUT",
+ "matcherGroup": {
+ "combiner": "AND",
+ "matchers": [
+ {
+ "keySelector": {
+ "trafficType": "user",
+ "attribute": null
+ },
+ "matcherType": "ALL_KEYS",
+ "negate": false,
+ "userDefinedSegmentMatcherData": null,
+ "whitelistMatcherData": null,
+ "unaryNumericMatcherData": null,
+ "betweenMatcherData": null
+ }
+ ]
+ },
+ "partitions": [
+ {
+ "treatment": "on",
+ "size": 0
+ },
+ {
+ "treatment": "of",
+ "size": 100
+ }
+ ],
+ "label": "in segment all"
+ }
+ ]
+ }
+ ],
+ "since": 1491244291288,
+ "till": 1491244291288
+}
diff --git a/pom.xml b/pom.xml
index 228edcafe..1cb5c1638 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.split.client
java-client-parent
- 3.3.2
+ 3.3.3
diff --git a/testing/pom.xml b/testing/pom.xml
index d19608296..8782d2ca6 100644
--- a/testing/pom.xml
+++ b/testing/pom.xml
@@ -6,7 +6,7 @@
io.split.client
java-client-parent
- 3.3.2
+ 3.3.3
java-client-testing