From 4a18e4ab89926b5de8b03167bfc2f255fa852ce6 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 00:41:43 +0200 Subject: [PATCH 01/10] fix standalone mode with app dir --- packages/next/src/build/utils.ts | 108 ++++++++++++++++----- packages/next/src/server/config.ts | 4 + packages/next/src/server/lib/server-ipc.ts | 2 + packages/next/src/server/next.ts | 8 +- 4 files changed, 97 insertions(+), 25 deletions(-) diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index b5960ffb1ceb6..c791fa6166458 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -1912,17 +1912,23 @@ export async function copyTracedFiles( serverOutputPath, `${ moduleType - ? `import Server from 'next/dist/server/next-server.js' -import http from 'http' + ? `import http from 'http' import path from 'path' import { fileURLToPath } from 'url' const __dirname = fileURLToPath(new URL('.', import.meta.url)) -const NextServer = Server.default` + +import httpProxy from 'next/dist/compiled/http-proxy' +import { normalizeRepeatedSlashes } from 'next/dist/shared/lib/utils' +import { Worker } from 'next/dist/compiled/jest-worker' +` : ` -const NextServer = require('next/dist/server/next-server').default const http = require('http') -const path = require('path')` +const path = require('path') +const httpProxy = require('next/dist/compiled/http-proxy') +const { normalizeRepeatedSlashes } = require('next/dist/shared/lib/utils') +const { Worker } = require('next/dist/compiled/jest-worker')` } + process.env.NODE_ENV = 'production' process.chdir(__dirname) @@ -1943,16 +1949,7 @@ const nextConfig = ${JSON.stringify({ distDir: `./${path.relative(dir, distDir)}`, })} -${ - // In standalone mode, we don't have separated render workers so if both - // app and pages are used, we need to resolve to the prebundled React - // to ensure the correctness of the version for app. - `\ -if (nextConfig && nextConfig.experimental && nextConfig.experimental.appDir) { - process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = nextConfig.experimental.serverActions ? 'experimental' : 'next' -} -` -} +process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig) const server = http.createServer(async (req, res) => { try { @@ -1971,20 +1968,87 @@ if ( ) { server.keepAliveTimeout = keepAliveTimeout } -server.listen(currentPort, (err) => { +server.listen(currentPort, async (err) => { if (err) { console.error("Failed to start server", err) process.exit(1) } - const nextServer = new NextServer({ - hostname, - port: currentPort, + + const renderServerPath = require.resolve('next/dist/server/lib/render-server') + const routerWorker = new Worker(renderServerPath, { + numWorkers: 1, + maxRetries: 10, + forkOptions: { + env: { + FORCE_COLOR: '1', + ...process.env, + }, + }, + exposedMethods: ['initialize'], + }) + let didInitialize = false + + for (const _worker of (routerWorker._workerPool?._workers || [])) { + _worker._child.on('exit', (code, signal) => { + // catch failed initializing without retry + if ((code || signal) && !didInitialize) { + routerWorker?.end() + process.exit(1) + } + }) + } + + const workerStdout = routerWorker.getStdout() + const workerStderr = routerWorker.getStderr() + + workerStdout.on('data', (data) => { + process.stdout.write(data) + }) + workerStderr.on('data', (data) => { + process.stderr.write(data) + }) + + const { port: routerPort } = await routerWorker.initialize({ dir: path.join(__dirname), + port: currentPort, dev: false, - customServer: false, - conf: nextConfig, + hostname, + workerType: 'router', }) - handler = nextServer.getRequestHandler() + didInitialize = true + + const getProxyServer = (pathname) => { + const targetUrl = \`http://\${hostname}:\${routerPort}\${pathname}\` + const proxyServer = httpProxy.createProxy({ + target: targetUrl, + changeOrigin: false, + ignorePath: true, + xfwd: true, + ws: true, + followRedirects: false, + }) + return proxyServer + } + + // proxy to router worker + handler = async (req, res) => { + const urlParts = (req.url || '').split('?') + const urlNoQuery = urlParts[0] + + // this normalizes repeated slashes in the path e.g. hello//world -> + // hello/world or backslashes to forward slashes, this does not + // handle trailing slash as that is handled the same as a next.config.js + // redirect + if (urlNoQuery?.match(/(\\|\\/\\/)/)) { + const cleanUrl = normalizeRepeatedSlashes(req.url) + res.statusCode = 308 + res.setHeader('Location', cleanUrl) + res.end(cleanUrl) + return + } + const proxyServer = getProxyServer(req.url || '/') + proxyServer.web(req, res) + } console.log( 'Listening on port', diff --git a/packages/next/src/server/config.ts b/packages/next/src/server/config.ts index 213527483418b..2a33058579777 100644 --- a/packages/next/src/server/config.ts +++ b/packages/next/src/server/config.ts @@ -689,6 +689,10 @@ export default async function loadConfig( rawConfig?: boolean, silent?: boolean ): Promise { + if (process.env.__NEXT_PRIVATE_STANDALONE_CONFIG) { + return JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG) + } + const curLog = silent ? { warn: () => {}, diff --git a/packages/next/src/server/lib/server-ipc.ts b/packages/next/src/server/lib/server-ipc.ts index e711ecaea81c1..1d64797c5aaa8 100644 --- a/packages/next/src/server/lib/server-ipc.ts +++ b/packages/next/src/server/lib/server-ipc.ts @@ -104,6 +104,8 @@ export const createWorker = ( __NEXT_PRIVATE_RENDER_WORKER: type, __NEXT_PRIVATE_ROUTER_IPC_PORT: ipcPort + '', __NEXT_PRIVATE_ROUTER_IPC_KEY: ipcValidationKey, + __NEXT_PRIVATE_STANDALONE_CONFIG: + process.env.__NEXT_PRIVATE_STANDALONE_CONFIG, NODE_ENV: process.env.NODE_ENV, ...(type === 'app' ? { diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts index 867999a701714..29a9bb0367b11 100644 --- a/packages/next/src/server/next.ts +++ b/packages/next/src/server/next.ts @@ -169,9 +169,11 @@ export class NextServer { this.serverPromise = this.loadConfig().then(async (conf) => { if (!this.options.dev) { if (conf.output === 'standalone') { - log.warn( - `"next start" does not work with "output: standalone" configuration. Use "node .next/standalone/server.js" instead.` - ) + if (!process.env.__NEXT_PRIVATE_STANDALONE_CONFIG) { + log.warn( + `"next start" does not work with "output: standalone" configuration. Use "node .next/standalone/server.js" instead.` + ) + } } else if (conf.output === 'export') { throw new Error( `"next start" does not work with "output: export" configuration. Use "npx serve@latest out" instead.` From d80b7b7f6a27e10a240ffc788dc5c4427f0e0260 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 16:50:01 +0200 Subject: [PATCH 02/10] fix untraced files --- packages/next/src/build/index.ts | 50 +++++++--- packages/next/src/build/utils.ts | 87 +--------------- .../server/lib/render-server-standalone.ts | 98 +++++++++++++++++++ 3 files changed, 140 insertions(+), 95 deletions(-) create mode 100644 packages/next/src/server/lib/render-server-standalone.ts diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index ce86c729dfb68..bdf4d7f429b4f 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -1859,10 +1859,21 @@ export default async function build( config.experimental?.turbotrace?.contextDirectory ?? outputFileTracingRoot + // Under standalone mode, we need to trace the extra IPC server and + // worker files. + const isStandalone = config.output === 'standalone' + const nextServerEntry = require.resolve( 'next/dist/server/next-server' ) - const toTrace = [nextServerEntry] + const toTrace = [ + nextServerEntry, + isStandalone + ? require.resolve( + 'next/dist/server/lib/render-server-standalone' + ) + : null, + ].filter(nonNullable) // ensure we trace any dependencies needed for custom // incremental cache handler @@ -1887,7 +1898,7 @@ export default async function build( '**/*.d.ts', '**/*.map', '**/next/dist/pages/**/*', - '**/next/dist/compiled/jest-worker/**/*', + isStandalone ? null : '**/next/dist/compiled/jest-worker/**/*', '**/next/dist/compiled/webpack/(bundle4|bundle5).js', '**/node_modules/webpack5/**/*', '**/next/dist/server/lib/squoosh/**/*.wasm', @@ -1904,7 +1915,8 @@ export default async function build( ? ['**/next/dist/compiled/@ampproject/toolbox-optimizer/**/*'] : []), ...additionalIgnores, - ] + ].filter(nonNullable) + const ignoreFn = (pathname: string) => { if (path.isAbsolute(pathname) && !pathname.startsWith(root)) { return true @@ -1917,6 +1929,26 @@ export default async function build( } const traceContext = path.join(nextServerEntry, '..', '..') const tracedFiles = new Set() + + function addToTracedFiles(base: string, file: string) { + tracedFiles.add( + path + .relative(distDir, path.join(base, file)) + .replace(/\\/g, '/') + ) + } + + if (isStandalone) { + addToTracedFiles( + '', + require.resolve('next/dist/compiled/jest-worker/processChild') + ) + addToTracedFiles( + '', + require.resolve('next/dist/compiled/jest-worker/threadChild') + ) + } + if (config.experimental.turbotrace) { const files: string[] = await nodeFileTrace( { @@ -1932,11 +1964,7 @@ export default async function build( ) for (const file of files) { if (!ignoreFn(path.join(traceContext, file))) { - tracedFiles.add( - path - .relative(distDir, path.join(traceContext, file)) - .replace(/\\/g, '/') - ) + addToTracedFiles(traceContext, file) } } } else { @@ -1947,11 +1975,7 @@ export default async function build( }) serverResult.fileList.forEach((file) => { - tracedFiles.add( - path - .relative(distDir, path.join(root, file)) - .replace(/\\/g, '/') - ) + addToTracedFiles(root, file) }) } await promises.writeFile( diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index c791fa6166458..2beca52ed3e0d 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -1916,19 +1916,16 @@ export async function copyTracedFiles( import path from 'path' import { fileURLToPath } from 'url' const __dirname = fileURLToPath(new URL('.', import.meta.url)) - -import httpProxy from 'next/dist/compiled/http-proxy' -import { normalizeRepeatedSlashes } from 'next/dist/shared/lib/utils' -import { Worker } from 'next/dist/compiled/jest-worker' +import { createServerHandler } from 'next/dist/server/lib/render-server-standalone.js' ` : ` const http = require('http') const path = require('path') -const httpProxy = require('next/dist/compiled/http-proxy') -const { normalizeRepeatedSlashes } = require('next/dist/shared/lib/utils') -const { Worker } = require('next/dist/compiled/jest-worker')` +const { createServerHandler } = require('next/dist/server/lib/render-server-standalone')` } +const dir = path.join(__dirname) + process.env.NODE_ENV = 'production' process.chdir(__dirname) @@ -1974,81 +1971,7 @@ server.listen(currentPort, async (err) => { process.exit(1) } - const renderServerPath = require.resolve('next/dist/server/lib/render-server') - const routerWorker = new Worker(renderServerPath, { - numWorkers: 1, - maxRetries: 10, - forkOptions: { - env: { - FORCE_COLOR: '1', - ...process.env, - }, - }, - exposedMethods: ['initialize'], - }) - let didInitialize = false - - for (const _worker of (routerWorker._workerPool?._workers || [])) { - _worker._child.on('exit', (code, signal) => { - // catch failed initializing without retry - if ((code || signal) && !didInitialize) { - routerWorker?.end() - process.exit(1) - } - }) - } - - const workerStdout = routerWorker.getStdout() - const workerStderr = routerWorker.getStderr() - - workerStdout.on('data', (data) => { - process.stdout.write(data) - }) - workerStderr.on('data', (data) => { - process.stderr.write(data) - }) - - const { port: routerPort } = await routerWorker.initialize({ - dir: path.join(__dirname), - port: currentPort, - dev: false, - hostname, - workerType: 'router', - }) - didInitialize = true - - const getProxyServer = (pathname) => { - const targetUrl = \`http://\${hostname}:\${routerPort}\${pathname}\` - const proxyServer = httpProxy.createProxy({ - target: targetUrl, - changeOrigin: false, - ignorePath: true, - xfwd: true, - ws: true, - followRedirects: false, - }) - return proxyServer - } - - // proxy to router worker - handler = async (req, res) => { - const urlParts = (req.url || '').split('?') - const urlNoQuery = urlParts[0] - - // this normalizes repeated slashes in the path e.g. hello//world -> - // hello/world or backslashes to forward slashes, this does not - // handle trailing slash as that is handled the same as a next.config.js - // redirect - if (urlNoQuery?.match(/(\\|\\/\\/)/)) { - const cleanUrl = normalizeRepeatedSlashes(req.url) - res.statusCode = 308 - res.setHeader('Location', cleanUrl) - res.end(cleanUrl) - return - } - const proxyServer = getProxyServer(req.url || '/') - proxyServer.web(req, res) - } + handler = await createServerHandler({ port: currentPort, hostname, dir }) console.log( 'Listening on port', diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts new file mode 100644 index 0000000000000..dae0c33d6c532 --- /dev/null +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -0,0 +1,98 @@ +import type { IncomingMessage, ServerResponse } from 'http' +import type { ChildProcess } from 'child_process' + +import * as httpProxy from 'next/dist/compiled/http-proxy' +import { Worker } from 'next/dist/compiled/jest-worker' +import { normalizeRepeatedSlashes } from '../../shared/lib/utils' + +const renderServerPath = require.resolve('./render-server') + +export const createServerHandler = async ({ + port, + hostname, + dir, +}: { + port: number + hostname: string + dir: string +}) => { + const routerWorker = new Worker(renderServerPath, { + numWorkers: 1, + maxRetries: 10, + forkOptions: { + env: { + FORCE_COLOR: '1', + ...process.env, + }, + }, + exposedMethods: ['initialize'], + }) as any as InstanceType & { + initialize: typeof import('./render-server').initialize + } + + let didInitialize = false + + for (const _worker of ((routerWorker as any)._workerPool?._workers || []) as { + _child: ChildProcess + }[]) { + _worker._child.on('exit', (code, signal) => { + // catch failed initializing without retry + if ((code || signal) && !didInitialize) { + routerWorker?.end() + process.exit(1) + } + }) + } + + const workerStdout = routerWorker.getStdout() + const workerStderr = routerWorker.getStderr() + + workerStdout.on('data', (data) => { + process.stdout.write(data) + }) + workerStderr.on('data', (data) => { + process.stderr.write(data) + }) + + const { port: routerPort } = await routerWorker.initialize({ + dir, + port, + dev: false, + hostname, + workerType: 'router', + }) + didInitialize = true + + const getProxyServer = (pathname: string) => { + const targetUrl = `http://${hostname}:${routerPort}${pathname}` + const proxyServer = httpProxy.createProxy({ + target: targetUrl, + changeOrigin: false, + ignorePath: true, + xfwd: true, + ws: true, + followRedirects: false, + }) + return proxyServer + } + + // proxy to router worker + return async (req: IncomingMessage, res: ServerResponse) => { + const urlParts = (req.url || '').split('?') + const urlNoQuery = urlParts[0] + + // this normalizes repeated slashes in the path e.g. hello//world -> + // hello/world or backslashes to forward slashes, this does not + // handle trailing slash as that is handled the same as a next.config.js + // redirect + if (urlNoQuery?.match(/(\\|\/\/)/)) { + const cleanUrl = normalizeRepeatedSlashes(req.url!) + res.statusCode = 308 + res.setHeader('Location', cleanUrl) + res.end(cleanUrl) + return + } + const proxyServer = getProxyServer(req.url || '/') + proxyServer.web(req, res) + } +} From 5009338c4b1cfd903dd5b5c50e2d12ac6947a124 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 17:22:02 +0200 Subject: [PATCH 03/10] fix type error --- packages/next/src/server/lib/render-server-standalone.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts index dae0c33d6c532..93823ad2b7221 100644 --- a/packages/next/src/server/lib/render-server-standalone.ts +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -1,7 +1,7 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { ChildProcess } from 'child_process' -import * as httpProxy from 'next/dist/compiled/http-proxy' +import httpProxy from 'next/dist/compiled/http-proxy' import { Worker } from 'next/dist/compiled/jest-worker' import { normalizeRepeatedSlashes } from '../../shared/lib/utils' From d12920d8f7afc66d4f0dea7b350626add929d7c6 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 19:47:45 +0200 Subject: [PATCH 04/10] fix lint --- packages/next/src/server/lib/render-server-standalone.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts index 93823ad2b7221..ba4f58f7411dd 100644 --- a/packages/next/src/server/lib/render-server-standalone.ts +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -30,6 +30,7 @@ export const createServerHandler = async ({ initialize: typeof import('./render-server').initialize } + // eslint-disable-next-line no-loop-func let didInitialize = false for (const _worker of ((routerWorker as any)._workerPool?._workers || []) as { From bee3e14cc5ed4a01416483d2fb46ebe10f39b355 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 20:45:12 +0200 Subject: [PATCH 05/10] fix bug --- packages/next/src/build/index.ts | 2 -- packages/next/src/build/utils.ts | 7 ++++++- packages/next/src/server/lib/render-server-standalone.ts | 3 +++ packages/next/src/server/lib/render-server.ts | 1 + packages/next/src/server/next.ts | 3 --- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 0389058376428..93e074792b051 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -285,8 +285,6 @@ export default async function build( const isAppDirEnabled = !!config.experimental.appDir if (isAppDirEnabled) { - process.env.NEXT_PREBUNDLED_REACT = '1' - if (!process.env.__NEXT_TEST_MODE && ciEnvironment.hasNextSupport) { const requireHook = require.resolve('../server/require-hook') const contents = await promises.readFile(requireHook, 'utf8') diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index c87bfb28c9bec..c24fea22bfbdc 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -1972,7 +1972,12 @@ server.listen(currentPort, async (err) => { process.exit(1) } - handler = await createServerHandler({ port: currentPort, hostname, dir }) + handler = await createServerHandler({ + port: currentPort, + hostname, + dir, + conf: nextConfig, + }) console.log( 'Listening on port', diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts index ba4f58f7411dd..158739acffe01 100644 --- a/packages/next/src/server/lib/render-server-standalone.ts +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -11,10 +11,12 @@ export const createServerHandler = async ({ port, hostname, dir, + minimalMode, }: { port: number hostname: string dir: string + minimalMode: boolean }) => { const routerWorker = new Worker(renderServerPath, { numWorkers: 1, @@ -60,6 +62,7 @@ export const createServerHandler = async ({ port, dev: false, hostname, + minimalMode, workerType: 'router', }) didInitialize = true diff --git a/packages/next/src/server/lib/render-server.ts b/packages/next/src/server/lib/render-server.ts index 8b8d87f08ca4d..773955ac0e385 100644 --- a/packages/next/src/server/lib/render-server.ts +++ b/packages/next/src/server/lib/render-server.ts @@ -42,6 +42,7 @@ export async function initialize(opts: { dir: string port: number dev: boolean + minimalMode?: boolean hostname?: string workerType: 'router' | 'render' keepAliveTimeout?: number diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts index 29a9bb0367b11..85a0085d959c2 100644 --- a/packages/next/src/server/next.ts +++ b/packages/next/src/server/next.ts @@ -180,9 +180,6 @@ export class NextServer { ) } } - if (conf.experimental.appDir) { - process.env.NEXT_PREBUNDLED_REACT = '1' - } this.server = await this.createServer({ ...this.options, conf, From ee5d6a98cb909827cc6e7acf6bf0e3f7c3e5af58 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 21:01:26 +0200 Subject: [PATCH 06/10] fix passing minimalMode --- packages/next/src/server/next-server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 2b0b9382d0e2d..6c28341820f5e 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -263,6 +263,7 @@ export default class NextNodeServer extends BaseServer { dir: this.dir, workerType: 'render', hostname: this.hostname, + minimalMode: this.minimalMode, dev: !!options.dev, } const { createWorker, createIpcServer } = From da2b63a80cbd80e580de324483650981296835f4 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 21:54:36 +0200 Subject: [PATCH 07/10] lint error --- packages/next/src/server/lib/render-server-standalone.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts index 158739acffe01..6488a107c3730 100644 --- a/packages/next/src/server/lib/render-server-standalone.ts +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -32,7 +32,6 @@ export const createServerHandler = async ({ initialize: typeof import('./render-server').initialize } - // eslint-disable-next-line no-loop-func let didInitialize = false for (const _worker of ((routerWorker as any)._workerPool?._workers || []) as { @@ -40,6 +39,7 @@ export const createServerHandler = async ({ }[]) { _worker._child.on('exit', (code, signal) => { // catch failed initializing without retry + // eslint-disable-next-line no-loop-func if ((code || signal) && !didInitialize) { routerWorker?.end() process.exit(1) From 4ebb46f4198326d4cd51e21d145d622e439b2b68 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 3 May 2023 22:44:12 +0200 Subject: [PATCH 08/10] handle errors --- packages/next/src/server/lib/render-server-standalone.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts index 6488a107c3730..7cfae25752ee7 100644 --- a/packages/next/src/server/lib/render-server-standalone.ts +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -98,5 +98,11 @@ export const createServerHandler = async ({ } const proxyServer = getProxyServer(req.url || '/') proxyServer.web(req, res) + proxyServer.on('error', (err, _req, res) => { + res.statusCode = 500 + res.end('Internal Server Error') + + console.error(err) + }) } } From 71911c06897aa439f9794c1686a1e2efa10eedb3 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Thu, 4 May 2023 05:11:20 +0200 Subject: [PATCH 09/10] normalize case --- docs/advanced-features/custom-server.md | 2 +- packages/next/src/build/utils.ts | 2 +- packages/next/src/server/image-optimizer.ts | 2 +- .../server/lib/render-server-standalone.ts | 5 ++-- packages/next/src/server/lib/render-server.ts | 30 +++++++++++-------- .../e2e/streaming-ssr/custom-server/server.js | 2 +- .../error-in-error/test/index.test.js | 4 +-- .../i18n-support/test/index.test.js | 2 +- test/production/custom-server/server.js | 2 +- .../required-server-files-i18n.test.ts | 8 ++--- .../required-server-files.test.ts | 8 ++--- 11 files changed, 36 insertions(+), 31 deletions(-) diff --git a/docs/advanced-features/custom-server.md b/docs/advanced-features/custom-server.md index 073d67ff9f933..ba694180861bb 100644 --- a/docs/advanced-features/custom-server.md +++ b/docs/advanced-features/custom-server.md @@ -51,7 +51,7 @@ app.prepare().then(() => { } catch (err) { console.error('Error occurred handling', req.url, err) res.statusCode = 500 - res.end('internal server error') + res.end('Internal Server Error') } }) .once('error', (err) => { diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index c24fea22bfbdc..6dddc14d054e1 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -1955,7 +1955,7 @@ const server = http.createServer(async (req, res) => { } catch (err) { console.error(err); res.statusCode = 500 - res.end('internal server error') + res.end('Internal Server Error') } }) diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index 3dff37597b20b..2d58841bcb4bf 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -460,7 +460,7 @@ export async function optimizeImage({ console.error( `Error: 'sharp' is required to be installed in standalone mode for the image optimization to function correctly. Read more at: https://nextjs.org/docs/messages/sharp-missing-in-production` ) - throw new ImageError(500, 'internal server error') + throw new ImageError(500, 'Internal Server Error') } // Show sharp warning in production once if (showSharpMissingWarning) { diff --git a/packages/next/src/server/lib/render-server-standalone.ts b/packages/next/src/server/lib/render-server-standalone.ts index 7cfae25752ee7..83be239a0079c 100644 --- a/packages/next/src/server/lib/render-server-standalone.ts +++ b/packages/next/src/server/lib/render-server-standalone.ts @@ -37,9 +37,9 @@ export const createServerHandler = async ({ for (const _worker of ((routerWorker as any)._workerPool?._workers || []) as { _child: ChildProcess }[]) { + // eslint-disable-next-line no-loop-func _worker._child.on('exit', (code, signal) => { // catch failed initializing without retry - // eslint-disable-next-line no-loop-func if ((code || signal) && !didInitialize) { routerWorker?.end() process.exit(1) @@ -98,10 +98,9 @@ export const createServerHandler = async ({ } const proxyServer = getProxyServer(req.url || '/') proxyServer.web(req, res) - proxyServer.on('error', (err, _req, res) => { + proxyServer.on('error', (err) => { res.statusCode = 500 res.end('Internal Server Error') - console.error(err) }) } diff --git a/packages/next/src/server/lib/render-server.ts b/packages/next/src/server/lib/render-server.ts index 773955ac0e385..7d5d73c027183 100644 --- a/packages/next/src/server/lib/render-server.ts +++ b/packages/next/src/server/lib/render-server.ts @@ -55,18 +55,24 @@ export async function initialize(opts: { let requestHandler: RequestHandler const server = http.createServer((req, res) => { - return requestHandler(req, res).finally(() => { - if ( - process.memoryUsage().heapUsed / 1024 / 1024 > - MAXIMUM_HEAP_SIZE_ALLOWED - ) { - warn( - 'The server is running out of memory, restarting to free up memory.' - ) - server.close() - process.exit(WORKER_SELF_EXIT_CODE) - } - }) + return requestHandler(req, res) + .catch((err) => { + res.statusCode = 500 + res.end('Internal Server Error') + console.error(err) + }) + .finally(() => { + if ( + process.memoryUsage().heapUsed / 1024 / 1024 > + MAXIMUM_HEAP_SIZE_ALLOWED + ) { + warn( + 'The server is running out of memory, restarting to free up memory.' + ) + server.close() + process.exit(WORKER_SELF_EXIT_CODE) + } + }) }) if (opts.keepAliveTimeout) { diff --git a/test/e2e/streaming-ssr/custom-server/server.js b/test/e2e/streaming-ssr/custom-server/server.js index c8619cd29f5fe..5442c40ef1679 100644 --- a/test/e2e/streaming-ssr/custom-server/server.js +++ b/test/e2e/streaming-ssr/custom-server/server.js @@ -14,7 +14,7 @@ const server = http.createServer(async (req, res) => { } catch (err) { console.error(err) res.statusCode = 500 - res.end('internal server error') + res.end('Internal Server Error') } }) const currentPort = parseInt(process.env.PORT, 10) || 3000 diff --git a/test/integration/error-in-error/test/index.test.js b/test/integration/error-in-error/test/index.test.js index aa03f34b42a49..cd81039e428bf 100644 --- a/test/integration/error-in-error/test/index.test.js +++ b/test/integration/error-in-error/test/index.test.js @@ -25,7 +25,7 @@ describe('Handles an Error in _error', () => { it('Handles error during SSR', async () => { const html = await renderViaHTTP(port, '/some-404-page') - expect(html).toMatch(/internal server error/i) + expect(html).toMatch(/Internal Server Error/i) }) it('Handles error during client transition', async () => { @@ -33,6 +33,6 @@ describe('Handles an Error in _error', () => { await browser.waitForElementByCss('a').click() await waitFor(1000) const html = await browser.eval('document.body.innerHTML') - expect(html).toMatch(/internal server error/i) + expect(html).toMatch(/Internal Server Error/i) }) }) diff --git a/test/integration/i18n-support/test/index.test.js b/test/integration/i18n-support/test/index.test.js index d5d15292c0d82..8c95dabd6bd85 100644 --- a/test/integration/i18n-support/test/index.test.js +++ b/test/integration/i18n-support/test/index.test.js @@ -83,7 +83,7 @@ describe('i18n Support', () => { 'utf8' ) expect(content).toContain('500') - expect(content).toMatch(/internal server error/i) + expect(content).toMatch(/Internal Server Error/i) } }) }) diff --git a/test/production/custom-server/server.js b/test/production/custom-server/server.js index 4f596181ac669..e0a74bbab0392 100644 --- a/test/production/custom-server/server.js +++ b/test/production/custom-server/server.js @@ -26,7 +26,7 @@ app.prepare().then(() => { } catch (err) { console.error('Error occurred handling', req.url, err) res.statusCode = 500 - res.end('internal server error') + res.end('Internal Server Error') } }).listen(port, (err) => { if (err) throw err diff --git a/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts b/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts index 177302de48b07..045f4099b0691 100644 --- a/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts +++ b/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts @@ -592,7 +592,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/errors/gip', { crash: '1' }) expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => (errors[0].includes('gip hit an oops') ? 'success' : errors[0]), @@ -604,7 +604,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/errors/gssp', { crash: '1' }) expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => (errors[0].includes('gssp hit an oops') ? 'success' : errors[0]), 'success' @@ -615,7 +615,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/errors/gsp/crash') expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => (errors[0].includes('gsp hit an oops') ? 'success' : errors[0]), 'success' @@ -626,7 +626,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/api/error') expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => errors[0].includes('some error from /api/error') diff --git a/test/production/standalone-mode/required-server-files/required-server-files.test.ts b/test/production/standalone-mode/required-server-files/required-server-files.test.ts index fbc5203718ff1..6bab2cfde3f04 100644 --- a/test/production/standalone-mode/required-server-files/required-server-files.test.ts +++ b/test/production/standalone-mode/required-server-files/required-server-files.test.ts @@ -941,7 +941,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/errors/gip', { crash: '1' }) expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => (errors[0].includes('gip hit an oops') ? 'success' : errors[0]), @@ -953,7 +953,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/errors/gssp', { crash: '1' }) expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => (errors[0].includes('gssp hit an oops') ? 'success' : errors[0]), 'success' @@ -964,7 +964,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/errors/gsp/crash') expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => (errors[0].includes('gsp hit an oops') ? 'success' : errors[0]), 'success' @@ -975,7 +975,7 @@ describe('should set-up next', () => { errors = [] const res = await fetchViaHTTP(appPort, '/api/error') expect(res.status).toBe(500) - expect(await res.text()).toBe('internal server error') + expect(await res.text()).toBe('Internal Server Error') await check( () => errors[0].includes('some error from /api/error') From eb5ef487a1ad7bf5bfa6f333d407a17640c10603 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Thu, 4 May 2023 05:19:12 +0200 Subject: [PATCH 10/10] remove top-level --- .../required-server-files-app.test.ts | 6 +++--- .../required-server-files-i18n.test.ts | 10 ++++------ .../required-server-files.test.ts | 14 +++++--------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/test/production/standalone-mode/required-server-files/required-server-files-app.test.ts b/test/production/standalone-mode/required-server-files/required-server-files-app.test.ts index 27ae31428404a..9cfd3a69a5524 100644 --- a/test/production/standalone-mode/required-server-files/required-server-files-app.test.ts +++ b/test/production/standalone-mode/required-server-files/required-server-files-app.test.ts @@ -71,9 +71,9 @@ describe('should set-up next', () => { const testServer = join(next.testDir, 'standalone/server.js') await fs.writeFile( testServer, - (await fs.readFile(testServer, 'utf8')) - .replace('console.error(err)', `console.error('top-level', err)`) - .replace('conf:', `minimalMode: ${minimalMode},conf:`) + ( + await fs.readFile(testServer, 'utf8') + ).replace('conf:', `minimalMode: ${minimalMode},conf:`) ) appPort = await findPort() server = await initNextServerScript( diff --git a/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts b/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts index 045f4099b0691..14a48f86ebc84 100644 --- a/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts +++ b/test/production/standalone-mode/required-server-files/required-server-files-i18n.test.ts @@ -105,9 +105,9 @@ describe('should set-up next', () => { const testServer = join(next.testDir, 'standalone/server.js') await fs.writeFile( testServer, - (await fs.readFile(testServer, 'utf8')) - .replace('console.error(err)', `console.error('top-level', err)`) - .replace('conf:', 'minimalMode: true,conf:') + ( + await fs.readFile(testServer, 'utf8') + ).replace('conf:', 'minimalMode: true,conf:') ) appPort = await findPort() server = await initNextServerScript( @@ -121,9 +121,7 @@ describe('should set-up next', () => { { cwd: next.testDir, onStderr(msg) { - if (msg.includes('top-level')) { - errors.push(msg) - } + errors.push(msg) }, } ) diff --git a/test/production/standalone-mode/required-server-files/required-server-files.test.ts b/test/production/standalone-mode/required-server-files/required-server-files.test.ts index 6bab2cfde3f04..c192562b09e16 100644 --- a/test/production/standalone-mode/required-server-files/required-server-files.test.ts +++ b/test/production/standalone-mode/required-server-files/required-server-files.test.ts @@ -107,9 +107,9 @@ describe('should set-up next', () => { const testServer = join(next.testDir, 'standalone/server.js') await fs.writeFile( testServer, - (await fs.readFile(testServer, 'utf8')) - .replace('console.error(err)', `console.error('top-level', err)`) - .replace('conf:', `minimalMode: ${minimalMode},conf:`) + ( + await fs.readFile(testServer, 'utf8') + ).replace('conf:', `minimalMode: ${minimalMode},conf:`) ) appPort = await findPort() server = await initNextServerScript( @@ -123,9 +123,7 @@ describe('should set-up next', () => { { cwd: next.testDir, onStderr(msg) { - if (msg.includes('top-level')) { - errors.push(msg) - } + errors.push(msg) stderr += msg }, } @@ -1280,9 +1278,7 @@ describe('should set-up next', () => { { cwd: next.testDir, onStderr(msg) { - if (msg.includes('top-level')) { - errors.push(msg) - } + errors.push(msg) stderr += msg }, }