diff --git a/.changeset/calm-dolphins-remain.md b/.changeset/calm-dolphins-remain.md new file mode 100644 index 000000000000..4d2af1f17a4f --- /dev/null +++ b/.changeset/calm-dolphins-remain.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix for adding set-cookie multiple times diff --git a/packages/astro/src/vite-plugin-astro-server/index.ts b/packages/astro/src/vite-plugin-astro-server/index.ts index e162b4cd1e45..67f9514222c9 100644 --- a/packages/astro/src/vite-plugin-astro-server/index.ts +++ b/packages/astro/src/vite-plugin-astro-server/index.ts @@ -47,7 +47,24 @@ function writeHtmlResponse(res: http.ServerResponse, statusCode: number, html: s async function writeWebResponse(res: http.ServerResponse, webResponse: Response) { const { status, headers, body } = webResponse; - res.writeHead(status, Object.fromEntries(headers.entries())); + + let _headers = {}; + if('raw' in headers) { + // Node fetch allows you to get the raw headers, which includes multiples of the same type. + // This is needed because Set-Cookie *must* be called for each cookie, and can't be + // concatenated together. + type HeadersWithRaw = Headers & { + raw: () => Record + }; + + for(const [key, value] of Object.entries((headers as HeadersWithRaw).raw())) { + res.setHeader(key, value); + } + } else { + _headers = Object.fromEntries(headers.entries()); + } + + res.writeHead(status, _headers); if (body) { if (body instanceof Readable) { body.pipe(res); diff --git a/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js b/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js new file mode 100644 index 000000000000..f486927a0eac --- /dev/null +++ b/packages/astro/test/fixtures/ssr-api-route/src/pages/login.js @@ -0,0 +1,11 @@ + +export function post() { + const headers = new Headers(); + headers.append('Set-Cookie', `foo=foo; HttpOnly`); + headers.append('Set-Cookie', `bar=bar; HttpOnly`); + + return new Response('', { + status: 201, + headers, + }); +} diff --git a/packages/astro/test/ssr-api-route.test.js b/packages/astro/test/ssr-api-route.test.js index 666fd217d9d5..496b3a412ed5 100644 --- a/packages/astro/test/ssr-api-route.test.js +++ b/packages/astro/test/ssr-api-route.test.js @@ -56,5 +56,13 @@ describe('API routes in SSR', () => { const text = await response.text(); expect(text).to.equal(`ok`); }); + + it('Can set multiple headers of the same type', async () => { + const response = await fixture.fetch('/login', { + method: 'POST', + }); + const setCookie = response.headers.get('set-cookie'); + expect(setCookie).to.equal('foo=foo; HttpOnly, bar=bar; HttpOnly'); + }); }); });