From e0f522a745e5562680a552a292b874388043431f Mon Sep 17 00:00:00 2001 From: SpaceEEC Date: Thu, 4 Oct 2018 00:20:53 +0200 Subject: [PATCH] backport(ClientOptions): add retryLimit (#2869) --- src/client/Client.js | 5 ++++- src/client/rest/RESTManager.js | 1 + src/client/rest/RequestHandlers/Burst.js | 14 ++++++++++---- src/client/rest/RequestHandlers/Sequential.js | 10 ++++++++-- src/util/Constants.js | 3 +++ typings/index.d.ts | 1 + 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/client/Client.js b/src/client/Client.js index b4c9f42c0..504d4a1fd 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -509,7 +509,7 @@ class Client extends EventEmitter { * @param {ClientOptions} [options=this.options] Options to validate * @private */ - _validateOptions(options = this.options) { + _validateOptions(options = this.options) { // eslint-disable-line complexity if (typeof options.shardCount !== 'number' || isNaN(options.shardCount)) { throw new TypeError('The shardCount option must be a number.'); } @@ -540,6 +540,9 @@ class Client extends EventEmitter { throw new TypeError('The restWsBridgeTimeout option must be a number.'); } if (!(options.disabledEvents instanceof Array)) throw new TypeError('The disabledEvents option must be an Array.'); + if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) { + throw new TypeError('The retryLimit options must be a number.'); + } } } diff --git a/src/client/rest/RESTManager.js b/src/client/rest/RESTManager.js index 8acc708c6..ed499091c 100644 --- a/src/client/rest/RESTManager.js +++ b/src/client/rest/RESTManager.js @@ -42,6 +42,7 @@ class RESTManager { } reject(error); }, + retries: 0, }); }); } diff --git a/src/client/rest/RequestHandlers/Burst.js b/src/client/rest/RequestHandlers/Burst.js index a47703b0e..eceee65c9 100644 --- a/src/client/rest/RequestHandlers/Burst.js +++ b/src/client/rest/RequestHandlers/Burst.js @@ -42,11 +42,17 @@ class BurstRequestHandler extends RequestHandler { this.resetTimeout = null; }, Number(res.headers['retry-after']) + this.client.options.restTimeOffset); } else if (err.status >= 500 && err.status < 600) { - this.queue.unshift(item); - this.resetTimeout = this.client.setTimeout(() => { + if (item.retries === this.client.options.retryLimit) { + item.reject(err); this.handle(); - this.resetTimeout = null; - }, 1e3 + this.client.options.restTimeOffset); + } else { + item.retries++; + this.queue.unshift(item); + this.resetTimeout = this.client.setTimeout(() => { + this.handle(); + this.resetTimeout = null; + }, 1e3 + this.client.options.restTimeOffset); + } } else { item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body, res.request.method) : err); diff --git a/src/client/rest/RequestHandlers/Sequential.js b/src/client/rest/RequestHandlers/Sequential.js index 2f7e6e91f..1d574250b 100644 --- a/src/client/rest/RequestHandlers/Sequential.js +++ b/src/client/rest/RequestHandlers/Sequential.js @@ -72,8 +72,14 @@ class SequentialRequestHandler extends RequestHandler { }, Number(res.headers['retry-after']) + this.client.options.restTimeOffset); if (res.headers['x-ratelimit-global']) this.globalLimit = true; } else if (err.status >= 500 && err.status < 600) { - this.queue.unshift(item); - this.client.setTimeout(resolve, 1e3 + this.client.options.restTimeOffset); + if (item.retries === this.client.options.retryLimit) { + item.reject(err); + resolve(); + } else { + item.retries++; + this.queue.unshift(item); + this.client.setTimeout(resolve, 1e3 + this.client.options.restTimeOffset); + } } else { item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body, res.request.method) : err); diff --git a/src/util/Constants.js b/src/util/Constants.js index ab99a742c..5d9a38594 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -24,6 +24,8 @@ exports.Package = require('../../package.json'); * corresponding websocket events * @property {number} [restTimeOffset=500] Extra time in millseconds to wait before continuing to make REST * requests (higher values will reduce rate-limiting errors on bad connections) + * @property {number} [retryLimit=Infinity] How many times to retry on 5XX errors + * (Infinity for indefinite amount of retries) * @property {WSEventType[]} [disabledEvents] An array of disabled websocket events. Events in this array will not be * processed, potentially resulting in performance improvements for larger bots. Only disable events you are * 100% certain you don't need, as many are important, but not obviously so. The safest one to disable with the @@ -42,6 +44,7 @@ exports.DefaultOptions = { disableEveryone: false, sync: false, restWsBridgeTimeout: 5000, + retryLimit: Infinity, disabledEvents: [], restTimeOffset: 500, diff --git a/typings/index.d.ts b/typings/index.d.ts index 4e13377a1..3e387b3df 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1647,6 +1647,7 @@ declare module 'discord.js' { sync?: boolean; restWsBridgeTimeout?: number; restTimeOffset?: number; + retryLimit?: number; disabledEvents?: WSEventType[]; ws?: WebSocketOptions; http?: HTTPOptions;