From e552259c48bb2b092b54970a8214959cc797490f Mon Sep 17 00:00:00 2001 From: nkzawa Date: Tue, 8 Mar 2022 21:28:44 +0700 Subject: [PATCH] hard-navigate when preflight request fails --- packages/next/server/dev/next-dev-server.ts | 7 +++++++ packages/next/shared/lib/router/router.ts | 19 ++++++++++++++----- .../core/pages/errors/_middleware.js | 14 ++++++++++++++ .../middleware/core/pages/errors/index.js | 11 +++++++++++ .../core/pages/errors/throw-on-preflight.js | 12 ++++++++++++ .../middleware/core/test/index.test.js | 14 ++++++++++++++ 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 test/integration/middleware/core/pages/errors/_middleware.js create mode 100644 test/integration/middleware/core/pages/errors/index.js create mode 100644 test/integration/middleware/core/pages/errors/throw-on-preflight.js diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index ce87caa9f6ae..cb23b707bedb 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -540,9 +540,16 @@ export default class DevServer extends Server { return result } catch (error) { this.logErrorWithOriginalStack(error, undefined, 'client') + + const preflight = + params.request.method === 'HEAD' && + params.request.headers['x-middleware-preflight'] + if (preflight) throw error + const err = getProperError(error) ;(err as any).middleware = true const { request, response, parsedUrl } = params + response.statusCode = 500 this.renderError(err, request, response, parsedUrl.pathname) return null } diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index 30214fa6a073..c7c64cf7158a 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -1878,11 +1878,20 @@ export default class Router implements BaseRouter { return { type: 'next' } } - const preflight = await this._getPreflightData({ - preflightHref: options.as, - shouldCache: options.cache, - isPreview: options.isPreview, - }) + let preflight: PreflightData | undefined + try { + preflight = await this._getPreflightData({ + preflightHref: options.as, + shouldCache: options.cache, + isPreview: options.isPreview, + }) + } catch (err) { + // If preflight request fails, we need to do a hard-navigation. + return { + type: 'redirect', + destination: options.as, + } + } if (preflight.rewrite) { // for external rewrites we need to do a hard navigation diff --git a/test/integration/middleware/core/pages/errors/_middleware.js b/test/integration/middleware/core/pages/errors/_middleware.js new file mode 100644 index 000000000000..39f8795f4976 --- /dev/null +++ b/test/integration/middleware/core/pages/errors/_middleware.js @@ -0,0 +1,14 @@ +import { NextResponse } from 'next/server' + +export async function middleware(request) { + const url = request.nextUrl + + if ( + url.pathname === '/errors/throw-on-preflight' && + request.headers.has('x-middleware-preflight') + ) { + throw new Error('test error') + } + + return NextResponse.next() +} diff --git a/test/integration/middleware/core/pages/errors/index.js b/test/integration/middleware/core/pages/errors/index.js new file mode 100644 index 000000000000..4c740edb43a9 --- /dev/null +++ b/test/integration/middleware/core/pages/errors/index.js @@ -0,0 +1,11 @@ +import Link from 'next/link' + +export default function Errors() { + return ( +
+ + Throw on preflight + +
+ ) +} diff --git a/test/integration/middleware/core/pages/errors/throw-on-preflight.js b/test/integration/middleware/core/pages/errors/throw-on-preflight.js new file mode 100644 index 000000000000..ffe346a86103 --- /dev/null +++ b/test/integration/middleware/core/pages/errors/throw-on-preflight.js @@ -0,0 +1,12 @@ +export default function ThrowOnPreflight({ message }) { + return ( +
+

Throw on preflight request

+

{message}

+
+ ) +} + +export const getServerSideProps = ({ query }) => ({ + props: { message: query.message || '' }, +}) diff --git a/test/integration/middleware/core/test/index.test.js b/test/integration/middleware/core/test/index.test.js index b1ad4d164eec..d85a53826ad6 100644 --- a/test/integration/middleware/core/test/index.test.js +++ b/test/integration/middleware/core/test/index.test.js @@ -47,6 +47,8 @@ describe('Middleware base tests', () => { interfaceTests('/fr') urlTests(log) urlTests(log, '/fr') + errorTests() + errorTests('/fr') it('should have showed warning for middleware usage', () => { expect(log.output).toContain(middlewareWarning) @@ -84,6 +86,8 @@ describe('Middleware base tests', () => { interfaceTests('/fr') urlTests(serverOutput) urlTests(serverOutput, '/fr') + errorTests() + errorTests('/fr') it('should have middleware warning during build', () => { expect(buildOutput).toContain(middlewareWarning) @@ -787,6 +791,16 @@ function interfaceTests(locale = '') { }) } +function errorTests(locale = '') { + it(`${locale} should hard-navigate when preflight request failed`, async () => { + const browser = await webdriver(context.appPort, `${locale}/errors`) + await browser.eval('window.__SAME_PAGE = true') + await browser.elementByCss('#throw-on-preflight').click() + await browser.waitForElementByCss('.refreshed') + expect(await browser.eval('window.__SAME_PAGE')).toBeUndefined() + }) +} + function getCookieFromResponse(res, cookieName) { // node-fetch bundles the cookies as string in the Response const cookieArray = res.headers.raw()['set-cookie']