From 1de0ac6ba691cf8b6b9390e815c4bf4bb2733d84 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 14 Sep 2026 07:02:55 +0000 Subject: [PATCH] fix(redirect): ignore informational responses in history Signed-off-by: Matteo Collina --- lib/handler/redirect-handler.js | 5 +++++ test/interceptors/redirect.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/lib/handler/redirect-handler.js b/lib/handler/redirect-handler.js index a5d92508631..7b0a0cf927d 100644 --- a/lib/handler/redirect-handler.js +++ b/lib/handler/redirect-handler.js @@ -57,6 +57,11 @@ class RedirectHandler { } onResponseStart (controller, statusCode, headers, statusMessage) { + if (statusCode < 200) { + this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage) + return + } + if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) { throw new Error('max redirects') } diff --git a/test/interceptors/redirect.js b/test/interceptors/redirect.js index cf0f0a815a9..69c2c5c2087 100644 --- a/test/interceptors/redirect.js +++ b/test/interceptors/redirect.js @@ -812,6 +812,38 @@ test('should redirect to relative URL according to RFC 7231', async t => { t.strictEqual(finalPath, '/absolute/b') }) +test('informational responses are not added to redirect history', async t => { + t = tspl(t, { plan: 4 }) + + const server = createServer((_req, res) => { + for (let i = 0; i < 20; i++) { + res.writeProcessing() + } + res.end('hello world!') + }).listen(0) + + after(() => server.close()) + await once(server, 'listening') + + const dispatcher = new undici.Client(`http://localhost:${server.address().port}`) + .compose(redirect({ maxRedirections: 1, throwOnMaxRedirect: true })) + after(() => dispatcher.close()) + + const infos = [] + const response = await dispatcher.request({ + method: 'GET', + path: '/', + onInfo: info => infos.push(info) + }) + + t.strictEqual(response.statusCode, 200) + t.strictEqual(await response.body.text(), 'hello world!') + t.strictEqual(infos.length, 20) + t.deepStrictEqual(response.context.history, [ + new URL(`http://localhost:${server.address().port}/`) + ]) +}) + test('same-origin redirect preserves plain object headers with polluted Object.prototype[Symbol.iterator]', async (t) => { const { strictEqual } = tspl(t, { plan: 2 })