From a25af36e32ff695bb1949a8d33b9debc3deb7932 Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Thu, 21 May 2020 11:15:36 -0400 Subject: [PATCH] Include timestamps in all server-to-client websocket messages. --- .../filters/TimestampResponseFilter.java | 6 +++--- .../textsecuregcm/util/TimestampHeaderUtil.java | 13 +++++++++++++ .../websocket/ProvisioningConnection.java | 14 +++++++++----- .../websocket/WebSocketConnection.java | 6 ++++-- 4 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/util/TimestampHeaderUtil.java diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/filters/TimestampResponseFilter.java b/service/src/main/java/org/whispersystems/textsecuregcm/filters/TimestampResponseFilter.java index 130787a24..cdf451b4f 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/filters/TimestampResponseFilter.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/filters/TimestampResponseFilter.java @@ -1,5 +1,7 @@ package org.whispersystems.textsecuregcm.filters; +import org.whispersystems.textsecuregcm.util.TimestampHeaderUtil; + import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; @@ -10,10 +12,8 @@ import java.util.Collections; */ public class TimestampResponseFilter implements ContainerResponseFilter { - private static final String TIMESTAMP_HEADER = "X-Signal-Timestamp"; - @Override public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) { - responseContext.getStringHeaders().put(TIMESTAMP_HEADER, Collections.singletonList(String.valueOf(System.currentTimeMillis()))); + responseContext.getStringHeaders().put(TimestampHeaderUtil.TIMESTAMP_HEADER, Collections.singletonList(String.valueOf(System.currentTimeMillis()))); } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/util/TimestampHeaderUtil.java b/service/src/main/java/org/whispersystems/textsecuregcm/util/TimestampHeaderUtil.java new file mode 100644 index 000000000..6e8f784ae --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/util/TimestampHeaderUtil.java @@ -0,0 +1,13 @@ +package org.whispersystems.textsecuregcm.util; + +public class TimestampHeaderUtil { + + public static final String TIMESTAMP_HEADER = "X-Signal-Timestamp"; + + private TimestampHeaderUtil() { + } + + public static String getTimestampHeader() { + return TIMESTAMP_HEADER + ":" + System.currentTimeMillis(); + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java b/service/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java index fa79cbc3f..ace5f96d0 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/websocket/ProvisioningConnection.java @@ -6,8 +6,10 @@ import org.slf4j.LoggerFactory; import org.whispersystems.dispatch.DispatchChannel; import org.whispersystems.textsecuregcm.entities.MessageProtos.ProvisioningUuid; import org.whispersystems.textsecuregcm.storage.PubSubProtos.PubSubMessage; +import org.whispersystems.textsecuregcm.util.TimestampHeaderUtil; import org.whispersystems.websocket.WebSocketClient; +import java.util.Collections; import java.util.Optional; public class ProvisioningConnection implements DispatchChannel { @@ -28,7 +30,7 @@ public class ProvisioningConnection implements DispatchChannel { if (outgoingMessage.getType() == PubSubMessage.Type.DELIVER) { Optional body = Optional.of(outgoingMessage.getContent().toByteArray()); - client.sendRequest("PUT", "/v1/message", null, body) + client.sendRequest("PUT", "/v1/message", Collections.singletonList(TimestampHeaderUtil.getTimestampHeader()), body) .thenAccept(response -> client.close(1001, "All you get.")) .exceptionally(throwable -> { client.close(1001, "That's all!"); @@ -44,10 +46,12 @@ public class ProvisioningConnection implements DispatchChannel { public void onDispatchSubscribed(String channel) { try { ProvisioningAddress address = new ProvisioningAddress(channel); - this.client.sendRequest("PUT", "/v1/address", null, Optional.of(ProvisioningUuid.newBuilder() - .setUuid(address.getAddress()) - .build() - .toByteArray())); + this.client.sendRequest("PUT", "/v1/address", Collections.singletonList(TimestampHeaderUtil.getTimestampHeader()), + Optional.of(ProvisioningUuid.newBuilder() + .setUuid(address.getAddress()) + .build() + .toByteArray())); + } catch (InvalidWebsocketAddressException e) { logger.warn("Badly formatted address", e); this.client.close(1001, "Server Error"); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java b/service/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java index 0ee69e072..a7d916c71 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/websocket/WebSocketConnection.java @@ -21,6 +21,7 @@ import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.MessagesManager; import org.whispersystems.textsecuregcm.util.Constants; +import org.whispersystems.textsecuregcm.util.TimestampHeaderUtil; import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.websocket.WebSocketClient; import org.whispersystems.websocket.messages.WebSocketResponseMessage; @@ -28,6 +29,7 @@ import org.whispersystems.websocket.messages.WebSocketResponseMessage; import javax.ws.rs.WebApplicationException; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Optional; import static com.codahale.metrics.MetricRegistry.name; @@ -118,7 +120,7 @@ public class WebSocketConnection implements DispatchChannel { body = Optional.ofNullable(new EncryptedOutgoingMessage(message, device.getSignalingKey()).toByteArray()); } - client.sendRequest("PUT", "/api/v1/message", Collections.singletonList(header), body) + client.sendRequest("PUT", "/api/v1/message", List.of(header, TimestampHeaderUtil.getTimestampHeader()), body) .thenAccept(response -> { boolean isReceipt = message.getType() == Envelope.Type.RECEIPT; @@ -201,7 +203,7 @@ public class WebSocketConnection implements DispatchChannel { } if (!messages.hasMore()) { - client.sendRequest("PUT", "/api/v1/queue/empty", null, Optional.empty()); + client.sendRequest("PUT", "/api/v1/queue/empty", Collections.singletonList(TimestampHeaderUtil.getTimestampHeader()), Optional.empty()); } }