From 47b0f43fc26ef1545b351bf4289364cad5700a83 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 25 Oct 2021 19:56:53 -0400 Subject: [PATCH 1/5] Fix middleware header propegation --- packages/next/server/next-server.ts | 10 +++++++++- .../middleware-core/pages/responses/_middleware.js | 5 +++++ .../pages/responses/deep/_middleware.js | 10 ++++++++++ .../middleware-core/pages/responses/deep/index.js | 10 ++++++++++ test/integration/middleware-core/test/index.test.js | 10 ++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/integration/middleware-core/pages/responses/deep/_middleware.js create mode 100644 test/integration/middleware-core/pages/responses/deep/index.js diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 0d9149777e1f863..eeb401de1a870ea 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -624,7 +624,7 @@ export default class Server { const subreq = params.request.headers[`x-middleware-subrequest`] const subrequests = typeof subreq === 'string' ? subreq.split(':') : [] - + const allHeaders = new Headers() let result: FetchEventResult | null = null for (const middleware of this.middleware || []) { @@ -667,6 +667,10 @@ export default class Server { }, }) + for (let [key, value] of result.response.headers) { + allHeaders.set(key, value) + } + if (!this.renderOpts.dev) { result.waitUntil.catch((error) => { console.error(`Uncaught: middleware waitUntil errored`, error) @@ -681,6 +685,10 @@ export default class Server { if (!result) { this.render404(params.request, params.response, params.parsed) + } else { + for (let [key, value] of allHeaders) { + result.response.headers.set(key, value) + } } return result diff --git a/test/integration/middleware-core/pages/responses/_middleware.js b/test/integration/middleware-core/pages/responses/_middleware.js index 246ab0a18d479af..c1adb758b6f3e74 100644 --- a/test/integration/middleware-core/pages/responses/_middleware.js +++ b/test/integration/middleware-core/pages/responses/_middleware.js @@ -15,6 +15,11 @@ export async function middleware(request, ev) { next.headers.set('x-nested-header', 'valid') } + // Ensure deep can override top header + if (url.searchParams.get('override-me') === 'true') { + next.headers.set('x-override-me', 'top') + } + // Sends a header if (url.pathname === '/responses/header') { next.headers.set('x-first-header', 'valid') diff --git a/test/integration/middleware-core/pages/responses/deep/_middleware.js b/test/integration/middleware-core/pages/responses/deep/_middleware.js new file mode 100644 index 000000000000000..1f9b62f08522abe --- /dev/null +++ b/test/integration/middleware-core/pages/responses/deep/_middleware.js @@ -0,0 +1,10 @@ +import { NextResponse } from 'next/server' + +export async function middleware(request, _event) { + const next = NextResponse.next() + next.headers.set('x-deep-header', 'valid') + if (request.nextUrl.searchParams.get('override-me') === 'true') { + next.headers.set('x-override-me', 'deep') + } + return next +} diff --git a/test/integration/middleware-core/pages/responses/deep/index.js b/test/integration/middleware-core/pages/responses/deep/index.js new file mode 100644 index 000000000000000..5d2341782b7d411 --- /dev/null +++ b/test/integration/middleware-core/pages/responses/deep/index.js @@ -0,0 +1,10 @@ +function Deep() { + return ( +
+

Deep

+

This is a deep page with deep middleware, check the headers

+
+ ) +} + +export default Deep diff --git a/test/integration/middleware-core/test/index.test.js b/test/integration/middleware-core/test/index.test.js index 121375f254f725e..2ba0cd5b22f8078 100644 --- a/test/integration/middleware-core/test/index.test.js +++ b/test/integration/middleware-core/test/index.test.js @@ -325,6 +325,16 @@ function responseTests(locale = '') { ) expect(res.headers.get('x-first-header')).toBe('valid') }) + + it(`${locale} should respond with top level headers and override with deep headers`, async () => { + const res = await fetchViaHTTP( + context.appPort, + `${locale}/responses/deep?nested-header=true&override-me=true` + ) + expect(res.headers.get('x-nested-header')).toBe('valid') + expect(res.headers.get('x-deep-header')).toBe('valid') + expect(res.headers.get('x-override-me')).toBe('deep') + }) } function interfaceTests(locale = '') { From 5a08c013633cefeb0b7fdbd10fd48745925ed6c7 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 25 Oct 2021 22:24:13 -0400 Subject: [PATCH 2/5] Fix appending headers --- packages/next/server/next-server.ts | 2 +- packages/next/server/web/utils.ts | 22 +++++++++---------- .../pages/responses/_middleware.js | 6 ++--- .../pages/responses/deep/_middleware.js | 4 ++-- .../middleware-core/test/index.test.js | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index eeb401de1a870ea..cfe3191d4f50601 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -668,7 +668,7 @@ export default class Server { }) for (let [key, value] of result.response.headers) { - allHeaders.set(key, value) + allHeaders.append(key, value) } if (!this.renderOpts.dev) { diff --git a/packages/next/server/web/utils.ts b/packages/next/server/web/utils.ts index 3f2b8cb989403bf..57521b9236a97d4 100644 --- a/packages/next/server/web/utils.ts +++ b/packages/next/server/web/utils.ts @@ -20,25 +20,25 @@ export function notImplemented(name: string, method: string): any { ) } -export function fromNodeHeaders(object: NodeHeaders) { - const headers: { [k: string]: string } = {} - for (let headerKey in object) { - const headerValue = object[headerKey] - if (Array.isArray(headerValue)) { - headers[headerKey] = headerValue.join('; ') - } else if (headerValue) { - headers[headerKey] = String(headerValue) +export function fromNodeHeaders(object: NodeHeaders): Headers { + const headers = new Headers() + for (let [key, value] of Object.entries(object)) { + const values = Array.isArray(value) ? value : [value] + for (let v of values) { + if (v !== undefined) { + headers.append(key, v) + } } } return headers } export function toNodeHeaders(headers?: Headers): NodeHeaders { - const object: NodeHeaders = {} + const result: NodeHeaders = {} if (headers) { for (const [key, value] of headers.entries()) { - object[key] = value.includes(';') ? value.split(';') : value + result[key] = value } } - return object + return result } diff --git a/test/integration/middleware-core/pages/responses/_middleware.js b/test/integration/middleware-core/pages/responses/_middleware.js index c1adb758b6f3e74..03f31e1c251cc81 100644 --- a/test/integration/middleware-core/pages/responses/_middleware.js +++ b/test/integration/middleware-core/pages/responses/_middleware.js @@ -15,9 +15,9 @@ export async function middleware(request, ev) { next.headers.set('x-nested-header', 'valid') } - // Ensure deep can override top header - if (url.searchParams.get('override-me') === 'true') { - next.headers.set('x-override-me', 'top') + // Ensure deep can append to this value + if (url.searchParams.get('append-me') === 'true') { + next.headers.append('x-append-me', 'top') } // Sends a header diff --git a/test/integration/middleware-core/pages/responses/deep/_middleware.js b/test/integration/middleware-core/pages/responses/deep/_middleware.js index 1f9b62f08522abe..296476e9035a392 100644 --- a/test/integration/middleware-core/pages/responses/deep/_middleware.js +++ b/test/integration/middleware-core/pages/responses/deep/_middleware.js @@ -3,8 +3,8 @@ import { NextResponse } from 'next/server' export async function middleware(request, _event) { const next = NextResponse.next() next.headers.set('x-deep-header', 'valid') - if (request.nextUrl.searchParams.get('override-me') === 'true') { - next.headers.set('x-override-me', 'deep') + if (request.nextUrl.searchParams.get('append-me') === 'true') { + next.headers.append('x-append-me', 'deep') } return next } diff --git a/test/integration/middleware-core/test/index.test.js b/test/integration/middleware-core/test/index.test.js index 2ba0cd5b22f8078..480f3f94854e4dd 100644 --- a/test/integration/middleware-core/test/index.test.js +++ b/test/integration/middleware-core/test/index.test.js @@ -329,11 +329,11 @@ function responseTests(locale = '') { it(`${locale} should respond with top level headers and override with deep headers`, async () => { const res = await fetchViaHTTP( context.appPort, - `${locale}/responses/deep?nested-header=true&override-me=true` + `${locale}/responses/deep?nested-header=true&append-me=true` ) expect(res.headers.get('x-nested-header')).toBe('valid') expect(res.headers.get('x-deep-header')).toBe('valid') - expect(res.headers.get('x-override-me')).toBe('deep') + expect(res.headers.get('x-append-me')).toBe('top, deep') }) } From d31615b858aa8abe605924b735c32d80c244aaa9 Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 25 Oct 2021 22:37:47 -0400 Subject: [PATCH 3/5] Special case `Set-Cookie` --- packages/next/server/next-server.ts | 6 +++++- packages/next/server/web/utils.ts | 4 ++++ .../middleware-core/pages/responses/_middleware.js | 5 +++++ .../middleware-core/pages/responses/deep/_middleware.js | 3 +++ test/integration/middleware-core/test/index.test.js | 3 ++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index cfe3191d4f50601..d9b6b7fb48b049e 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1164,7 +1164,11 @@ export default class Server { for (const [key, value] of result.response.headers.entries()) { if (key !== 'content-encoding') { - res.setHeader(key, value) + if (key.toLowerCase() === 'set-cookie') { + res.setHeader(key, value.split(', ')) + } else { + res.setHeader(key, value) + } } } diff --git a/packages/next/server/web/utils.ts b/packages/next/server/web/utils.ts index 57521b9236a97d4..130ad3bc7e21a86 100644 --- a/packages/next/server/web/utils.ts +++ b/packages/next/server/web/utils.ts @@ -38,6 +38,10 @@ export function toNodeHeaders(headers?: Headers): NodeHeaders { if (headers) { for (const [key, value] of headers.entries()) { result[key] = value + if (key.toLowerCase() === 'set-cookie') { + console.log('the value of the cookie header is' + value) + result[key] = value.split(', ') + } } } return result diff --git a/test/integration/middleware-core/pages/responses/_middleware.js b/test/integration/middleware-core/pages/responses/_middleware.js index 03f31e1c251cc81..6329017ec0d6e59 100644 --- a/test/integration/middleware-core/pages/responses/_middleware.js +++ b/test/integration/middleware-core/pages/responses/_middleware.js @@ -20,6 +20,11 @@ export async function middleware(request, ev) { next.headers.append('x-append-me', 'top') } + // Ensure deep can append to this value + if (url.searchParams.get('cookie-me') === 'true') { + next.headers.append('set-cookie', 'chocochip') + } + // Sends a header if (url.pathname === '/responses/header') { next.headers.set('x-first-header', 'valid') diff --git a/test/integration/middleware-core/pages/responses/deep/_middleware.js b/test/integration/middleware-core/pages/responses/deep/_middleware.js index 296476e9035a392..4d7262838413b75 100644 --- a/test/integration/middleware-core/pages/responses/deep/_middleware.js +++ b/test/integration/middleware-core/pages/responses/deep/_middleware.js @@ -6,5 +6,8 @@ export async function middleware(request, _event) { if (request.nextUrl.searchParams.get('append-me') === 'true') { next.headers.append('x-append-me', 'deep') } + if (request.nextUrl.searchParams.get('cookie-me') === 'true') { + next.headers.append('set-cookie', 'oatmeal') + } return next } diff --git a/test/integration/middleware-core/test/index.test.js b/test/integration/middleware-core/test/index.test.js index 480f3f94854e4dd..75ce6db001e67da 100644 --- a/test/integration/middleware-core/test/index.test.js +++ b/test/integration/middleware-core/test/index.test.js @@ -329,11 +329,12 @@ function responseTests(locale = '') { it(`${locale} should respond with top level headers and override with deep headers`, async () => { const res = await fetchViaHTTP( context.appPort, - `${locale}/responses/deep?nested-header=true&append-me=true` + `${locale}/responses/deep?nested-header=true&append-me=true&cookie-me=true` ) expect(res.headers.get('x-nested-header')).toBe('valid') expect(res.headers.get('x-deep-header')).toBe('valid') expect(res.headers.get('x-append-me')).toBe('top, deep') + expect(res.headers.raw()['set-cookie']).toEqual(['chocochip', 'oatmeal']) }) } From 86097d81ba97d10c3e614e099d3dda4cd673d59b Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 25 Oct 2021 22:46:00 -0400 Subject: [PATCH 4/5] Remove console.log() --- packages/next/server/web/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/next/server/web/utils.ts b/packages/next/server/web/utils.ts index 130ad3bc7e21a86..1f789163d66a605 100644 --- a/packages/next/server/web/utils.ts +++ b/packages/next/server/web/utils.ts @@ -39,7 +39,6 @@ export function toNodeHeaders(headers?: Headers): NodeHeaders { for (const [key, value] of headers.entries()) { result[key] = value if (key.toLowerCase() === 'set-cookie') { - console.log('the value of the cookie header is' + value) result[key] = value.split(', ') } } From 9d26acadaa21e2dd4d0bdf9805a2c79770d1a8bd Mon Sep 17 00:00:00 2001 From: Steven Date: Mon, 25 Oct 2021 22:50:27 -0400 Subject: [PATCH 5/5] Cleanup tests --- .../middleware-core/pages/responses/deep/_middleware.js | 8 ++------ test/integration/middleware-core/test/index.test.js | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/test/integration/middleware-core/pages/responses/deep/_middleware.js b/test/integration/middleware-core/pages/responses/deep/_middleware.js index 4d7262838413b75..28974fdeb1284e4 100644 --- a/test/integration/middleware-core/pages/responses/deep/_middleware.js +++ b/test/integration/middleware-core/pages/responses/deep/_middleware.js @@ -3,11 +3,7 @@ import { NextResponse } from 'next/server' export async function middleware(request, _event) { const next = NextResponse.next() next.headers.set('x-deep-header', 'valid') - if (request.nextUrl.searchParams.get('append-me') === 'true') { - next.headers.append('x-append-me', 'deep') - } - if (request.nextUrl.searchParams.get('cookie-me') === 'true') { - next.headers.append('set-cookie', 'oatmeal') - } + next.headers.append('x-append-me', 'deep') + next.headers.append('set-cookie', 'oatmeal') return next } diff --git a/test/integration/middleware-core/test/index.test.js b/test/integration/middleware-core/test/index.test.js index 75ce6db001e67da..d359dfa15f50849 100644 --- a/test/integration/middleware-core/test/index.test.js +++ b/test/integration/middleware-core/test/index.test.js @@ -326,7 +326,7 @@ function responseTests(locale = '') { expect(res.headers.get('x-first-header')).toBe('valid') }) - it(`${locale} should respond with top level headers and override with deep headers`, async () => { + it(`${locale} should respond with top level headers and append deep headers`, async () => { const res = await fetchViaHTTP( context.appPort, `${locale}/responses/deep?nested-header=true&append-me=true&cookie-me=true`