pull/349/head
nielsandriesse 3 years ago
parent b3ccb2ed23
commit 65f397ccd1

@ -76,15 +76,14 @@ public final class JobQueue : NSObject, JobDelegate {
private func getRetryInterval(for job: Job) -> TimeInterval {
// Arbitrary backoff factor...
// try 1 delay: 0.00s
// try 2 delay: 0.19s
// try 1 delay: 0.5s
// try 2 delay: 1s
// ...
// try 5 delay: 1.30s
// try 5 delay: 16s
// ...
// try 11 delay: 61.31s
let backoffFactor = 1.9
let maxBackoff: Double = 60 * 60 * 1000
return 0.1 * min(maxBackoff, pow(backoffFactor, Double(job.failureCount)))
// try 11 delay: 512s
let maxBackoff: Double = 10 * 60 // 10 minutes
return 0.25 * min(maxBackoff, pow(2, Double(job.failureCount)))
}
@objc private func retry(_ timer: Timer) {

@ -57,15 +57,17 @@ public final class MessageReceiveJob : NSObject, Job, NSCoding { // NSObject/NSC
let (promise, seal) = Promise<Void>.pending()
SNMessagingKitConfiguration.shared.storage.write(with: { transaction in // Intentionally capture self
do {
let (message, proto) = try MessageReceiver.parse(self.data, openGroupMessageServerID: self.openGroupMessageServerID, using: transaction)
let isRetry = (self.failureCount != 0)
let (message, proto) = try MessageReceiver.parse(self.data, openGroupMessageServerID: self.openGroupMessageServerID, isRetry: isRetry, using: transaction)
try MessageReceiver.handle(message, associatedWithProto: proto, openGroupID: self.openGroupID, isBackgroundPoll: self.isBackgroundPoll, using: transaction)
self.handleSuccess()
seal.fulfill(())
} catch {
SNLog("Couldn't receive message due to error: \(error).")
if let error = error as? MessageReceiver.Error, !error.isRetryable {
SNLog("Message receive job permanently failed due to error: \(error).")
self.handlePermanentFailure(error: error)
} else {
SNLog("Couldn't receive message due to error: \(error).")
self.handleFailure(error: error)
}
seal.fulfill(()) // The promise is just used to keep track of when we're done

@ -1,6 +1,7 @@
import SessionUtilitiesKit
public enum MessageReceiver {
private static var lastEncryptionKeyPairRequest: [String:Date] = [:]
public enum Error : LocalizedError {
case duplicateMessage
@ -18,7 +19,6 @@ public enum MessageReceiver {
// Shared sender keys
case invalidGroupPublicKey
case noGroupKeyPair
case sharedSecretGenerationFailed
public var isRetryable: Bool {
switch self {
@ -44,18 +44,20 @@ public enum MessageReceiver {
// Shared sender keys
case .invalidGroupPublicKey: return "Invalid group public key."
case .noGroupKeyPair: return "Missing group key pair."
case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret."
}
}
}
public static func parse(_ data: Data, openGroupMessageServerID: UInt64?, using transaction: Any) throws -> (Message, SNProtoContent) {
public static func parse(_ data: Data, openGroupMessageServerID: UInt64?, isRetry: Bool = false, using transaction: Any) throws -> (Message, SNProtoContent) {
let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey()
let isOpenGroupMessage = (openGroupMessageServerID != nil)
// Parse the envelope
let envelope = try SNProtoEnvelope.parseData(data)
let storage = SNMessagingKitConfiguration.shared.storage
guard !Set(storage.getReceivedMessageTimestamps(using: transaction)).contains(envelope.timestamp) else { throw Error.duplicateMessage }
// If the message failed to process the first time around we retry it later (if the error is retryable). In this case the timestamp
// will already be in the database but we don't want to treat the message as a duplicate. The isRetry flag is a simple workaround
// for this issue.
guard !Set(storage.getReceivedMessageTimestamps(using: transaction)).contains(envelope.timestamp) || isRetry else { throw Error.duplicateMessage }
storage.addReceivedMessageTimestamp(envelope.timestamp, using: transaction)
// Decrypt the contents
guard let ciphertext = envelope.content else { throw Error.noData }
@ -93,9 +95,13 @@ public enum MessageReceiver {
try decrypt()
} catch {
do {
try MessageSender.requestEncryptionKeyPair(for: groupPublicKey!, using: transaction as! YapDatabaseReadWriteTransaction)
} catch {
// Do nothing
let now = Date()
// Don't spam encryption key pair requests
let shouldRequestEncryptionKeyPair = given(lastEncryptionKeyPairRequest[groupPublicKey!]) { now.timeIntervalSince($0) > 30 } ?? true
if shouldRequestEncryptionKeyPair {
try MessageSender.requestEncryptionKeyPair(for: groupPublicKey!, using: transaction as! YapDatabaseReadWriteTransaction)
lastEncryptionKeyPairRequest[groupPublicKey!] = now
}
}
throw error // Throw the * decryption * error and not the error generated by requestEncryptionKeyPair (if it generated one)
}

Loading…
Cancel
Save