diff --git a/lib/web/fetch/index.js b/lib/web/fetch/index.js index ce5a17340f6..75046a29cc1 100644 --- a/lib/web/fetch/index.js +++ b/lib/web/fetch/index.js @@ -2110,7 +2110,15 @@ async function httpNetworkFetch ( const headersList = new HeadersList() for (let i = 0; i < rawHeaders.length; i += 2) { - headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString('latin1'), true) + const nameStr = bufferToLowerCasedHeaderName(rawHeaders[i]) + const value = rawHeaders[i + 1] + if (Array.isArray(value) && !Buffer.isBuffer(rawHeaders[i + 1])) { + for (const val of value) { + headersList.append(nameStr, val.toString('latin1'), true) + } + } else { + headersList.append(nameStr, value.toString('latin1'), true) + } } const contentEncoding = headersList.get('content-encoding', true) if (contentEncoding) { diff --git a/test/issue-4389.js b/test/issue-4389.js new file mode 100644 index 00000000000..92b51ea53d4 --- /dev/null +++ b/test/issue-4389.js @@ -0,0 +1,45 @@ +const { describe, test } = require('node:test') +const assert = require('node:assert') +const { once } = require('node:events') +const { createServer: createHttpServer } = require('node:http') +const { createServer: createSocketServer } = require('node:net') +const { fetch, Agent } = require('..') + +describe('Interceptor should not break multi-value headers', () => { + test('Interceptor should not break Set-Cookie', async (t) => { + const server = createHttpServer({ joinDuplicateHeaders: true }, async (_req, res) => { + const headers = [ + ['set-cookie', 'a=1'], + ['set-cookie', 'b=2'], + ['set-cookie', 'c=3'] + ] + res.writeHead(200, headers) + res.end() + }).listen(0) + + await once(server, 'listening') + t.after(() => server.close()) + + const dispatcher = new Agent().compose((dispatch) => dispatch) + + const { headers } = await fetch(`http://localhost:${server.address().port}`, { dispatcher }) + assert.deepStrictEqual(headers.getSetCookie(), ['a=1', 'b=2', 'c=3']) + }) + + test('Interceptor should not break other multi-value header', async (t) => { + const server = createSocketServer((socket) => { + socket.write('HTTP/1.0 204 No Content\r\n') + socket.write('X-Test-Header: 1\r\n') + socket.write('X-Test-Header: 2\r\n') + socket.write('X-Test-Header: 3\r\n') + socket.end('\r\n\r\n') + }).listen(0) + t.after(() => server.close()) + await once(server, 'listening') + + const dispatcher = new Agent().compose((dispatch) => dispatch) + + const { headers } = await fetch(`http://localhost:${server.address().port}`, { dispatcher }) + assert.deepStrictEqual(headers.get('x-test-header'), '1, 2, 3') + }) +})