diff --git a/readme.md b/readme.md index e1ae5bd20..0d7927ec3 100644 --- a/readme.md +++ b/readme.md @@ -368,6 +368,13 @@ Defines if redirect responses should be followed automatically. Note that if a `303` is sent by the server in response to any request type (`POST`, `DELETE`, etc.), Got will automatically request the resource pointed to in the location header via `GET`. This is in accordance with [the spec](https://tools.ietf.org/html/rfc7231#section-6.4.4). +###### maxRedirects + +Type: `number`
+Default: `10` + +If exceeded, the request will be aborted and a `MaxRedirectsError` will be thrown. + ###### decompress Type: `boolean`
diff --git a/source/errors.ts b/source/errors.ts index 95d820723..483ecdae1 100644 --- a/source/errors.ts +++ b/source/errors.ts @@ -76,8 +76,8 @@ export class HTTPError extends GotError { export class MaxRedirectsError extends GotError { readonly response!: Response; - constructor(response: Response, options: NormalizedOptions) { - super('Redirected 10 times. Aborting.', {}, options); + constructor(response: Response, maxRedirects: number, options: NormalizedOptions) { + super(`Redirected ${maxRedirects} times. Aborting.`, {}, options); this.name = 'MaxRedirectsError'; Object.defineProperty(this, 'response', { diff --git a/source/index.ts b/source/index.ts index 9b0228f8a..160646c54 100644 --- a/source/index.ts +++ b/source/index.ts @@ -55,7 +55,8 @@ const defaults: Partial = { dnsCache: false, useElectronNet: false, responseType: 'text', - resolveBodyOnly: false + resolveBodyOnly: false, + maxRedirects: 10 }, mutableDefaults: false }; diff --git a/source/request-as-event-emitter.ts b/source/request-as-event-emitter.ts index af776b02a..43589c043 100644 --- a/source/request-as-event-emitter.ts +++ b/source/request-as-event-emitter.ts @@ -150,8 +150,8 @@ export default (options: NormalizedOptions, input?: TransformStream) => { options.method = 'GET'; } - if (redirects.length >= 10) { - throw new MaxRedirectsError(typedResponse, options); + if (redirects.length >= options.maxRedirects) { + throw new MaxRedirectsError(typedResponse, options.maxRedirects, options); } // Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604 diff --git a/source/utils/types.ts b/source/utils/types.ts index 219705d4c..50d50b439 100644 --- a/source/utils/types.ts +++ b/source/utils/types.ts @@ -161,6 +161,7 @@ export interface Options extends Except; json?: Record; context?: {[key: string]: unknown}; + maxRedirects?: number; } export interface NormalizedOptions extends Except, 'timeout' | 'dnsCache' | 'retry' | 'auth' | 'body' | 'port'> { diff --git a/test/redirects.ts b/test/redirects.ts index 26b672f56..60edd4c38 100644 --- a/test/redirects.ts +++ b/test/redirects.ts @@ -81,6 +81,20 @@ test('throws on endless redirects', withServer, async (t, server, got) => { t.deepEqual(error.response.redirectUrls, new Array(10).fill(`${server.url}/`)); }); +test('custom `maxRedirects` option', withServer, async (t, server, got) => { + server.get('/', (_request, response) => { + response.writeHead(302, { + location: server.url + }); + response.end(); + }); + + const error = await t.throwsAsync(got('', {maxRedirects: 5}), 'Redirected 5 times. Aborting.'); + + // @ts-ignore + t.deepEqual(error.response.redirectUrls, new Array(5).fill(`${server.url}/`)); +}); + test('searchParams are not breaking redirects', withServer, async (t, server, got) => { server.get('/', reachedHandler);