From 4481a6bf2fdcdde86692b8b0819153b97e53bf00 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Fri, 28 Jan 2022 17:20:34 +0100 Subject: [PATCH] Fix encoding error with location and refresh headers (#33763) Fixes the merge conflicts in #27003 Fixes #24977 Fixes #24047 Closes #27003 Co-Authored-By: Jamie <5964236+jamsinclair@users.noreply.github.com> ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- packages/next/server/server-route-utils.ts | 18 +++++++++++++---- .../custom-routes/test/index.test.js | 20 +++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/next/server/server-route-utils.ts b/packages/next/server/server-route-utils.ts index 5917470c2c1a4af..9c17bc3c2cb1fb2 100644 --- a/packages/next/server/server-route-utils.ts +++ b/packages/next/server/server-route-utils.ts @@ -140,15 +140,25 @@ export const createRedirectRoute = ({ // we need to re-encode them here but still allow passing through // values from rewrites/redirects export const stringifyQuery = (req: BaseNextRequest, query: ParsedUrlQuery) => { - const initialQueryValues = Object.values( - getRequestMeta(req, '__NEXT_INIT_QUERY') || {} - ) + const initialQuery = getRequestMeta(req, '__NEXT_INIT_QUERY') || {} + const initialQueryValues: Array = + Object.values(initialQuery) return stringifyQs(query, undefined, undefined, { encodeURIComponent(value) { - if (initialQueryValues.some((val) => val === value)) { + if ( + value in initialQuery || + initialQueryValues.some((initialQueryVal: string | string[]) => { + // `value` always refers to a query value, even if it's nested in an array + return Array.isArray(initialQueryVal) + ? initialQueryVal.includes(value) + : initialQueryVal === value + }) + ) { + // Encode keys and values from initial query return encodeURIComponent(value) } + return value }, }) diff --git a/test/integration/custom-routes/test/index.test.js b/test/integration/custom-routes/test/index.test.js index 0972067f0375857..ac370167265cc75 100644 --- a/test/integration/custom-routes/test/index.test.js +++ b/test/integration/custom-routes/test/index.test.js @@ -690,6 +690,26 @@ const runTests = (isDev = false) => { expect(res.headers.get('refresh')).toBe(`0;url=/`) }) + it('should have correctly encoded query in location and refresh headers', async () => { + const res = await fetchViaHTTP( + appPort, + // Query unencoded is ?ใƒ†ใ‚นใƒˆ=ใ‚ + '/redirect4?%E3%83%86%E3%82%B9%E3%83%88=%E3%81%82', + undefined, + { + redirect: 'manual', + } + ) + expect(res.status).toBe(308) + + expect(res.headers.get('location').split('?')[1]).toBe( + '%E3%83%86%E3%82%B9%E3%83%88=%E3%81%82' + ) + expect(res.headers.get('refresh')).toBe( + '0;url=/?%E3%83%86%E3%82%B9%E3%83%88=%E3%81%82' + ) + }) + it('should handle basic api rewrite successfully', async () => { const data = await renderViaHTTP(appPort, '/api-hello') expect(JSON.parse(data)).toEqual({ query: {} })