16
16
import retrofit2 .Response ;
17
17
18
18
import java .io .IOException ;
19
+ import java .time .ZonedDateTime ;
20
+ import java .time .format .DateTimeFormatter ;
19
21
import java .util .concurrent .Executors ;
20
22
import java .util .concurrent .ScheduledExecutorService ;
21
23
import java .util .concurrent .TimeUnit ;
@@ -30,6 +32,7 @@ public final class EnvironmentConfigManager {
30
32
31
33
private ProjectConfig config ;
32
34
private String configETag = "" ;
35
+ private String configLastModified = "" ;
33
36
34
37
private final String sdkKey ;
35
38
private final int pollingIntervalMS ;
@@ -69,7 +72,7 @@ public boolean isConfigInitialized() {
69
72
}
70
73
71
74
private ProjectConfig getConfig () throws DevCycleException {
72
- Call <ProjectConfig > config = this .configApiClient .getConfig (this .sdkKey , this .configETag );
75
+ Call <ProjectConfig > config = this .configApiClient .getConfig (this .sdkKey , this .configETag , this . configLastModified );
73
76
this .config = getResponseWithRetries (config , 1 );
74
77
return this .config ;
75
78
}
@@ -129,23 +132,42 @@ private ProjectConfig getConfigResponse(Call<ProjectConfig> call) throws DevCycl
129
132
130
133
if (response .isSuccessful ()) {
131
134
String currentETag = response .headers ().get ("ETag" );
135
+ String headerLastModified = response .headers ().get ("Last-Modified" );
136
+
137
+ if (!this .configLastModified .isEmpty () && headerLastModified != null && !headerLastModified .isEmpty ()) {
138
+ ZonedDateTime parsedLastModified = ZonedDateTime .parse (
139
+ headerLastModified ,
140
+ DateTimeFormatter .RFC_1123_DATE_TIME
141
+ );
142
+ ZonedDateTime configLastModified = ZonedDateTime .parse (
143
+ this .configLastModified ,
144
+ DateTimeFormatter .RFC_1123_DATE_TIME
145
+ );
146
+
147
+ if (parsedLastModified .isBefore (configLastModified )) {
148
+ DevCycleLogger .warning ("Received a config with last-modified header before the current stored timestamp. Not saving config." );
149
+ return this .config ;
150
+ }
151
+ }
152
+
132
153
ProjectConfig config = response .body ();
133
154
try {
134
155
ObjectMapper mapper = new ObjectMapper ();
135
156
localBucketing .storeConfig (sdkKey , mapper .writeValueAsString (config ));
136
157
} catch (JsonProcessingException e ) {
137
158
if (this .config != null ) {
138
- DevCycleLogger .error ("Unable to parse config with etag: " + currentETag + ". Using cache, etag " + this .configETag );
159
+ DevCycleLogger .error ("Unable to parse config with etag: " + currentETag + ". Using cache, etag " + this .configETag + " last-modified: " + this . configLastModified );
139
160
return this .config ;
140
161
} else {
141
162
errorResponse .setMessage (e .getMessage ());
142
163
throw new DevCycleException (HttpResponseCode .SERVER_ERROR , errorResponse );
143
164
}
144
165
}
145
166
this .configETag = currentETag ;
167
+ this .configLastModified = headerLastModified ;
146
168
return response .body ();
147
169
} else if (httpResponseCode == HttpResponseCode .NOT_MODIFIED ) {
148
- DevCycleLogger .debug ("Config not modified, using cache, etag: " + this .configETag );
170
+ DevCycleLogger .debug ("Config not modified, using cache, etag: " + this .configETag + " last-modified: " + this . configLastModified );
149
171
return this .config ;
150
172
} else {
151
173
if (response .errorBody () != null ) {
0 commit comments