Allow setting multiple cookies in Netlify adapter (#3092)
* Allow setting multiple cookies in Netlify adapter * Adds a changeset * Set the response status code * Add a comment on why this is needed
This commit is contained in:
parent
c459c87325
commit
a5caf08e24
5 changed files with 100 additions and 4 deletions
5
.changeset/sharp-moose-perform.md
Normal file
5
.changeset/sharp-moose-perform.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@astrojs/netlify': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes setting multiple cookies with the Netlify adapter
|
|
@ -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();
|
const responseBody = await response.text();
|
||||||
|
|
||||||
return {
|
const responseHeaders = Object.fromEntries(response.headers.entries());
|
||||||
statusCode: 200,
|
const fnResponse: any = {
|
||||||
headers: Object.fromEntries(response.headers.entries()),
|
statusCode: response.status,
|
||||||
|
headers: responseHeaders,
|
||||||
body: responseBody,
|
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 };
|
return { handler };
|
||||||
|
|
50
packages/integrations/netlify/test/cookies.test.js
Normal file
50
packages/integrations/netlify/test/cookies.test.js
Normal file
|
@ -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' ]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
6
packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro
vendored
Normal file
6
packages/integrations/netlify/test/fixtures/cookies/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<head><title>Testing</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Testing</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js
vendored
Normal file
12
packages/integrations/netlify/test/fixtures/cookies/src/pages/login.js
vendored
Normal file
|
@ -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,
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue