From f7cab1a6437cff0f06232527139e85626fd6092e Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Fri, 5 Sep 2025 19:09:21 +0200 Subject: [PATCH 1/7] fix: Avoid merging Location header on response when its an array --- packages/open-next/src/http/util.ts | 6 ++++++ packages/tests-unit/tests/http/utils.test.ts | 22 +++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/open-next/src/http/util.ts b/packages/open-next/src/http/util.ts index d5b29768d..2d82637e9 100644 --- a/packages/open-next/src/http/util.ts +++ b/packages/open-next/src/http/util.ts @@ -12,6 +12,12 @@ export const parseHeaders = ( if (value === undefined) { continue; } + // Next can sometimes return an Array for the Location header + // See: https://github.com/opennextjs/opennextjs-cloudflare/issues/875#issuecomment-3258248276 + if (key.toLowerCase() === "location" && Array.isArray(value)) { + result[key.toLowerCase()] = value[0]; + continue; + } result[key.toLowerCase()] = convertHeader(value); } diff --git a/packages/tests-unit/tests/http/utils.test.ts b/packages/tests-unit/tests/http/utils.test.ts index 635f1d742..36aed6518 100644 --- a/packages/tests-unit/tests/http/utils.test.ts +++ b/packages/tests-unit/tests/http/utils.test.ts @@ -1,4 +1,6 @@ -import { parseSetCookieHeader } from "@opennextjs/aws/http/util.js"; +import type http from "node:http"; + +import { parseHeaders, parseSetCookieHeader } from "@opennextjs/aws/http/util.js"; describe("parseSetCookieHeader", () => { it("returns an empty list if cookies is emptyish", () => { @@ -43,3 +45,21 @@ describe("parseSetCookieHeader", () => { ]); }); }); + +describe("parseHeaders", () => { + it("parses headers correctly", () => { + const headers = parseHeaders({ + location: ["/target", "/target"], + "x-custom-header": "customValue", + "x-multiple-values": ["value1", "value2"], + "x-undefined-header": undefined, + "x-opennext": "is-so-cool", + } as unknown as http.OutgoingHttpHeaders); + expect(headers).toEqual({ + location: "/target", + "x-custom-header": "customValue", + "x-multiple-values": "value1,value2", + "x-opennext": "is-so-cool", + }); + }); +}); From 46d3dda2a2a31b92634d83fb218642a19820161d Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Fri, 5 Sep 2025 19:12:17 +0200 Subject: [PATCH 2/7] changeset --- .changeset/seven-ducks-breathe.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/seven-ducks-breathe.md diff --git a/.changeset/seven-ducks-breathe.md b/.changeset/seven-ducks-breathe.md new file mode 100644 index 000000000..830070962 --- /dev/null +++ b/.changeset/seven-ducks-breathe.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +fix: Avoid merging Location header on response when its an array From 7eed3c0612fea869e1ef3d2645a1a860fa32006d Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Fri, 5 Sep 2025 19:18:54 +0200 Subject: [PATCH 3/7] biome --- packages/tests-unit/tests/http/utils.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/tests-unit/tests/http/utils.test.ts b/packages/tests-unit/tests/http/utils.test.ts index 36aed6518..34f0717dc 100644 --- a/packages/tests-unit/tests/http/utils.test.ts +++ b/packages/tests-unit/tests/http/utils.test.ts @@ -1,6 +1,9 @@ import type http from "node:http"; -import { parseHeaders, parseSetCookieHeader } from "@opennextjs/aws/http/util.js"; +import { + parseHeaders, + parseSetCookieHeader, +} from "@opennextjs/aws/http/util.js"; describe("parseSetCookieHeader", () => { it("returns an empty list if cookies is emptyish", () => { From d409da2614e84ce59f1ac773bd494c432e50d314 Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Fri, 5 Sep 2025 19:24:25 +0200 Subject: [PATCH 4/7] refactor comment --- packages/open-next/src/http/util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/open-next/src/http/util.ts b/packages/open-next/src/http/util.ts index 2d82637e9..9b8585415 100644 --- a/packages/open-next/src/http/util.ts +++ b/packages/open-next/src/http/util.ts @@ -12,7 +12,8 @@ export const parseHeaders = ( if (value === undefined) { continue; } - // Next can sometimes return an Array for the Location header + // Next can return an Array for the Location header + // We dont want to merge that into a comma-separated string // See: https://github.com/opennextjs/opennextjs-cloudflare/issues/875#issuecomment-3258248276 if (key.toLowerCase() === "location" && Array.isArray(value)) { result[key.toLowerCase()] = value[0]; From 63d6f9196f9c79ab6d572f8befa529b028261cf4 Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Sun, 7 Sep 2025 11:40:09 +0200 Subject: [PATCH 5/7] review --- packages/open-next/src/http/util.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/open-next/src/http/util.ts b/packages/open-next/src/http/util.ts index 9b8585415..d4087ddf1 100644 --- a/packages/open-next/src/http/util.ts +++ b/packages/open-next/src/http/util.ts @@ -12,14 +12,24 @@ export const parseHeaders = ( if (value === undefined) { continue; } - // Next can return an Array for the Location header - // We dont want to merge that into a comma-separated string - // See: https://github.com/opennextjs/opennextjs-cloudflare/issues/875#issuecomment-3258248276 - if (key.toLowerCase() === "location" && Array.isArray(value)) { - result[key.toLowerCase()] = value[0]; + const keyLower = key.toLowerCase(); + /** + * Next can return an Array for the Location header + * We dont want to merge that into a comma-separated string + * If they are the same just return one of them + * Otherwise return the last one + * See: https://github.com/opennextjs/opennextjs-cloudflare/issues/875#issuecomment-3258248276 + * and https://github.com/opennextjs/opennextjs-aws/pull/977#issuecomment-3261763114 + */ + if (keyLower === "location" && Array.isArray(value)) { + if (value[0] === value[1]) { + result[keyLower] = value[0]; + } else { + result[keyLower] = value[value.length - 1]; + } continue; } - result[key.toLowerCase()] = convertHeader(value); + result[keyLower] = convertHeader(value); } return result; From ffeb6a2e52f40e1e722cc9ccbb055a156d4f60d8 Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Sun, 7 Sep 2025 12:10:32 +0200 Subject: [PATCH 6/7] add log --- packages/open-next/src/http/util.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/open-next/src/http/util.ts b/packages/open-next/src/http/util.ts index d4087ddf1..e99b3a3c5 100644 --- a/packages/open-next/src/http/util.ts +++ b/packages/open-next/src/http/util.ts @@ -1,4 +1,5 @@ import type http from "node:http"; +import logger from "../logger"; export const parseHeaders = ( headers?: http.OutgoingHttpHeader[] | http.OutgoingHttpHeaders, @@ -25,6 +26,9 @@ export const parseHeaders = ( if (value[0] === value[1]) { result[keyLower] = value[0]; } else { + logger.warn( + "Multiple different values for Location header found. Using the last one", + ); result[keyLower] = value[value.length - 1]; } continue; From de49e8812d7bf2efc4e92dbd144ba1c304448ff7 Mon Sep 17 00:00:00 2001 From: Magnus Dahl Eide Date: Mon, 22 Sep 2025 09:46:59 +0200 Subject: [PATCH 7/7] review --- packages/open-next/src/http/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/open-next/src/http/util.ts b/packages/open-next/src/http/util.ts index e99b3a3c5..de3b07de6 100644 --- a/packages/open-next/src/http/util.ts +++ b/packages/open-next/src/http/util.ts @@ -15,7 +15,7 @@ export const parseHeaders = ( } const keyLower = key.toLowerCase(); /** - * Next can return an Array for the Location header + * Next can return an Array for the Location header when you return null from a get in the cacheHandler on a page that has a redirect() * We dont want to merge that into a comma-separated string * If they are the same just return one of them * Otherwise return the last one