From 37369550ab57ca529fd6c796e5b0e96e897ca6e5 Mon Sep 17 00:00:00 2001 From: Arsh <69170106+lilnasy@users.noreply.github.com> Date: Wed, 31 Jan 2024 06:07:25 +0000 Subject: [PATCH] fix(dev): prevent comma-separating multiple Set-Cookie headers (#9884) * fix(dev): prevent comma-separating multiple Set-Cookie headers * add changeset * add test --- .changeset/long-peaches-fetch.md | 5 ++ .../src/vite-plugin-astro-server/response.ts | 10 +-- .../vite-plugin-astro-server/response.test.js | 63 +++++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 .changeset/long-peaches-fetch.md create mode 100644 packages/astro/test/units/vite-plugin-astro-server/response.test.js diff --git a/.changeset/long-peaches-fetch.md b/.changeset/long-peaches-fetch.md new file mode 100644 index 0000000000..810b067be8 --- /dev/null +++ b/.changeset/long-peaches-fetch.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Fixes an issue where multiple cookies were sent in a single Set-Cookie header in the dev mode. diff --git a/packages/astro/src/vite-plugin-astro-server/response.ts b/packages/astro/src/vite-plugin-astro-server/response.ts index ac36e703b0..75649a7144 100644 --- a/packages/astro/src/vite-plugin-astro-server/response.ts +++ b/packages/astro/src/vite-plugin-astro-server/response.ts @@ -62,16 +62,10 @@ export async function writeWebResponse(res: http.ServerResponse, webResponse: Re res.setHeader('set-cookie', setCookieHeaders); } - const _headers = Object.fromEntries(headers.entries()); + const _headers: http.OutgoingHttpHeaders = Object.fromEntries(headers.entries()); - // Undici 5.20.0+ includes a `getSetCookie` helper that returns an array of all the `set-cookies` headers. - // Previously, `headers.entries()` would already have these merged, but it seems like this isn't the case anymore. if (headers.has('set-cookie')) { - if ('getSetCookie' in headers && typeof headers.getSetCookie === 'function') { - _headers['set-cookie'] = headers.getSetCookie().toString(); - } else { - _headers['set-cookie'] = headers.get('set-cookie')!; - } + _headers['set-cookie'] = headers.getSetCookie(); } res.writeHead(status, _headers); diff --git a/packages/astro/test/units/vite-plugin-astro-server/response.test.js b/packages/astro/test/units/vite-plugin-astro-server/response.test.js new file mode 100644 index 0000000000..780a725f50 --- /dev/null +++ b/packages/astro/test/units/vite-plugin-astro-server/response.test.js @@ -0,0 +1,63 @@ +import { + createBasicSettings, + createFs, + createRequestAndResponse, + defaultLogger, +} from '../test-utils.js'; +import { fileURLToPath } from 'node:url'; +import { expect } from 'chai'; +import { createContainer } from '../../../dist/core/dev/container.js'; +import testAdapter from '../../test-adapter.js'; + +const root = new URL('../../fixtures/api-routes/', import.meta.url); +const fileSystem = { + '/src/pages/index.js': `export const GET = () => { + const headers = new Headers(); + headers.append('x-single', 'single'); + headers.append('x-triple', 'one'); + headers.append('x-triple', 'two'); + headers.append('x-triple', 'three'); + headers.append('Set-cookie', 'hello'); + headers.append('Set-Cookie', 'world'); + return new Response(null, { headers }); + }`, +}; + +describe('endpoints', () => { + let container; + let settings; + + before(async () => { + const fs = createFs(fileSystem, root); + settings = await createBasicSettings({ + root: fileURLToPath(root), + output: 'server', + adapter: testAdapter(), + }); + container = await createContainer({ + fs, + settings, + logger: defaultLogger, + }); + }); + + after(async () => { + await container.close(); + }); + + it('Headers with multiple values (set-cookie special case)', async () => { + const { req, res, done } = createRequestAndResponse({ + method: 'GET', + url: '/', + }); + container.handle(req, res); + await done; + const headers = res.getHeaders(); + expect(headers).to.deep.equal({ + "access-control-allow-origin": "*", + 'x-single': 'single', + 'x-triple': 'one, two, three', + 'set-cookie': ['hello', 'world'], + }); + }); +});