diff --git a/.changeset/many-elephants-sing.md b/.changeset/many-elephants-sing.md new file mode 100644 index 000000000..2ebdbc5a1 --- /dev/null +++ b/.changeset/many-elephants-sing.md @@ -0,0 +1,8 @@ +--- +"@opennextjs/aws": patch +--- + +fix city name header encoding + +- encode the header in cloudflare wrapper +- decode the header in the routing layer diff --git a/packages/open-next/src/core/routing/middleware.ts b/packages/open-next/src/core/routing/middleware.ts index 4562eb60d..70ef37f66 100644 --- a/packages/open-next/src/core/routing/middleware.ts +++ b/packages/open-next/src/core/routing/middleware.ts @@ -74,7 +74,9 @@ export async function handleMiddleware( const result: Response = await middleware.default({ // `geo` is pre Next 15. geo: { - city: headers["x-open-next-city"], + // The city name is percent-encoded. + // See https://github.com/vercel/vercel/blob/4cb6143/packages/functions/src/headers.ts#L94C19-L94C37 + city: decodeURIComponent(headers["x-open-next-city"]), country: headers["x-open-next-country"], region: headers["x-open-next-region"], latitude: headers["x-open-next-latitude"], diff --git a/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts b/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts index 02a7318eb..3c26f4f44 100644 --- a/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts +++ b/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts @@ -5,8 +5,13 @@ import type { } from "types/open-next"; import type { Wrapper, WrapperHandler } from "types/overrides"; -const cfPropNameToHeaderName = { - city: "x-open-next-city", +const cfPropNameMapping: Record< + string, + string | [(s: string) => string, string] +> = { + // The city name is percent-encoded. + // See https://github.com/vercel/vercel/blob/4cb6143/packages/functions/src/headers.ts#L94C19-L94C37 + city: [encodeURIComponent, "x-open-next-city"], country: "x-open-next-country", regionCode: "x-open-next-region", latitude: "x-open-next-latitude", @@ -46,12 +51,15 @@ const handler: WrapperHandler< const cfProperties = (request as any).cf as | Record | undefined; - for (const [propName, headerName] of Object.entries( - cfPropNameToHeaderName, - )) { + for (const [propName, mapping] of Object.entries(cfPropNameMapping)) { const propValue = cfProperties?.[propName]; if (propValue != null) { - internalEvent.headers[headerName] = propValue; + const [encode, headerName] = Array.isArray(mapping) + ? mapping + : [null, mapping]; + internalEvent.headers[headerName] = encode + ? encode(propValue) + : propValue; } }