|
|
|
@ -54,7 +54,12 @@ void AssertIsOnSendingQueue()
|
|
|
|
|
|
|
|
|
|
static void *kNSError_MessageSender_IsRetryable = &kNSError_MessageSender_IsRetryable;
|
|
|
|
|
static void *kNSError_MessageSender_ShouldBeIgnoredForGroups = &kNSError_MessageSender_ShouldBeIgnoredForGroups;
|
|
|
|
|
static void *kNSError_MessageSender_IsFatal = &kNSError_MessageSender_IsFatal;
|
|
|
|
|
|
|
|
|
|
// isRetryable and isFatal are opposites but not redundant.
|
|
|
|
|
//
|
|
|
|
|
// If a group message send fails, the send will be retried if any of the errors were retryable UNLESS
|
|
|
|
|
// any of the errors were fatal. Fatal errors trump retryable errors.
|
|
|
|
|
@implementation NSError (OWSMessageSender)
|
|
|
|
|
|
|
|
|
|
- (BOOL)isRetryable
|
|
|
|
@ -84,6 +89,19 @@ static void *kNSError_MessageSender_ShouldBeIgnoredForGroups = &kNSError_Message
|
|
|
|
|
objc_setAssociatedObject(self, kNSError_MessageSender_ShouldBeIgnoredForGroups, @(value), OBJC_ASSOCIATION_COPY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL)isFatal
|
|
|
|
|
{
|
|
|
|
|
NSNumber *value = objc_getAssociatedObject(self, kNSError_MessageSender_IsFatal);
|
|
|
|
|
// This value will NOT always be set for all errors by the time we query it's value.
|
|
|
|
|
// Default to NOT fatal.
|
|
|
|
|
return value ? [value boolValue] : NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)setIsFatal:(BOOL)value
|
|
|
|
|
{
|
|
|
|
|
objc_setAssociatedObject(self, kNSError_MessageSender_IsFatal, @(value), OBJC_ASSOCIATION_COPY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
@ -266,7 +284,7 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4;
|
|
|
|
|
RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) {
|
|
|
|
|
DDLogInfo(@"%@ Sending failed.", self.tag);
|
|
|
|
|
|
|
|
|
|
if (![error isRetryable]) {
|
|
|
|
|
if (![error isRetryable] || [error isFatal]) {
|
|
|
|
|
DDLogInfo(@"%@ Skipping retry due to terminal error: %@", self.tag, error);
|
|
|
|
|
self.failureHandler(error);
|
|
|
|
|
return;
|
|
|
|
@ -748,6 +766,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|
|
|
|
id failureResult = groupSendFuture.forceGetFailure;
|
|
|
|
|
if ([failureResult isKindOfClass:[NSError class]]) {
|
|
|
|
|
NSError *error = failureResult;
|
|
|
|
|
|
|
|
|
|
// Some errors should be ignored when sending messages
|
|
|
|
|
// to groups. See discussion on
|
|
|
|
|
// NSError (OWSMessageSender) category.
|
|
|
|
@ -755,6 +774,15 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Some errors should never be retried, in order to avoid
|
|
|
|
|
// hitting rate limits, for example. Unfortunately, since
|
|
|
|
|
// group send retry is all-or-nothing, we need to fail
|
|
|
|
|
// immediately even if some of the other recipients had
|
|
|
|
|
// retryable errors.
|
|
|
|
|
if ([error isFatal]) {
|
|
|
|
|
failureHandler(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([error isRetryable] && !firstRetryableError) {
|
|
|
|
|
firstRetryableError = error;
|
|
|
|
|
} else if (![error isRetryable] && !firstNonRetryableError) {
|
|
|
|
@ -859,6 +887,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|
|
|
|
// Key will continue to be unaccepted, so no need to retry. It'll only cause us to hit the Pre-Key request
|
|
|
|
|
// rate limit
|
|
|
|
|
[error setIsRetryable:NO];
|
|
|
|
|
// Avoid the "Too many failures with this contact" error rate limiting.
|
|
|
|
|
[error setIsFatal:YES];
|
|
|
|
|
return failureHandler(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -869,6 +899,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
|
|
|
|
|
|
|
|
|
// We're already rate-limited. No need to exacerbate the problem.
|
|
|
|
|
[error setIsRetryable:NO];
|
|
|
|
|
// Avoid exacerbating the rate limiting.
|
|
|
|
|
[error setIsFatal:YES];
|
|
|
|
|
return failureHandler(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|