2022-01-12 00:50:11 +00:00
|
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import type { LoggerType } from '../../types/Logging';
|
|
|
|
import * as Errors from '../../types/errors';
|
2022-02-25 00:26:58 +00:00
|
|
|
import { sleepForRateLimitRetryAfterTime } from './sleepForRateLimitRetryAfterTime';
|
2022-01-12 00:50:11 +00:00
|
|
|
import { getHttpErrorCode } from './getHttpErrorCode';
|
|
|
|
import { strictAssert } from '../../util/assert';
|
|
|
|
import { findRetryAfterTimeFromError } from './findRetryAfterTimeFromError';
|
2022-02-16 18:36:21 +00:00
|
|
|
import { SendMessageProtoError } from '../../textsecure/Errors';
|
2022-01-12 00:50:11 +00:00
|
|
|
|
2022-02-16 18:36:21 +00:00
|
|
|
export function maybeExpandErrors(error: unknown): ReadonlyArray<unknown> {
|
|
|
|
if (error instanceof SendMessageProtoError) {
|
|
|
|
return error.errors || [error];
|
|
|
|
}
|
|
|
|
|
|
|
|
return [error];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: toThrow is very important to preserve the full error for outer handlers. For
|
|
|
|
// example, the catch handler check for Safety Number Errors in conversationJobQueue.
|
2022-01-12 00:50:11 +00:00
|
|
|
export async function handleMultipleSendErrors({
|
|
|
|
errors,
|
|
|
|
isFinalAttempt,
|
|
|
|
log,
|
|
|
|
markFailed,
|
|
|
|
timeRemaining,
|
2022-02-16 18:36:21 +00:00
|
|
|
toThrow,
|
2022-01-12 00:50:11 +00:00
|
|
|
}: Readonly<{
|
|
|
|
errors: ReadonlyArray<unknown>;
|
|
|
|
isFinalAttempt: boolean;
|
|
|
|
log: Pick<LoggerType, 'info'>;
|
2022-01-14 21:34:52 +00:00
|
|
|
markFailed?: (() => void) | (() => Promise<void>);
|
2022-01-12 00:50:11 +00:00
|
|
|
timeRemaining: number;
|
2022-02-16 18:36:21 +00:00
|
|
|
toThrow: unknown;
|
2022-01-12 00:50:11 +00:00
|
|
|
}>): Promise<void> {
|
|
|
|
strictAssert(errors.length, 'Expected at least one error');
|
|
|
|
|
|
|
|
const formattedErrors: Array<string> = [];
|
|
|
|
|
|
|
|
let retryAfterError: unknown;
|
|
|
|
let longestRetryAfterTime = -Infinity;
|
|
|
|
|
|
|
|
let serverAskedUsToStop = false;
|
|
|
|
|
|
|
|
errors.forEach(error => {
|
|
|
|
formattedErrors.push(Errors.toLogFormat(error));
|
|
|
|
|
|
|
|
const errorCode = getHttpErrorCode(error);
|
2022-02-25 00:26:58 +00:00
|
|
|
if (errorCode === 413 || errorCode === 429) {
|
2022-01-12 00:50:11 +00:00
|
|
|
const retryAfterTime = findRetryAfterTimeFromError(error);
|
|
|
|
if (retryAfterTime > longestRetryAfterTime) {
|
|
|
|
retryAfterError = error;
|
|
|
|
longestRetryAfterTime = retryAfterTime;
|
|
|
|
}
|
2022-05-23 16:27:40 +00:00
|
|
|
} else if (errorCode === 508 || errorCode === 400) {
|
2022-01-12 00:50:11 +00:00
|
|
|
serverAskedUsToStop = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
`${formattedErrors.length} send error(s): ${formattedErrors.join(',')}`
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isFinalAttempt || serverAskedUsToStop) {
|
2022-01-14 21:34:52 +00:00
|
|
|
await markFailed?.();
|
2022-01-12 00:50:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (serverAskedUsToStop) {
|
2022-05-23 16:27:40 +00:00
|
|
|
log.info('server responded with 508 or 400. Giving up on this job');
|
2022-01-12 00:50:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retryAfterError && !isFinalAttempt) {
|
2022-02-25 00:26:58 +00:00
|
|
|
await sleepForRateLimitRetryAfterTime({
|
2022-01-12 00:50:11 +00:00
|
|
|
err: retryAfterError,
|
|
|
|
log,
|
|
|
|
timeRemaining,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-02-16 18:36:21 +00:00
|
|
|
throw toThrow;
|
2022-01-12 00:50:11 +00:00
|
|
|
}
|