summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alex Sherwin <alex-sherwin@users.noreply.github.com> 2023-06-06 11:09:16 -0400
committerGravatar GitHub <noreply@github.com> 2023-06-06 10:09:16 -0500
commit4929332c3210d1634b8607c7736d9049860a2079 (patch)
treef232675fb2d6820e6262a0f50aaa49efa42e9e0e
parent409c60028aaab09b8f2383ef5730531cd23db4ba (diff)
downloadastro-4929332c3210d1634b8607c7736d9049860a2079.tar.gz
astro-4929332c3210d1634b8607c7736d9049860a2079.tar.zst
astro-4929332c3210d1634b8607c7736d9049860a2079.zip
#7226 - fixes NodeJS adapter for multiple set-cookie headers (and other header issues) (#7227)
* Utilizes the new standard WebAPI Fetch Headers.getSetCookie() function to safely handle multiple set-cookie headers when converting from a WebAPI Response to a NodeJS ServerResponse Modifies the existing nodeMiddleware logic which first set AstroCookies on ServerResponse.setHeader(...) and then called ServerResponse.writeHead(status, Response.headers) which means any that if the WebAPI Response had any set-cookie headers on it, they would replace anything from AstroCookies. The new logic delegates appending AstroCookie values onto the WebAPI Response Headers object, so that a single unified function safely converts the WebAPI Response Headers into a NodeJS compatible OutgoingHttpHeaders object utilizing the new standard Headers.getSetCookie() function provided by the undici WebAPI polyfills. Plus extensive test coverage. * #7226 - changeset for NodeJS adapter set-cookie fix * fixing all double quotes to single quotes --------- Co-authored-by: Alex Sherwin <alex.sherwin@acadia.inc> Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
-rw-r--r--.changeset/chilled-rockets-flash.md5
-rw-r--r--packages/integrations/node/package.json1
-rw-r--r--packages/integrations/node/src/createOutgoingHttpHeaders.ts40
-rw-r--r--packages/integrations/node/src/nodeMiddleware.ts9
-rw-r--r--packages/integrations/node/test/createOutgoingHttpHeaders.test.js78
-rw-r--r--packages/integrations/node/test/fixtures/headers/package.json9
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-multi.astro5
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-single.astro4
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-multi.astro7
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-single.astro5
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-multi.astro5
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-single.astro4
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-multi.ts9
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-single.ts8
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-multi.ts11
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-single.ts9
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/kitchen-sink.ts11
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-multi.ts7
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-single.ts6
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-empty-headers-object.ts4
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-undefined-headers-object.ts3
-rw-r--r--packages/integrations/node/test/fixtures/headers/src/pages/endpoints/simple.ts6
-rw-r--r--packages/integrations/node/test/headers.test.js.js148
-rw-r--r--pnpm-lock.yaml12
24 files changed, 404 insertions, 2 deletions
diff --git a/.changeset/chilled-rockets-flash.md b/.changeset/chilled-rockets-flash.md
new file mode 100644
index 000000000..7c1ba400b
--- /dev/null
+++ b/.changeset/chilled-rockets-flash.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/node': minor
+---
+
+Fixes NodeJS adapter for multiple set-cookie headers and combining AstroCookies and Response.headers cookies
diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json
index 74338af4c..439a2c061 100644
--- a/packages/integrations/node/package.json
+++ b/packages/integrations/node/package.json
@@ -41,6 +41,7 @@
"astro": "workspace:^2.5.7"
},
"devDependencies": {
+ "@types/node": "^18.7.21",
"@types/send": "^0.17.1",
"@types/server-destroy": "^1.0.1",
"astro": "workspace:*",
diff --git a/packages/integrations/node/src/createOutgoingHttpHeaders.ts b/packages/integrations/node/src/createOutgoingHttpHeaders.ts
new file mode 100644
index 000000000..5b99cfa4f
--- /dev/null
+++ b/packages/integrations/node/src/createOutgoingHttpHeaders.ts
@@ -0,0 +1,40 @@
+import type { OutgoingHttpHeaders } from 'http';
+
+/**
+ * Takes in a nullable WebAPI Headers object and produces a NodeJS OutgoingHttpHeaders object suitable for usage
+ * with ServerResponse.writeHead(..) or ServerResponse.setHeader(..)
+ *
+ * @param webHeaders WebAPI Headers object
+ * @returns NodeJS OutgoingHttpHeaders object with multiple set-cookie handled as an array of values
+ */
+export const createOutgoingHttpHeaders = (webHeaders: Headers | undefined | null): OutgoingHttpHeaders | undefined => {
+ if (!webHeaders) {
+ return undefined;
+ }
+
+ // re-type to access Header.getSetCookie()
+ const headers = webHeaders as HeadersWithGetSetCookie;
+
+ // at this point, a multi-value'd set-cookie header is invalid (it was concatenated as a single CSV, which is not valid for set-cookie)
+ const nodeHeaders: OutgoingHttpHeaders = Object.fromEntries(headers.entries());
+
+ if (Object.keys(nodeHeaders).length === 0) {
+ return undefined;
+ }
+
+ // if there is > 1 set-cookie header, we have to fix it to be an array of values
+ if (headers.has('set-cookie')) {
+ const cookieHeaders = headers.getSetCookie();
+ if (cookieHeaders.length > 1) {
+ // the Headers.entries() API already normalized all header names to lower case so we can safely index this as 'set-cookie'
+ nodeHeaders['set-cookie'] = cookieHeaders;
+ }
+ }
+
+ return nodeHeaders;
+};
+
+interface HeadersWithGetSetCookie extends Headers {
+ // the @astrojs/webapi polyfill makes this available (as of undici@5.19.0), but tsc doesn't pick it up on the built-in Headers type from DOM lib
+ getSetCookie(): string[];
+}
diff --git a/packages/integrations/node/src/nodeMiddleware.ts b/packages/integrations/node/src/nodeMiddleware.ts
index c23cdb89c..492130daa 100644
--- a/packages/integrations/node/src/nodeMiddleware.ts
+++ b/packages/integrations/node/src/nodeMiddleware.ts
@@ -3,6 +3,7 @@ import type { IncomingMessage, ServerResponse } from 'http';
import type { Readable } from 'stream';
import { responseIterator } from './response-iterator';
import type { Options } from './types';
+import { createOutgoingHttpHeaders } from './createOutgoingHttpHeaders';
export default function (app: NodeApp, mode: Options['mode']) {
return async function (
@@ -44,12 +45,16 @@ async function writeWebResponse(app: NodeApp, res: ServerResponse, webResponse:
if (app.setCookieHeaders) {
const setCookieHeaders: Array<string> = Array.from(app.setCookieHeaders(webResponse));
+
if (setCookieHeaders.length) {
- res.setHeader('Set-Cookie', setCookieHeaders);
+ for (const setCookieHeader of setCookieHeaders) {
+ webResponse.headers.append('set-cookie', setCookieHeader);
+ }
}
}
- res.writeHead(status, Object.fromEntries(headers.entries()));
+ const nodeHeaders = createOutgoingHttpHeaders(headers);
+ res.writeHead(status, nodeHeaders);
if (webResponse.body) {
try {
for await (const chunk of responseIterator(webResponse) as unknown as Readable) {
diff --git a/packages/integrations/node/test/createOutgoingHttpHeaders.test.js b/packages/integrations/node/test/createOutgoingHttpHeaders.test.js
new file mode 100644
index 000000000..1876c9a00
--- /dev/null
+++ b/packages/integrations/node/test/createOutgoingHttpHeaders.test.js
@@ -0,0 +1,78 @@
+import { expect } from 'chai';
+
+import { createOutgoingHttpHeaders } from '../dist/createOutgoingHttpHeaders.js';
+
+describe('createOutgoingHttpHeaders', () => {
+
+ it('undefined input headers', async () => {
+ const result = createOutgoingHttpHeaders(undefined);
+ expect(result).to.equal(undefined);
+ });
+
+ it('null input headers', async () => {
+ const result = createOutgoingHttpHeaders(undefined);
+ expect(result).to.equal(undefined);
+ });
+
+ it('Empty Headers', async () => {
+ const headers = new Headers();
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.equal(undefined);
+ });
+
+ it('Headers with single key', async () => {
+ const headers = new Headers();
+ headers.append('x-test', 'hello world');
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.deep.equal({ 'x-test': 'hello world' });
+ });
+
+ it('Headers with multiple keys', async () => {
+ const headers = new Headers();
+ headers.append('x-test1', 'hello');
+ headers.append('x-test2', 'world');
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.deep.equal({ 'x-test1': 'hello', 'x-test2': 'world' });
+ });
+
+ it('Headers with multiple values (not set-cookie)', async () => {
+ const headers = new Headers();
+ headers.append('x-test', 'hello');
+ headers.append('x-test', 'world');
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.deep.equal({ 'x-test': 'hello, world' });
+ });
+
+ it('Headers with multiple values (set-cookie special case)', async () => {
+ const headers = new Headers();
+ headers.append('set-cookie', 'hello');
+ headers.append('set-cookie', 'world');
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.deep.equal({ 'set-cookie': ['hello', 'world'] });
+ });
+
+ it('Headers with multiple values (set-cookie case handling)', async () => {
+ const headers = new Headers();
+ headers.append('Set-cookie', 'hello');
+ headers.append('Set-Cookie', 'world');
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.deep.equal({ 'set-cookie': ['hello', 'world'] });
+ });
+
+ it('Headers with all use cases', async () => {
+ 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');
+ const result = createOutgoingHttpHeaders(headers);
+ expect(result).to.deep.equal({
+ 'x-single': 'single',
+ 'x-triple': 'one, two, three',
+ 'set-cookie': ['hello', 'world'],
+ });
+ });
+
+}); \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/package.json b/packages/integrations/node/test/fixtures/headers/package.json
new file mode 100644
index 000000000..6a0858521
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/nodejs-headers",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/node": "workspace:*"
+ }
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-multi.astro b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-multi.astro
new file mode 100644
index 000000000..a9ff193df
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-multi.astro
@@ -0,0 +1,5 @@
+---
+Astro.cookies.set('from1', 'astro1');
+Astro.cookies.set('from2', 'astro2');
+---
+<p>hello world</p> \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-single.astro b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-single.astro
new file mode 100644
index 000000000..c469fd66f
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-cookies-single.astro
@@ -0,0 +1,4 @@
+---
+Astro.cookies.set('from1', 'astro1');
+---
+<p>hello world</p> \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-multi.astro b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-multi.astro
new file mode 100644
index 000000000..91244e838
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-multi.astro
@@ -0,0 +1,7 @@
+---
+Astro.response.headers.append('set-cookie', 'from1=response1');
+Astro.response.headers.append('set-cookie', 'from2=response2');
+Astro.cookies.set('from3', 'astro1');
+Astro.cookies.set('from4', 'astro2');
+---
+<p>hello world</p> \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-single.astro b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-single.astro
new file mode 100644
index 000000000..97719dfa9
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-astro-response-cookie-single.astro
@@ -0,0 +1,5 @@
+---
+Astro.response.headers.append('set-cookie', 'from1=response1');
+Astro.cookies.set('from1', 'astro1');
+---
+<p>hello world</p> \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-multi.astro b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-multi.astro
new file mode 100644
index 000000000..133cbd423
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-multi.astro
@@ -0,0 +1,5 @@
+---
+Astro.response.headers.append('set-cookie', 'from1=value1');
+Astro.response.headers.append('set-cookie', 'from2=value2');
+---
+<p>hello world</p> \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-single.astro b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-single.astro
new file mode 100644
index 000000000..dc76082db
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/astro/component-response-cookies-single.astro
@@ -0,0 +1,4 @@
+---
+Astro.response.headers.append('set-cookie', 'from1=value1');
+---
+<p>hello world</p> \ No newline at end of file
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-multi.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-multi.ts
new file mode 100644
index 000000000..53d0c214c
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-multi.ts
@@ -0,0 +1,9 @@
+import type { APIContext } from 'astro';
+
+export async function get({ request, cookies }: APIContext) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ cookies.set('from1', 'astro1');
+ cookies.set('from2', 'astro2');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-single.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-single.ts
new file mode 100644
index 000000000..5150f3990
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-cookies-single.ts
@@ -0,0 +1,8 @@
+import type { APIContext } from 'astro';
+
+export async function get({ request, cookies }: APIContext) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ cookies.set('from1', 'astro1');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-multi.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-multi.ts
new file mode 100644
index 000000000..215b1a5ba
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-multi.ts
@@ -0,0 +1,11 @@
+import type { APIContext } from 'astro';
+
+export async function get({ request, cookies }: APIContext) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ headers.append('set-cookie', 'from1=response1');
+ headers.append('set-cookie', 'from2=response2');
+ cookies.set('from3', 'astro1');
+ cookies.set('from4', 'astro2');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-single.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-single.ts
new file mode 100644
index 000000000..7202941da
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/astro-response-cookie-single.ts
@@ -0,0 +1,9 @@
+import type { APIContext } from 'astro';
+
+export async function get({ request, cookies }: APIContext) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ headers.append('set-cookie', 'from1=response1');
+ cookies.set('from1', 'astro1');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/kitchen-sink.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/kitchen-sink.ts
new file mode 100644
index 000000000..2b3074a85
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/kitchen-sink.ts
@@ -0,0 +1,11 @@
+export async function get({ request }: { request: Request }) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ headers.append('x-SINGLE', 'single');
+ headers.append('X-triple', 'one');
+ headers.append('x-Triple', 'two');
+ headers.append('x-TRIPLE', 'three');
+ headers.append('SET-cookie', 'hello1=world1');
+ headers.append('Set-Cookie', 'hello2=world2');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-multi.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-multi.ts
new file mode 100644
index 000000000..ed3f867f4
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-multi.ts
@@ -0,0 +1,7 @@
+export async function get({ request }: { request: Request }) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ headers.append('Set-Cookie', 'hello1=world1');
+ headers.append('SET-COOKIE', 'hello2=world2');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-single.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-single.ts
new file mode 100644
index 000000000..25de307ef
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-cookies-single.ts
@@ -0,0 +1,6 @@
+export async function get({ request }: { request: Request }) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ headers.append('Set-Cookie', 'hello1=world1');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-empty-headers-object.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-empty-headers-object.ts
new file mode 100644
index 000000000..0c79cb537
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-empty-headers-object.ts
@@ -0,0 +1,4 @@
+export async function get({ request }: { request: Request }) {
+ const headers = new Headers();
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-undefined-headers-object.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-undefined-headers-object.ts
new file mode 100644
index 000000000..1afc6d843
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/response-undefined-headers-object.ts
@@ -0,0 +1,3 @@
+export async function get({ request }: { request: Request }) {
+ return new Response('hello world', { headers: undefined });
+}
diff --git a/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/simple.ts b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/simple.ts
new file mode 100644
index 000000000..3b728f6fa
--- /dev/null
+++ b/packages/integrations/node/test/fixtures/headers/src/pages/endpoints/simple.ts
@@ -0,0 +1,6 @@
+export async function get({ request }: { request: Request }) {
+ const headers = new Headers();
+ headers.append('content-type', 'text/plain;charset=utf-8');
+ headers.append('X-HELLO', 'world');
+ return new Response('hello world', { headers });
+}
diff --git a/packages/integrations/node/test/headers.test.js.js b/packages/integrations/node/test/headers.test.js.js
new file mode 100644
index 000000000..5b453dd56
--- /dev/null
+++ b/packages/integrations/node/test/headers.test.js.js
@@ -0,0 +1,148 @@
+import nodejs from '../dist/index.js';
+import { loadFixture, createRequestAndResponse } from './test-utils.js';
+import { expect } from 'chai';
+
+describe('Node Adapter Headers', () => {
+ /** @type {import('./test-utils').Fixture} */
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: './fixtures/headers/',
+ output: 'server',
+ adapter: nodejs({ mode: 'middleware' }),
+ });
+ await fixture.build();
+ });
+
+ it('Endpoint Simple Headers', async () => {
+ await runTest('/endpoints/simple', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'x-hello': 'world',
+ });
+ });
+
+ it('Endpoint Astro Single Cookie Header', async () => {
+ await runTest('/endpoints/astro-cookies-single', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'set-cookie': 'from1=astro1',
+ });
+ });
+
+ it('Endpoint Astro Multi Cookie Header', async () => {
+ await runTest('/endpoints/astro-cookies-multi', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'set-cookie': ['from1=astro1', 'from2=astro2'],
+ });
+ });
+
+ it('Endpoint Response Single Cookie Header', async () => {
+ await runTest('/endpoints/response-cookies-single', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'set-cookie': 'hello1=world1',
+ });
+ });
+
+ it('Endpoint Response Multi Cookie Header', async () => {
+ await runTest('/endpoints/response-cookies-multi', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'set-cookie': ['hello1=world1', 'hello2=world2'],
+ });
+ });
+
+ it('Endpoint Complex Headers Kitchen Sink', async () => {
+ await runTest('/endpoints/kitchen-sink', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'x-single': 'single',
+ 'x-triple': 'one, two, three',
+ 'set-cookie': ['hello1=world1', 'hello2=world2'],
+ });
+ });
+
+ it('Endpoint Astro and Response Single Cookie Header', async () => {
+ await runTest('/endpoints/astro-response-cookie-single', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'set-cookie': ['from1=response1', 'from1=astro1'],
+ });
+ });
+
+ it('Endpoint Astro and Response Multi Cookie Header', async () => {
+ await runTest('/endpoints/astro-response-cookie-multi', {
+ 'content-type': 'text/plain;charset=utf-8',
+ 'set-cookie': ['from1=response1', 'from2=response2', 'from3=astro1', 'from4=astro2'],
+ });
+ });
+
+ it('Endpoint Response Empty Headers Object', async () => {
+ await runTest('/endpoints/response-empty-headers-object', {
+ 'content-type': 'text/plain;charset=UTF-8',
+ });
+ });
+
+ it('Endpoint Response undefined Headers Object', async () => {
+ await runTest('/endpoints/response-undefined-headers-object', {
+ 'content-type': 'text/plain;charset=UTF-8',
+ });
+ });
+
+ it('Component Astro Single Cookie Header', async () => {
+ await runTest('/astro/component-astro-cookies-single', {
+ 'content-type': 'text/html',
+ 'set-cookie': 'from1=astro1',
+ });
+ });
+
+ it('Component Astro Multi Cookie Header', async () => {
+ await runTest('/astro/component-astro-cookies-multi', {
+ 'content-type': 'text/html',
+ 'set-cookie': ['from1=astro1', 'from2=astro2'],
+ });
+ });
+
+ it('Component Response Single Cookie Header', async () => {
+ await runTest('/astro/component-response-cookies-single', {
+ 'content-type': 'text/html',
+ 'set-cookie': 'from1=value1',
+ });
+ });
+
+ it('Component Response Multi Cookie Header', async () => {
+ await runTest('/astro/component-response-cookies-multi', {
+ 'content-type': 'text/html',
+ 'set-cookie': ['from1=value1', 'from2=value2'],
+ });
+ });
+
+ it('Component Astro and Response Single Cookie Header', async () => {
+ await runTest('/astro/component-astro-response-cookie-single', {
+ 'content-type': 'text/html',
+ 'set-cookie': ['from1=response1', 'from1=astro1'],
+ });
+ });
+
+ it('Component Astro and Response Multi Cookie Header', async () => {
+ await runTest('/astro/component-astro-response-cookie-multi', {
+ 'content-type': 'text/html',
+ 'set-cookie': ['from1=response1', 'from2=response2', 'from3=astro1', 'from4=astro2'],
+ });
+ });
+
+});
+
+async function runTest(url, expectedHeaders) {
+ const { handler } = await import('./fixtures/headers/dist/server/entry.mjs');
+
+ let { req, res, done } = createRequestAndResponse({
+ method: 'GET',
+ url,
+ });
+
+ handler(req, res);
+
+ req.send();
+
+ await done;
+ const headers = res.getHeaders();
+
+ expect(headers).to.deep.equal(expectedHeaders);
+} \ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 107b9e2c0..29d4f497a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4497,6 +4497,9 @@ importers:
specifier: ^1.0.1
version: 1.0.1
devDependencies:
+ '@types/node':
+ specifier: ^18.7.21
+ version: 18.16.3
'@types/send':
specifier: ^0.17.1
version: 0.17.1
@@ -4561,6 +4564,15 @@ importers:
specifier: workspace:*
version: link:../../../../../astro
+ packages/integrations/node/test/fixtures/headers:
+ dependencies:
+ '@astrojs/node':
+ specifier: workspace:*
+ version: link:../../..
+ astro:
+ specifier: workspace:*
+ version: link:../../../../../astro
+
packages/integrations/node/test/fixtures/node-middleware:
dependencies:
'@astrojs/node':