diff --git a/ts/test-both/util/BackOff_test.ts b/ts/test-both/util/BackOff_test.ts index 3213791e4..de53b67c9 100644 --- a/ts/test-both/util/BackOff_test.ts +++ b/ts/test-both/util/BackOff_test.ts @@ -42,4 +42,32 @@ describe('BackOff', () => { assert.strictEqual(b.getAndIncrement(), 1); assert.strictEqual(b.getAndIncrement(), 2); }); + + it('should apply jitter', () => { + const b = new BackOff([1, 2, 3], { + jitter: 1, + random: () => 0.5, + }); + + assert.strictEqual(b.getIndex(), 0); + assert.strictEqual(b.isFull(), false); + + assert.strictEqual(b.get(), 1); + assert.strictEqual(b.getAndIncrement(), 1); + assert.strictEqual(b.get(), 2.5); + assert.strictEqual(b.getIndex(), 1); + assert.strictEqual(b.isFull(), false); + + assert.strictEqual(b.getAndIncrement(), 2.5); + assert.strictEqual(b.getIndex(), 2); + assert.strictEqual(b.isFull(), true); + + assert.strictEqual(b.getAndIncrement(), 3.5); + assert.strictEqual(b.getIndex(), 2); + assert.strictEqual(b.isFull(), true); + + assert.strictEqual(b.getAndIncrement(), 3.5); + assert.strictEqual(b.getIndex(), 2); + assert.strictEqual(b.isFull(), true); + }); }); diff --git a/ts/textsecure/SocketManager.ts b/ts/textsecure/SocketManager.ts index ce74897c0..a2623c2b5 100644 --- a/ts/textsecure/SocketManager.ts +++ b/ts/textsecure/SocketManager.ts @@ -32,6 +32,8 @@ const TEN_SECONDS = 10 * durations.SECOND; const FIVE_MINUTES = 5 * durations.MINUTE; +const JITTER = 5 * durations.SECOND; + export type SocketManagerOptions = Readonly<{ url: string; certificateAuthority: string; @@ -54,7 +56,9 @@ export type SocketManagerOptions = Readonly<{ // Incoming requests on unauthenticated resource are not currently supported. // WebSocketResource is responsible for their immediate termination. export class SocketManager extends EventListener { - private backOff = new BackOff(FIBONACCI_TIMEOUTS); + private backOff = new BackOff(FIBONACCI_TIMEOUTS, { + jitter: JITTER, + }); private authenticated?: AbortableProcess; @@ -199,7 +203,7 @@ export class SocketManager extends EventListener { return; } - if (code !== 500 && code !== -1) { + if (!(code >= 500 && code <= 599) && code !== -1) { // No reconnect attempt should be made return; } diff --git a/ts/util/BackOff.ts b/ts/util/BackOff.ts index e021f60fb..baffd9380 100644 --- a/ts/util/BackOff.ts +++ b/ts/util/BackOff.ts @@ -15,13 +15,33 @@ export const FIBONACCI_TIMEOUTS: ReadonlyArray = [ 55 * SECOND, ]; +export type BackOffOptionsType = Readonly<{ + jitter?: number; + + // Testing + random?: () => number; +}>; + +const DEFAULT_RANDOM = () => Math.random(); + export class BackOff { private count = 0; - constructor(private readonly timeouts: ReadonlyArray) {} + constructor( + private readonly timeouts: ReadonlyArray, + private readonly options: BackOffOptionsType = {} + ) {} public get(): number { - return this.timeouts[this.count]; + let result = this.timeouts[this.count]; + const { jitter = 0, random = DEFAULT_RANDOM } = this.options; + + // Do not apply jitter larger than the timeout value. It is supposed to be + // activated for longer timeouts. + if (jitter < result) { + result += random() * jitter; + } + return result; } public getAndIncrement(): number {