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
56 changes: 50 additions & 6 deletions client/src/main/java/io/split/client/HttpSplitChangeFetcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

import com.google.common.annotations.VisibleForTesting;

import io.split.Spec;
import io.split.client.dtos.SplitChange;
import io.split.client.dtos.SplitHttpResponse;
import io.split.client.dtos.RuleBasedSegment;
import io.split.client.dtos.SplitChangesOldPayloadDto;
import io.split.client.dtos.ChangeDto;
import io.split.client.dtos.Split;
import io.split.client.exceptions.UriTooLongException;
import io.split.client.utils.Json;
import io.split.client.utils.Utils;
Expand All @@ -20,36 +25,44 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.split.Spec.SPEC_1_3;
import static io.split.Spec.SPEC_1_1;

/**
* Created by adilaijaz on 5/30/15.
*/
public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
private static final Logger _log = LoggerFactory.getLogger(HttpSplitChangeFetcher.class);

private final Object _lock = new Object();
private static final String SINCE = "since";
private static final String RB_SINCE = "rbSince";
private static final String TILL = "till";
private static final String SETS = "sets";
private static final String SPEC = "s";
private String specVersion = SPEC_1_3;
private int PROXY_CHECK_INTERVAL_MILLISECONDS_SS = 24 * 60 * 60 * 1000;
private Long _lastProxyCheckTimestamp = 0L;
private final SplitHttpClient _client;
private final URI _target;
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
private final boolean _rootURIOverriden;

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

private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer) {
private HttpSplitChangeFetcher(SplitHttpClient client, URI uri, TelemetryRuntimeProducer telemetryRuntimeProducer, boolean rootURIOverriden) {
_client = client;
_target = uri;
checkNotNull(_target);
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
_rootURIOverriden = rootURIOverriden;
}

long makeRandomTill() {
Expand All @@ -61,32 +74,63 @@ long makeRandomTill() {
public SplitChange fetch(long since, long sinceRBS, FetchOptions options) {
long start = System.currentTimeMillis();
try {
if (specVersion.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) {
_log.info("Switching to new Feature flag spec ({}) and fetching.", SPEC_1_3);
specVersion = SPEC_1_3;
}
URI uri = buildURL(options, since, sinceRBS);
SplitHttpResponse response = _client.get(uri, options, null);

if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
if (response.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", response.statusCode(), response.statusMessage()));
}

if (response.statusCode() == HttpStatus.SC_BAD_REQUEST && specVersion.equals(Spec.SPEC_1_3) && _rootURIOverriden) {
specVersion = Spec.SPEC_1_1;
_log.warn("Detected proxy without support for Feature flags spec {} version, will switch to spec version {}",
SPEC_1_3, SPEC_1_1);
_lastProxyCheckTimestamp = System.currentTimeMillis();
return fetch(since, 0, options);
}

_telemetryRuntimeProducer.recordSyncError(ResourceEnum.SPLIT_SYNC, response.statusCode());
throw new IllegalStateException(
String.format("Could not retrieve splitChanges since %s; http return code %s", since, response.statusCode())
);
}
return Json.fromJson(response.body(), SplitChange.class);

String body = response.body();
if (specVersion.equals(Spec.SPEC_1_1)) {
return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange();
}

return Json.fromJson(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);
}
}

public Long getLastProxyCheckTimestamp() {
return _lastProxyCheckTimestamp;
}

public void setLastProxyCheckTimestamp(long lastProxyCheckTimestamp) {
synchronized (_lock) {
_lastProxyCheckTimestamp = lastProxyCheckTimestamp;
}
}


private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion);
uriBuilder.addParameter(SINCE, "" + since);
uriBuilder.addParameter(RB_SINCE, "" + sinceRBS);
if (specVersion.equals(SPEC_1_3)) {
uriBuilder.addParameter(RB_SINCE, "" + sinceRBS);
}
if (!options.flagSetsFilter().isEmpty()) {
uriBuilder.addParameter(SETS, "" + options.flagSetsFilter());
}
Expand Down
4 changes: 4 additions & 0 deletions client/src/main/java/io/split/client/SplitClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,10 @@ public CustomHeaderDecorator customHeaderDecorator() {
return _customHeaderDecorator;
}

public boolean isSdkEndpointOverridden() {
return !_endpoint.equals(SDK_ENDPOINT);
}

public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; }
public static final class Builder {

Expand Down
6 changes: 3 additions & 3 deletions client/src/main/java/io/split/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser();
// SplitFetcher
_splitFetcher = buildSplitFetcher(splitCache, splitParser, flagSetsFilter,
ruleBasedSegmentParser, ruleBasedSegmentCache);
ruleBasedSegmentParser, ruleBasedSegmentCache, config.isSdkEndpointOverridden());

// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
Expand Down Expand Up @@ -622,9 +622,9 @@ private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config,

private SplitFetcher buildSplitFetcher(SplitCacheProducer splitCacheProducer, SplitParser splitParser,
FlagSetsFilter flagSetsFilter, RuleBasedSegmentParser ruleBasedSegmentParser,
RuleBasedSegmentCacheProducer ruleBasedSegmentCache) throws URISyntaxException {
RuleBasedSegmentCacheProducer ruleBasedSegmentCache, boolean isRootURIOverriden) throws URISyntaxException {
SplitChangeFetcher splitChangeFetcher = HttpSplitChangeFetcher.create(_splitHttpClient, _rootTarget,
_telemetryStorageProducer);
_telemetryStorageProducer, isRootURIOverriden);
return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, _telemetryStorageProducer,
flagSetsFilter, ruleBasedSegmentParser, ruleBasedSegmentCache);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.split.client.dtos;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class SplitChangesOldPayloadDto {
@SerializedName("since")
public long s;

@SerializedName("till")
public long t;

@SerializedName("splits")
public List<Split> d;

public SplitChange toSplitChange() {
SplitChange splitChange = new SplitChange();
ChangeDto<Split> ff = new ChangeDto<>();
ff.s = this.s;
ff.t = this.t;
ff.d = this.d;
ChangeDto<RuleBasedSegment> rbs = new ChangeDto<>();
rbs.d = new ArrayList<>();
rbs.t = -1;
rbs.s = -1;

splitChange.featureFlags = ff;
splitChange.ruleBasedSegments = rbs;

return splitChange;
}
}
Loading