From 895bf54e0900e9d8c65ca5b3d7da996174d0dfc9 Mon Sep 17 00:00:00 2001 From: Saya <36309350+Deivu@users.noreply.github.com> Date: Tue, 20 Aug 2019 00:55:07 +0800 Subject: [PATCH] feat: abort Requests that takes a lot of time to resolve (#3327) * Add Request Timeout * Add abort controller in packages * Fix Lint Error. * Fix Lint Errors * Make Timeout Customizable & use finally * Fixed a minor issue * Fix eslint * Update request timeout to use d.js client timeout methods. --- package.json | 1 + src/client/Client.js | 3 +++ src/rest/APIRequest.js | 6 +++++- src/util/Constants.js | 2 ++ typings/index.d.ts | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c454decfcac..c234bfb7f6aa 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "runkitExampleFilename": "./docs/examples/ping.js", "unpkg": "./webpack/discord.min.js", "dependencies": { + "abort-controller": "^3.0.0", "form-data": "^2.3.3", "node-fetch": "^2.3.0", "pako": "^1.0.8", diff --git a/src/client/Client.js b/src/client/Client.js index 85a93bb8a6f1..3be8e311564a 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -392,6 +392,9 @@ class Client extends BaseClient { if (typeof options.restWsBridgeTimeout !== 'number' || isNaN(options.restWsBridgeTimeout)) { throw new TypeError('CLIENT_INVALID_OPTION', 'restWsBridgeTimeout', 'a number'); } + if (typeof options.restRequestTimeout !== 'number' || isNaN(options.restRequestTimeout)) { + throw new TypeError('CLIENT_INVALID_OPTION', 'restRequestTimeout', 'a number'); + } if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) { throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number'); } diff --git a/src/rest/APIRequest.js b/src/rest/APIRequest.js index 672cccd4ea0e..520febca025d 100644 --- a/src/rest/APIRequest.js +++ b/src/rest/APIRequest.js @@ -4,6 +4,7 @@ const FormData = require('form-data'); const https = require('https'); const { browser, UserAgent } = require('../util/Constants'); const fetch = require('node-fetch'); +const AbortController = require('abort-controller'); if (https.Agent) var agent = new https.Agent({ keepAlive: true }); @@ -46,12 +47,15 @@ class APIRequest { headers['Content-Type'] = 'application/json'; } + const controller = new AbortController(); + const timeout = this.client.setTimeout(() => controller.abort(), this.client.options.restRequestTimeout); return fetch(url, { method: this.method, headers, agent, body, - }); + signal: controller.signal, + }).finally(() => this.client.clearTimeout(timeout)); } } diff --git a/src/util/Constants.js b/src/util/Constants.js index 1286e18c2eff..50033919dd7e 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -28,6 +28,7 @@ const browser = exports.browser = typeof window !== 'undefined'; * 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} [restRequestTimeout=15000] Time to wait before cancelling a REST request * @property {number} [restSweepInterval=60] How frequently to delete inactive request buckets, in seconds * (or 0 for never) * @property {number} [retryLimit=1] How many times to retry on 5XX errors (Infinity for indefinite amount of retries) @@ -50,6 +51,7 @@ exports.DefaultOptions = { partials: [], restWsBridgeTimeout: 5000, disabledEvents: [], + restRequestTimeout: 15000, retryLimit: 1, restTimeOffset: 500, restSweepInterval: 60, diff --git a/typings/index.d.ts b/typings/index.d.ts index 877dfed320f2..0bc65d4e599f 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2018,6 +2018,7 @@ declare module 'discord.js' { partials?: PartialTypes[]; restWsBridgeTimeout?: number; restTimeOffset?: number; + restRequestTimeout?: number; restSweepInterval?: number; retryLimit?: number; presence?: PresenceData;