From 3f233ed39f47ecb35cda605b24a9c9e3c40da041 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 26 Aug 2021 10:24:20 -0300 Subject: [PATCH] Use AttachmentsV2 if the resumable upload link from V3 becomes corrupted. --- .../securesms/jobs/AttachmentUploadJob.java | 24 +++++++++++++++---- ...lResumableUploadResponseCodeException.java | 7 ++++++ .../internal/push/PushServiceSocket.java | 3 ++- 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResumableUploadResponseCodeException.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java index 1f7c798a9..ae18d89e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java @@ -32,6 +32,8 @@ import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResumableUploadResponseCodeException; import org.whispersystems.signalservice.api.push.exceptions.ResumeLocationInvalidException; import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; @@ -56,6 +58,7 @@ public final class AttachmentUploadJob extends BaseJob { private static final String KEY_ROW_ID = "row_id"; private static final String KEY_UNIQUE_ID = "unique_id"; + private static final String KEY_FORCE_V2 = "force_v2"; /** * Foreground notification shows while uploading attachments above this. @@ -64,24 +67,29 @@ public final class AttachmentUploadJob extends BaseJob { private final AttachmentId attachmentId; + private boolean forceV2; + public AttachmentUploadJob(AttachmentId attachmentId) { this(new Job.Parameters.Builder() .addConstraint(NetworkConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) .setMaxAttempts(Parameters.UNLIMITED) .build(), - attachmentId); + attachmentId, + false); } - private AttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull AttachmentId attachmentId) { + private AttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull AttachmentId attachmentId, boolean forceV2) { super(parameters); this.attachmentId = attachmentId; + this.forceV2 = forceV2; } @Override public @NonNull Data serialize() { return new Data.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) + .putBoolean(KEY_FORCE_V2, forceV2) .build(); } @@ -105,7 +113,10 @@ public final class AttachmentUploadJob extends BaseJob { ResumableUploadSpec resumableUploadSpec; - if (inputData != null && inputData.hasString(ResumableUploadSpecJob.KEY_RESUME_SPEC)) { + if (forceV2) { + Log.d(TAG, "Forcing utilization of V2"); + resumableUploadSpec = null; + } else if (inputData != null && inputData.hasString(ResumableUploadSpecJob.KEY_RESUME_SPEC)) { Log.d(TAG, "Using attachments V3"); resumableUploadSpec = ResumableUploadSpec.deserialize(inputData.getString(ResumableUploadSpecJob.KEY_RESUME_SPEC)); } else { @@ -137,6 +148,11 @@ public final class AttachmentUploadJob extends BaseJob { Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get(); database.updateAttachmentAfterUpload(databaseAttachment.getAttachmentId(), attachment, remoteAttachment.getUploadTimestamp()); + } catch (NonSuccessfulResumableUploadResponseCodeException e) { + if (e.getCode() == 400) { + Log.w(TAG, "Failed to upload due to a 400 when getting resumable upload information. Downgrading to attachments v2", e); + forceV2 = true; + } } } @@ -245,7 +261,7 @@ public final class AttachmentUploadJob extends BaseJob { public static final class Factory implements Job.Factory { @Override public @NonNull AttachmentUploadJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID))); + return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), data.getBooleanOrDefault(KEY_FORCE_V2, false)); } } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResumableUploadResponseCodeException.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResumableUploadResponseCodeException.java new file mode 100644 index 000000000..121c30cf3 --- /dev/null +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/push/exceptions/NonSuccessfulResumableUploadResponseCodeException.java @@ -0,0 +1,7 @@ +package org.whispersystems.signalservice.api.push.exceptions; + +public class NonSuccessfulResumableUploadResponseCodeException extends NonSuccessfulResponseCodeException { + public NonSuccessfulResumableUploadResponseCodeException(int code, String s) { + super(code, s); + } +} 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 2bf799666..b19f44067 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 @@ -59,6 +59,7 @@ import org.whispersystems.signalservice.api.push.exceptions.MalformedResponseExc import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException; import org.whispersystems.signalservice.api.push.exceptions.NoContentException; import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException; +import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResumableUploadResponseCodeException; import org.whispersystems.signalservice.api.push.exceptions.NotFoundException; import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; @@ -1374,7 +1375,7 @@ public class PushServiceSocket { } else if (response.code() == 404) { throw new ResumeLocationInvalidException(); } else { - throw new NonSuccessfulResponseCodeException(response.code(), "Response: " + response); + throw new NonSuccessfulResumableUploadResponseCodeException(response.code(), "Response: " + response); } } finally { synchronized (connections) {