11package io .split .client ;
22
33import com .google .common .annotations .VisibleForTesting ;
4+ import com .google .gson .JsonObject ;
45
6+ import io .split .Spec ;
57import io .split .client .dtos .SplitChange ;
68import io .split .client .dtos .SplitHttpResponse ;
79import io .split .client .exceptions .UriTooLongException ;
2325
2426import static com .google .common .base .Preconditions .checkNotNull ;
2527import static io .split .Spec .SPEC_VERSION ;
28+ import static io .split .Spec .SPEC_1_3 ;
29+ import static io .split .Spec .SPEC_1_2 ;
2630
2731/**
2832 * Created by adilaijaz on 5/30/15.
2933 */
3034public final class HttpSplitChangeFetcher implements SplitChangeFetcher {
3135 private static final Logger _log = LoggerFactory .getLogger (HttpSplitChangeFetcher .class );
3236
37+ private final Object _lock = new Object ();
3338 private static final String SINCE = "since" ;
3439 private static final String RB_SINCE = "rbSince" ;
3540 private static final String TILL = "till" ;
3641 private static final String SETS = "sets" ;
3742 private static final String SPEC = "s" ;
43+ private int PROXY_CHECK_INTERVAL_MINUTES_SS = 24 * 60 ;
44+ private Long _lastProxyCheckTimestamp = 0L ;
3845 private final SplitHttpClient _client ;
3946 private final URI _target ;
4047 private final TelemetryRuntimeProducer _telemetryRuntimeProducer ;
48+ private final boolean _rootURIOverriden ;
4149
42- public static HttpSplitChangeFetcher create (SplitHttpClient client , URI root , TelemetryRuntimeProducer telemetryRuntimeProducer )
50+ public static HttpSplitChangeFetcher create (SplitHttpClient client , URI root , TelemetryRuntimeProducer telemetryRuntimeProducer ,
51+ boolean rootURIOverriden )
4352 throws URISyntaxException {
44- return new HttpSplitChangeFetcher (client , Utils .appendPath (root , "api/splitChanges" ), telemetryRuntimeProducer );
53+ return new HttpSplitChangeFetcher (client , Utils .appendPath (root , "api/splitChanges" ), telemetryRuntimeProducer , rootURIOverriden );
4554 }
4655
47- private HttpSplitChangeFetcher (SplitHttpClient client , URI uri , TelemetryRuntimeProducer telemetryRuntimeProducer ) {
56+ private HttpSplitChangeFetcher (SplitHttpClient client , URI uri , TelemetryRuntimeProducer telemetryRuntimeProducer , boolean rootURIOverriden ) {
4857 _client = client ;
4958 _target = uri ;
5059 checkNotNull (_target );
5160 _telemetryRuntimeProducer = checkNotNull (telemetryRuntimeProducer );
61+ _rootURIOverriden = rootURIOverriden ;
5262 }
5363
5464 long makeRandomTill () {
@@ -59,27 +69,68 @@ long makeRandomTill() {
5969 @ Override
6070 public SplitChange fetch (long since , long sinceRBS , FetchOptions options ) {
6171 long start = System .currentTimeMillis ();
62- try {
63- URI uri = buildURL (options , since , sinceRBS );
64- SplitHttpResponse response = _client .get (uri , options , null );
65-
66- if (response .statusCode () < HttpStatus .SC_OK || response .statusCode () >= HttpStatus .SC_MULTIPLE_CHOICES ) {
67- if (response .statusCode () == HttpStatus .SC_REQUEST_URI_TOO_LONG ) {
68- _log .error ("The amount of flag sets provided are big causing uri length error." );
69- throw new UriTooLongException (String .format ("Status code: %s. Message: %s" , response .statusCode (), response .statusMessage ()));
72+ SplitHttpResponse response ;
73+ while (true ) {
74+ try {
75+ if (SPEC_VERSION .equals (SPEC_1_2 ) && (System .currentTimeMillis () - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MINUTES_SS )) {
76+ _log .info ("Switching to new Feature flag spec ({}) and fetching." , SPEC_1_3 );
77+ SPEC_VERSION = SPEC_1_3 ;
7078 }
79+ URI uri = buildURL (options , since , sinceRBS );
80+ response = _client .get (uri , options , null );
81+ if (response .statusCode () < HttpStatus .SC_OK || response .statusCode () >= HttpStatus .SC_MULTIPLE_CHOICES ) {
82+ if (response .statusCode () == HttpStatus .SC_REQUEST_URI_TOO_LONG ) {
83+ _log .error ("The amount of flag sets provided are big causing uri length error." );
84+ throw new UriTooLongException (String .format ("Status code: %s. Message: %s" , response .statusCode (), response .statusMessage ()));
85+ }
7186
72- _telemetryRuntimeProducer .recordSyncError (ResourceEnum .SPLIT_SYNC , response .statusCode ());
73- throw new IllegalStateException (
74- String .format ("Could not retrieve splitChanges since %s; http return code %s" , since , response .statusCode ())
75- );
87+ if (response .statusCode () == HttpStatus .SC_BAD_REQUEST && SPEC_VERSION .equals (Spec .SPEC_1_3 ) && _rootURIOverriden ) {
88+ SPEC_VERSION = Spec .SPEC_1_2 ;
89+ _log .warn ("Detected proxy without support for Feature flags spec {} version, will switch to spec version {}" ,
90+ SPEC_1_3 , SPEC_1_2 );
91+ _lastProxyCheckTimestamp = System .currentTimeMillis ();
92+ continue ;
93+ }
94+
95+ _telemetryRuntimeProducer .recordSyncError (ResourceEnum .SPLIT_SYNC , response .statusCode ());
96+ throw new IllegalStateException (
97+ String .format ("Could not retrieve splitChanges since %s; http return code %s" , since , response .statusCode ())
98+ );
99+ }
100+ break ;
101+ } catch (Exception e ) {
102+ throw new IllegalStateException (String .format ("Problem fetching splitChanges since %s: %s" , since , e ), e );
103+ } finally {
104+ _telemetryRuntimeProducer .recordSyncLatency (HTTPLatenciesEnum .SPLITS , System .currentTimeMillis () - start );
76105 }
77- return Json .fromJson (response .body (), SplitChange .class );
78- } catch (Exception e ) {
79- throw new IllegalStateException (String .format ("Problem fetching splitChanges since %s: %s" , since , e ), e );
80- } finally {
81- _telemetryRuntimeProducer .recordSyncLatency (HTTPLatenciesEnum .SPLITS , System .currentTimeMillis () - start );
82106 }
107+
108+ String body = response .body ();
109+ if (SPEC_VERSION .equals (Spec .SPEC_1_2 )) {
110+ body = convertBodyToOldSpec (body );
111+ _lastProxyCheckTimestamp = System .currentTimeMillis ();
112+ }
113+ return Json .fromJson (body , SplitChange .class );
114+ }
115+
116+ public Long getLastProxyCheckTimestamp () {
117+ return _lastProxyCheckTimestamp ;
118+ }
119+
120+ public void setLastProxyCheckTimestamp (long lastProxyCheckTimestamp ) {
121+ synchronized (_lock ) {
122+ _lastProxyCheckTimestamp = lastProxyCheckTimestamp ;
123+ }
124+ }
125+
126+ private String convertBodyToOldSpec (String body ) {
127+ JsonObject targetBody = Json .fromJson ("{\" ff\" : {\" t\" :-1, \" s\" : -1}," +
128+ "\" rbs\" : {\" d\" :[], \" t\" :-1, \" s\" : -1}}" , JsonObject .class );
129+ JsonObject jsonBody = Json .fromJson (body , JsonObject .class );
130+ targetBody .getAsJsonObject ("ff" ).add ("d" , jsonBody .getAsJsonArray ("splits" ));
131+ targetBody .getAsJsonObject ("ff" ).add ("s" , jsonBody .get ("since" ));
132+ targetBody .getAsJsonObject ("ff" ).add ("t" , jsonBody .get ("till" ));
133+ return Json .toJson (targetBody );
83134 }
84135
85136 private URI buildURL (FetchOptions options , long since , long sinceRBS ) throws URISyntaxException {
0 commit comments