diff --git a/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java b/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java index 5afb3fdec..30fc0bdfb 100644 --- a/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java +++ b/src/main/java/com/launchdarkly/client/DefaultEventProcessor.java @@ -11,6 +11,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; @@ -178,7 +179,6 @@ public String toString() { // for debugging only static final class EventDispatcher { private static final int MAX_FLUSH_THREADS = 5; private static final int MESSAGE_BATCH_SIZE = 50; - static final SimpleDateFormat HTTP_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); private final LDConfig config; private final List flushWorkers; @@ -231,8 +231,8 @@ public void uncaughtException(Thread t, Throwable e) { flushWorkers = new ArrayList<>(); EventResponseListener listener = new EventResponseListener() { - public void handleResponse(Response response) { - EventDispatcher.this.handleResponse(response); + public void handleResponse(Response response, Date responseDate) { + EventDispatcher.this.handleResponse(response, responseDate); } }; for (int i = 0; i < MAX_FLUSH_THREADS; i++) { @@ -404,13 +404,9 @@ private void triggerFlush(EventBuffer outbox, BlockingQueue payloa } } - private void handleResponse(Response response) { - String dateStr = response.header("Date"); - if (dateStr != null) { - try { - lastKnownPastTime.set(HTTP_DATE_FORMAT.parse(dateStr).getTime()); - } catch (ParseException e) { - } + private void handleResponse(Response response, Date responseDate) { + if (responseDate != null) { + lastKnownPastTime.set(responseDate.getTime()); } if (!isHttpErrorRecoverable(response.code())) { disabled.set(true); @@ -475,7 +471,7 @@ private static final class FlushPayload { } private static interface EventResponseListener { - void handleResponse(Response response); + void handleResponse(Response response, Date responseDate); } private static final class SendEventsTask implements Runnable { @@ -487,6 +483,7 @@ private static final class SendEventsTask implements Runnable { private final AtomicBoolean stopping; private final EventOutput.Formatter formatter; private final Thread thread; + private final SimpleDateFormat httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); // need one instance per task because the date parser isn't thread-safe SendEventsTask(String sdkKey, LDConfig config, EventResponseListener responseListener, BlockingQueue payloadQueue, AtomicInteger activeFlushWorkersCount, @@ -563,7 +560,7 @@ private void postEvents(List eventsOut) { continue; } } - responseListener.handleResponse(response); + responseListener.handleResponse(response, getResponseDate(response)); break; } catch (IOException e) { logger.warn("Unhandled exception in LaunchDarkly client when posting events to URL: " + request.url(), e); @@ -571,5 +568,17 @@ private void postEvents(List eventsOut) { } } } + + private Date getResponseDate(Response response) { + String dateStr = response.header("Date"); + if (dateStr != null) { + try { + return httpDateFormat.parse(dateStr); + } catch (ParseException e) { + logger.warn("Received invalid Date header from events service"); + } + } + return null; + } } } diff --git a/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java b/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java index 7f3ed22c9..9bb3d69dc 100644 --- a/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java +++ b/src/test/java/com/launchdarkly/client/DefaultEventProcessorTest.java @@ -10,6 +10,7 @@ import org.hamcrest.Matcher; import org.junit.Test; +import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; @@ -39,6 +40,7 @@ public class DefaultEventProcessorTest { gson.fromJson("{\"key\":\"userkey\",\"name\":\"Red\"}", JsonElement.class); private static final JsonElement filteredUserJson = gson.fromJson("{\"key\":\"userkey\",\"privateAttrs\":[\"name\"]}", JsonElement.class); + private static final SimpleDateFormat httpDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); // Note that all of these events depend on the fact that DefaultEventProcessor does a synchronous // flush when it is closed; in this case, it's closed implicitly by the try-with-resources block. @@ -594,7 +596,7 @@ private MockResponse eventsSuccessResponse() { } private MockResponse addDateHeader(MockResponse response, long timestamp) { - return response.addHeader("Date", EventDispatcher.HTTP_DATE_FORMAT.format(new Date(timestamp))); + return response.addHeader("Date", httpDateFormat.format(new Date(timestamp))); } private JsonArray getEventsFromLastRequest(MockWebServer server) throws Exception {