summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/sharp-moose-perform.md5
-rw-r--r--packages/integrations/netlify/src/netlify-functions.ts31
-rw-r--r--packages/integrations/netlify/test/cookies.test.js50
-rw-r--r--packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro6
-rw-r--r--packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js12
5 files changed, 100 insertions, 4 deletions
diff --git a/.changeset/sharp-moose-perform.md b/.changeset/sharp-moose-perform.md
new file mode 100644
index 000000000..2ade9c227
--- /dev/null
+++ b/.changeset/sharp-moose-perform.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/netlify': patch
+---
+
+Fixes setting multiple cookies with the Netlify adapter
diff --git a/packages/integrations/netlify/src/netlify-functions.ts b/packages/integrations/netlify/src/netlify-functions.ts
index 474cac4b6..bdfb78b8e 100644
--- a/packages/integrations/netlify/src/netlify-functions.ts
+++ b/packages/integrations/netlify/src/netlify-functions.ts
@@ -33,14 +33,37 @@ export const createExports = (manifest: SSRManifest, args: Args) => {
};
}
- const response = await app.render(request);
+ const response: Response = await app.render(request);
const responseBody = await response.text();
- return {
- statusCode: 200,
- headers: Object.fromEntries(response.headers.entries()),
+ const responseHeaders = Object.fromEntries(response.headers.entries());
+ const fnResponse: any = {
+ statusCode: response.status,
+ headers: responseHeaders,
body: responseBody,
};
+
+ // Special-case set-cookie which has to be set an different way :/
+ // The fetch API does not have a way to get multiples of a single header, but instead concatenates
+ // them. There are non-standard ways to do it, and node-fetch gives us headers.raw()
+ // See https://github.com/whatwg/fetch/issues/973 for discussion
+ if (response.headers.has('set-cookie') && 'raw' in response.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<string, string[]>;
+ };
+
+ const rawPacked = (response.headers as HeadersWithRaw).raw();
+ if('set-cookie' in rawPacked) {
+ fnResponse.multiValueHeaders = {
+ 'set-cookie': rawPacked['set-cookie']
+ }
+ }
+ }
+
+ return fnResponse;
};
return { handler };
diff --git a/packages/integrations/netlify/test/cookies.test.js b/packages/integrations/netlify/test/cookies.test.js
new file mode 100644
index 000000000..a8b304f56
--- /dev/null
+++ b/packages/integrations/netlify/test/cookies.test.js
@@ -0,0 +1,50 @@
+import { expect } from 'chai';
+import { load as cheerioLoad } from 'cheerio';
+import { loadFixture } from '../../../astro/test/test-utils.js';
+import netlifyAdapter from '../dist/index.js';
+import { fileURLToPath } from 'url';
+
+describe('Cookies', () => {
+ /** @type {import('../../../astro/test/test-utils').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: new URL('./fixtures/cookies/', import.meta.url).toString(),
+ experimental: {
+ ssr: true,
+ },
+ adapter: netlifyAdapter({
+ dist: new URL('./fixtures/cookies/dist/', import.meta.url),
+ }),
+ site: `http://example.com`,
+ vite: {
+ resolve: {
+ alias: {
+ '@astrojs/netlify/netlify-functions.js': fileURLToPath(
+ new URL('../dist/netlify-functions.js', import.meta.url)
+ ),
+ },
+ },
+ },
+ });
+ await fixture.build();
+ });
+
+ it('Can set multiple', async () => {
+ const entryURL = new URL('./fixtures/cookies/dist/functions/entry.mjs', import.meta.url);
+ const { handler } = await import(entryURL);
+ const resp = await handler({
+ httpMethod: 'POST',
+ headers: {},
+ rawUrl: 'http://example.com/login',
+ body: '{}',
+ isBase64Encoded: false
+ });
+ expect(resp.statusCode).to.equal(301);
+ expect(resp.headers.location).to.equal('/');
+ expect(resp.multiValueHeaders).to.be.deep.equal({
+ 'set-cookie': [ 'foo=foo; HttpOnly', 'bar=bar; HttpOnly' ]
+ });
+ });
+});
diff --git a/packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro b/packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro
new file mode 100644
index 000000000..53e029f04
--- /dev/null
+++ b/packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro
@@ -0,0 +1,6 @@
+<html>
+<head><title>Testing</title></head>
+<body>
+ <h1>Testing</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js b/packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js
new file mode 100644
index 000000000..a9ca52f69
--- /dev/null
+++ b/packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js
@@ -0,0 +1,12 @@
+
+export function post() {
+ const headers = new Headers();
+ headers.append('Set-Cookie', `foo=foo; HttpOnly`);
+ headers.append('Set-Cookie', `bar=bar; HttpOnly`);
+ headers.append('Location', '/');
+
+ return new Response('', {
+ status: 301,
+ headers,
+ });
+}