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
39 changes: 9 additions & 30 deletions client/src/main/java/io/split/client/HttpSegmentChangeFetcher.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.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
39 changes: 11 additions & 28 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 @@ -38,16 +35,16 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
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 +61,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 +72,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
2 changes: 1 addition & 1 deletion client/src/main/java/io/split/client/RequestDecorator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public Map<String, String> getHeaderOverrides() {
}
}

class RequestDecorator {
public final class RequestDecorator {
UserCustomHeaderDecorator _headerDecorator;

private static final Set<String> forbiddenHeaders = new HashSet<>(Arrays.asList(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.split.client;
package io.split.service;

import io.split.engine.common.FetchOptions;
import io.split.telemetry.domain.enums.HttpParamsWrapper;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.split.client;
package io.split.service;

import io.split.client.exceptions.UriTooLongException;
import io.split.client.utils.Json;
import io.split.client.RequestDecorator;
import io.split.client.utils.Utils;
import io.split.engine.common.FetchOptions;
import io.split.client.dtos.SplitHttpResponse;
Expand All @@ -14,7 +13,6 @@
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -63,10 +61,6 @@ public SplitHttpResponse get(URI uri, FetchOptions options) {
SplitHttpResponse httpResponse = new SplitHttpResponse();
httpResponse.statusMessage = "";
if (statusCode < HttpStatus.SC_OK || statusCode >= HttpStatus.SC_MULTIPLE_CHOICES) {
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()));
}
_log.warn(String.format("Response status was: %s. Reason: %s", statusCode , response.getReasonPhrase()));
httpResponse.statusMessage = response.getReasonPhrase();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import io.split.client.dtos.SegmentChange;
import io.split.engine.common.FetchOptions;
import io.split.engine.metrics.Metrics;
import io.split.service.SplitHttpClient;
import io.split.service.SplitHttpClientImpl;
import io.split.telemetry.storage.InMemoryTelemetryStorage;
import io.split.telemetry.storage.TelemetryStorage;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
Expand Down Expand Up @@ -31,35 +33,39 @@ public class HttpSegmentChangeFetcherTest {
public void testDefaultURL() throws URISyntaxException {
URI rootTarget = URI.create("https://api.split.io");
CloseableHttpClient httpClient = HttpClients.custom().build();
SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null));
Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://api.split.io/api/segmentChanges")));
}

@Test
public void testCustomURLNoPathNoBackslash() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
CloseableHttpClient httpClient = HttpClients.custom().build();
SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null));
Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/segmentChanges")));
}

@Test
public void testCustomURLAppendingPath() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split/");
CloseableHttpClient httpClient = HttpClients.custom().build();
SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null));
Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/segmentChanges")));
}

@Test
public void testCustomURLAppendingPathNoBackslash() throws URISyntaxException {
URI rootTarget = URI.create("https://kubernetesturl.com/split");
CloseableHttpClient httpClient = HttpClients.custom().build();
SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClient, new RequestDecorator(null));
Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClient, rootTarget, TELEMETRY_STORAGE);
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);
Assert.assertThat(fetcher.getTarget().toString(), Matchers.is(Matchers.equalTo("https://kubernetesturl.com/split/api/segmentChanges")));
}

Expand All @@ -68,9 +74,10 @@ public void testFetcherWithSpecialCharacters() throws URISyntaxException, IOExce
URI rootTarget = URI.create("https://api.split.io/api/segmentChanges");

CloseableHttpClient httpClientMock = TestHelper.mockHttpClient("segment-change-special-chatacters.json", HttpStatus.SC_OK);
SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null));

Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClientMock, rootTarget, TELEMETRY_STORAGE);
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget, TELEMETRY_STORAGE);

SegmentChange change = fetcher.fetch("some_segment", 1234567, new FetchOptions.Builder().build());

Expand All @@ -94,10 +101,12 @@ public void testFetcherWithCDNBypassOption() throws IOException, URISyntaxExcept

ArgumentCaptor<ClassicHttpRequest> requestCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class);
SplitHttpClient splitHtpClient = SplitHttpClientImpl.create(httpClientMock, new RequestDecorator(null));

when(httpClientMock.execute(requestCaptor.capture())).thenReturn(TestHelper.classicResponseToCloseableMock(response));

Metrics.NoopMetrics metrics = new Metrics.NoopMetrics();
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(httpClientMock, rootTarget, Mockito.mock(TelemetryStorage.class));
HttpSegmentChangeFetcher fetcher = HttpSegmentChangeFetcher.create(splitHtpClient, rootTarget, Mockito.mock(TelemetryStorage.class));

fetcher.fetch("someSegment", -1, new FetchOptions.Builder().targetChangeNumber(123).build());
fetcher.fetch("someSegment2",-1, new FetchOptions.Builder().build());
Expand Down
Loading