Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/handler/redirect-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ class RedirectHandler {
throw new InvalidArgumentError('maxRedirections must be a positive number')
}

if (opts.throwOnMaxRedirect != null && typeof opts.throwOnMaxRedirect !== 'boolean') {
throw new InvalidArgumentError('throwOnMaxRedirect must be a boolean')
}

this.dispatch = dispatch
this.location = null
const { maxRedirections: _, ...cleanOpts } = opts
Expand Down
6 changes: 3 additions & 3 deletions lib/interceptor/redirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

const RedirectHandler = require('../handler/redirect-handler')

function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections } = {}) {
function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections, throwOnMaxRedirect: defaultThrowOnMaxRedirect } = {}) {
return (dispatch) => {
return function Intercept (opts, handler) {
const { maxRedirections = defaultMaxRedirections, ...rest } = opts
const { maxRedirections = defaultMaxRedirections, throwOnMaxRedirect = defaultThrowOnMaxRedirect, ...rest } = opts

if (maxRedirections == null || maxRedirections === 0) {
return dispatch(opts, handler)
}

const dispatchOpts = { ...rest } // Stop sub dispatcher from also redirecting.
const dispatchOpts = { ...rest, throwOnMaxRedirect } // Stop sub dispatcher from also redirecting.
const redirectHandler = new RedirectHandler(dispatch, maxRedirections, dispatchOpts, handler)
return dispatch(dispatchOpts, redirectHandler)
}
Expand Down
37 changes: 37 additions & 0 deletions test/interceptors/redirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,43 @@ for (const factory of [
await t.completed
})

test('should throw when max redirections is reached and throwOnMaxRedirect is set as interceptor default', async t => {
t = tspl(t, { plan: 1 })

const server = await startRedirectingServer()

const dispatcher = new undici.Agent().compose(
redirect({ maxRedirections: 2, throwOnMaxRedirect: true })
)
after(() => dispatcher.close())

try {
await undici.request(`http://${server}/300`, { dispatcher })
t.fail('Did not throw')
} catch (error) {
t.strictEqual(error.message, 'max redirects')
}

await t.completed
})

test('should not allow invalid throwOnMaxRedirect arguments', async t => {
t = tspl(t, { plan: 1 })

try {
await request(t, 'localhost', undefined, 'http://localhost', {
method: 'GET',
maxRedirections: 1,
throwOnMaxRedirect: 'INVALID'
})
t.fail('Did not throw')
} catch (err) {
t.strictEqual(err.message, 'throwOnMaxRedirect must be a boolean')
}

await t.completed
})

test('when a Location response header is NOT present', async t => {
t = tspl(t, { plan: 6 * 3 })

Expand Down
10 changes: 10 additions & 0 deletions test/types/redirect-interceptor.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expectAssignable, expectNotAssignable } from 'tsd'
import Interceptors from '../../types/interceptors'

expectAssignable<Interceptors.RedirectInterceptorOpts>({})
expectAssignable<Interceptors.RedirectInterceptorOpts>({ maxRedirections: 3 })
expectAssignable<Interceptors.RedirectInterceptorOpts>({ throwOnMaxRedirect: true })
expectAssignable<Interceptors.RedirectInterceptorOpts>({ maxRedirections: 3, throwOnMaxRedirect: true })

expectNotAssignable<Interceptors.RedirectInterceptorOpts>({ maxRedirections: 'INVALID' })
expectNotAssignable<Interceptors.RedirectInterceptorOpts>({ throwOnMaxRedirect: 'INVALID' })
2 changes: 1 addition & 1 deletion types/interceptors.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default Interceptors
declare namespace Interceptors {
export type DumpInterceptorOpts = { maxSize?: number }
export type RetryInterceptorOpts = RetryHandler.RetryOptions
export type RedirectInterceptorOpts = { maxRedirections?: number }
export type RedirectInterceptorOpts = { maxRedirections?: number, throwOnMaxRedirect?: boolean }
export type DecompressInterceptorOpts = {
skipErrorResponses?: boolean
skipStatusCodes?: number[]
Expand Down
Loading