diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt index dcd98bf6fe..1f288b3c89 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt @@ -7,6 +7,7 @@ import assertk.assertions.any import assertk.assertions.contains import assertk.assertions.isEqualTo import assertk.assertions.isNotNull +import assertk.fail import io.mockk.every import io.mockk.justRun import io.mockk.mockk @@ -70,6 +71,7 @@ import org.whispersystems.signalservice.api.groupsv2.ReceivedGroupSendEndorsemen import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.push.ServiceIds +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException import org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException import java.io.IOException import java.util.Optional @@ -1045,4 +1047,41 @@ class GroupsV2StateProcessorTest { assertThat(result.updateStatus, "inactive local is still updated given same revision from server").isEqualTo(GroupUpdateResult.UpdateStatus.GROUP_UPDATED) } + + /** + * If we get a 500 back from the service we handle it gracefully. + */ + @Test + fun ignore403sWithoutSignalTimestampHeader() { + given { + localState( + active = false, + revision = 5, + members = selfAndOthers + ) + changeSet { + changeLog(6) { + change { + setNewTitle("new title") + } + } + } + apiCallParameters(requestedRevision = 5, includeFirst = false) + expectTableUpdate = false + } + + every { groupsV2API.getGroupJoinedAt(any()) } returns NetworkResult.StatusCodeError(NonSuccessfulResponseCodeException(500)) + + try { + processor.updateLocalGroupToRevision( + targetRevision = GroupsV2StateProcessor.LATEST, + timestamp = 0 + ) + } catch (e: NonSuccessfulResponseCodeException) { + assertThat(e.code).isEqualTo(500) + return + } + + fail("No exception thrown") + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 146d3eaf61..0602ba09a3 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -2910,10 +2910,18 @@ public class PushServiceSocket { } private static final ResponseCodeHandler GROUPS_V2_PUT_RESPONSE_HANDLER = (responseCode, body, getHeader) -> { + if (getHeader.apply("X-Signal-Timestamp") == null) { + throw new NonSuccessfulResponseCodeException(500, "Missing timestamp header"); + } + if (responseCode == 409) throw new GroupExistsException(); }; private static final ResponseCodeHandler GROUPS_V2_GET_CURRENT_HANDLER = (responseCode, body, getHeader) -> { + if (getHeader.apply("X-Signal-Timestamp") == null) { + throw new NonSuccessfulResponseCodeException(500, "Missing timestamp header"); + } + switch (responseCode) { case 403: throw new NotInGroupException(); case 404: throw new GroupNotFoundException(); @@ -2921,15 +2929,20 @@ public class PushServiceSocket { }; private static final ResponseCodeHandler GROUPS_V2_PATCH_RESPONSE_HANDLER = (responseCode, body, getHeader) -> { + if (getHeader.apply("X-Signal-Timestamp") == null) { + throw new NonSuccessfulResponseCodeException(500, "Missing timestamp header"); + } + if (responseCode == 400) throw new GroupPatchNotAcceptedException(); }; - private static final ResponseCodeHandler GROUPS_V2_GET_JOIN_INFO_HANDLER = new ResponseCodeHandler() { - @Override - public void handle(int responseCode, ResponseBody body, Function getHeader) throws NonSuccessfulResponseCodeException { - if (responseCode == 403) { - throw new ForbiddenException(Optional.ofNullable(getHeader.apply("X-Signal-Forbidden-Reason"))); - } + private static final ResponseCodeHandler GROUPS_V2_GET_JOIN_INFO_HANDLER = (responseCode, body, getHeader) -> { + if (getHeader.apply("X-Signal-Timestamp") == null) { + throw new NonSuccessfulResponseCodeException(500, "Missing timestamp header"); + } + + if (responseCode == 403) { + throw new ForbiddenException(Optional.ofNullable(getHeader.apply("X-Signal-Forbidden-Reason"))); } };