diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts index 275a7b8286a18..8754c8af2c774 100644 --- a/packages/next/src/cli/next-dev.ts +++ b/packages/next/src/cli/next-dev.ts @@ -2,7 +2,7 @@ import '../server/lib/cpu-profile' import type { StartServerOptions } from '../server/lib/start-server' -import { getPort, printAndExit } from '../server/lib/utils' +import { RESTART_EXIT_CODE, getPort, printAndExit } from '../server/lib/utils' import * as Log from '../build/output/log' import { CliCommand } from '../lib/commands' import { getProjectDir } from '../lib/get-project-dir' @@ -24,7 +24,6 @@ import { initialEnv, loadEnvConfig } from '@next/env' import { trace } from '../trace' import { validateTurboNextConfig } from '../lib/turbopack-warning' import { fork } from 'child_process' -import { RESTART_EXIT_CODE } from '../server/lib/setup-server-worker' import { getReservedPortExplanation, isPortIsReserved, diff --git a/packages/next/src/experimental/testmode/server.ts b/packages/next/src/experimental/testmode/server.ts index 6ae34bdf2fac1..c206efa7ecd19 100644 --- a/packages/next/src/experimental/testmode/server.ts +++ b/packages/next/src/experimental/testmode/server.ts @@ -5,8 +5,8 @@ import type { ProxyResponse, } from './proxy' import { ClientRequestInterceptor } from 'next/dist/compiled/@mswjs/interceptors/ClientRequest' -import { WorkerRequestHandler } from '../../server/lib/setup-server-worker' -import { NodeRequestHandler } from '../../server/next-server' +import type { WorkerRequestHandler } from '../../server/lib/types' +import type { NodeRequestHandler } from '../../server/next-server' interface TestReqInfo { url: string diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 265e472ab8612..0a07434a640f7 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -194,9 +194,6 @@ export interface Options { */ httpServer?: import('http').Server - _routerWorker?: boolean - _renderWorker?: boolean - isNodeDebugging?: 'brk' | boolean } @@ -373,7 +370,6 @@ export default abstract class Server { public readonly matchers: RouteMatcherManager protected readonly i18nProvider?: I18NProvider protected readonly localeNormalizer?: LocaleRouteNormalizer - protected readonly isRenderWorker?: boolean public constructor(options: ServerOptions) { const { @@ -388,7 +384,6 @@ export default abstract class Server { } = options this.serverOptions = options - this.isRenderWorker = options._renderWorker this.dir = process.env.NEXT_RUNTIME === 'edge' ? dir : require('path').resolve(dir) @@ -2640,7 +2635,6 @@ export default abstract class Server { const invokeOutput = ctx.req.headers['x-invoke-output'] if ( !this.minimalMode && - this.isRenderWorker && typeof invokeOutput === 'string' && isDynamicRoute(invokeOutput || '') && invokeOutput !== match.definition.pathname diff --git a/packages/next/src/server/dev/next-dev-server.ts b/packages/next/src/server/dev/next-dev-server.ts index 32bd6a2001c83..2928278686ca0 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -487,16 +487,10 @@ export default class DevServer extends Server { err?: unknown, type?: 'unhandledRejection' | 'uncaughtException' | 'warning' | 'app-dir' ): Promise { - if (this.isRenderWorker) { - await this.invokeDevMethod({ - method: 'logErrorWithOriginalStack', - args: [err, type], - }) - return - } - throw new Error( - 'Invariant logErrorWithOriginalStack called outside render worker' - ) + await this.invokeDevMethod({ + method: 'logErrorWithOriginalStack', + args: [err, type], + }) } protected getPagesManifest(): PagesManifest | undefined { @@ -731,10 +725,6 @@ export default class DevServer extends Server { appPaths?: ReadonlyArray | null match?: RouteMatch }): Promise { - if (!this.isRenderWorker) { - throw new Error('Invariant ensurePage called outside render worker') - } - await this.invokeDevMethod({ method: 'ensurePage', args: [opts], @@ -795,27 +785,17 @@ export default class DevServer extends Server { } protected async getFallbackErrorComponents(): Promise { - if (this.isRenderWorker) { - await this.invokeDevMethod({ - method: 'getFallbackErrorComponents', - args: [], - }) - return await loadDefaultErrorComponents(this.distDir) - } - throw new Error( - `Invariant getFallbackErrorComponents called outside render worker` - ) + await this.invokeDevMethod({ + method: 'getFallbackErrorComponents', + args: [], + }) + return await loadDefaultErrorComponents(this.distDir) } async getCompilationError(page: string): Promise { - if (this.isRenderWorker) { - return await this.invokeDevMethod({ - method: 'getCompilationError', - args: [page], - }) - } - throw new Error( - 'Invariant getCompilationError called outside render worker' - ) + return await this.invokeDevMethod({ + method: 'getCompilationError', + args: [page], + }) } } diff --git a/packages/next/src/server/lib/render-server.ts b/packages/next/src/server/lib/render-server.ts index 9d6a25815507d..ae18442393e5c 100644 --- a/packages/next/src/server/lib/render-server.ts +++ b/packages/next/src/server/lib/render-server.ts @@ -71,7 +71,6 @@ export async function initialize(opts: { dev: boolean minimalMode?: boolean hostname?: string - workerType: 'router' | 'render' isNodeDebugging: boolean keepAliveTimeout?: number serverFields?: any @@ -97,8 +96,6 @@ export async function initialize(opts: { const app = next({ ...opts, - _routerWorker: opts.workerType === 'router', - _renderWorker: opts.workerType === 'render', hostname: opts.hostname || 'localhost', customServer: false, httpServer: opts.server, diff --git a/packages/next/src/server/lib/route-resolver.ts b/packages/next/src/server/lib/route-resolver.ts index 45d013f819082..d12fb57cef8a9 100644 --- a/packages/next/src/server/lib/route-resolver.ts +++ b/packages/next/src/server/lib/route-resolver.ts @@ -1,5 +1,6 @@ import type { NextConfigComplete } from '../config-shared' import type { IncomingMessage, ServerResponse } from 'http' +import type { RenderServer } from './router-server' import '../require-hook' import '../node-polyfill-fetch' @@ -18,7 +19,6 @@ import { PERMANENT_REDIRECT_STATUS } from '../../shared/lib/constants' import { formatHostname } from './format-hostname' import { signalFromNodeResponse } from '../web/spec-extension/adapters/next-request' import { getMiddlewareRouteMatcher } from '../../shared/lib/router/utils/middleware-route-matcher' -import type { RenderWorker } from './router-server' import { pipeReadable } from '../pipe-readable' type RouteResult = @@ -125,57 +125,54 @@ export async function makeResolver( hostname, isNodeDebugging: false, dev: true, - workerType: 'render', }, { - pages: { - async initialize() { - return { - async requestHandler(req, res) { - if (!req.headers['x-middleware-invoke']) { - throw new Error(`Invariant unexpected request handler call`) - } - - const cloneableBody = getCloneableBody(req) - const { run } = - require('../web/sandbox') as typeof import('../web/sandbox') - - const result = await run({ - distDir, - name: middlewareInfo.name || '/', - paths: middlewareInfo.paths || [], - edgeFunctionEntry: middlewareInfo, - request: { - headers: req.headers, - method: req.method || 'GET', - nextConfig: { - i18n: nextConfig.i18n, - basePath: nextConfig.basePath, - trailingSlash: nextConfig.trailingSlash, - }, - url: `http://${fetchHostname}:${port}${req.url}`, - body: cloneableBody, - signal: signalFromNodeResponse(res), + async initialize() { + return { + async requestHandler(req, res) { + if (!req.headers['x-middleware-invoke']) { + throw new Error(`Invariant unexpected request handler call`) + } + + const cloneableBody = getCloneableBody(req) + const { run } = + require('../web/sandbox') as typeof import('../web/sandbox') + + const result = await run({ + distDir, + name: middlewareInfo.name || '/', + paths: middlewareInfo.paths || [], + edgeFunctionEntry: middlewareInfo, + request: { + headers: req.headers, + method: req.method || 'GET', + nextConfig: { + i18n: nextConfig.i18n, + basePath: nextConfig.basePath, + trailingSlash: nextConfig.trailingSlash, }, - useCache: true, - onWarning: console.warn, - }) - - const err = new Error() - ;(err as any).result = result - throw err - }, - async upgradeHandler() { - throw new Error(`Invariant: unexpected upgrade handler call`) - }, - } - }, - deleteAppClientCache() {}, - async deleteCache() {}, - async clearModuleContext() {}, - async propagateServerField() {}, - } as Partial as any, - }, + url: `http://${fetchHostname}:${port}${req.url}`, + body: cloneableBody, + signal: signalFromNodeResponse(res), + }, + useCache: true, + onWarning: console.warn, + }) + + const err = new Error() + ;(err as any).result = result + throw err + }, + async upgradeHandler() { + throw new Error(`Invariant: unexpected upgrade handler call`) + }, + } + }, + deleteAppClientCache() {}, + async deleteCache() {}, + async clearModuleContext() {}, + async propagateServerField() {}, + } as Partial as any, {} as any ) diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 4d1d92f8e968e..c8158e295baf2 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -1,10 +1,7 @@ import type { IncomingMessage } from 'http' // this must come first as it includes require hooks -import type { - WorkerRequestHandler, - WorkerUpgradeHandler, -} from './setup-server-worker' +import type { WorkerRequestHandler, WorkerUpgradeHandler } from './types' // This is required before other imports to ensure the require hook is setup. import '../node-polyfill-fetch' @@ -41,7 +38,7 @@ import type { NextJsHotReloaderInterface } from '../dev/hot-reloader-types' const debug = setupDebug('next:router-server:main') -export type RenderWorker = Pick< +export type RenderServer = Pick< typeof import('./render-server'), | 'initialize' | 'deleteCache' @@ -50,16 +47,15 @@ export type RenderWorker = Pick< | 'propagateServerField' > -export interface RenderWorkers { - app?: RenderWorker - pages?: RenderWorker -} - const devInstances: Record< string, UnwrapPromise> > = {} +export interface LazyRenderServerInstance { + instance?: RenderServer +} + const requestHandlers: Record = {} export async function initialize(opts: { @@ -69,7 +65,6 @@ export async function initialize(opts: { server?: import('http').Server minimalMode?: boolean hostname?: string - workerType: 'router' | 'render' isNodeDebugging: boolean keepAliveTimeout?: number customServer?: boolean @@ -102,7 +97,7 @@ export async function initialize(opts: { minimalMode: opts.minimalMode, }) - const renderWorkers: RenderWorkers = {} + const renderServer: LazyRenderServerInstance = {} let devInstance: | UnwrapPromise< @@ -121,7 +116,7 @@ export async function initialize(opts: { devInstance = await setupDev({ // Passed here but the initialization of this object happens below, doing the initialization before the setupDev call breaks. - renderWorkers, + renderServer, appDir, pagesDir, telemetry, @@ -202,15 +197,12 @@ export async function initialize(opts: { } as any } - renderWorkers.app = + renderServer.instance = require('./render-server') as typeof import('./render-server') - renderWorkers.pages = renderWorkers.app - - const renderWorkerOpts: Parameters[0] = { + const renderServerOpts: Parameters[0] = { port: opts.port, dir: opts.dir, - workerType: 'render', hostname: opts.hostname, minimalMode: opts.minimalMode, dev: !!opts.dev, @@ -222,11 +214,7 @@ export async function initialize(opts: { } // pre-initialize workers - const handlers = await renderWorkers.app?.initialize(renderWorkerOpts) - const initialized = { - app: handlers, - pages: handlers, - } + const handlers = await renderServer.instance.initialize(renderServerOpts) const logError = async ( type: 'uncaughtException' | 'unhandledRejection', @@ -235,23 +223,6 @@ export async function initialize(opts: { await devInstance?.logErrorWithOriginalStack(err, type) } - const cleanup = () => { - debug('router-server process cleanup') - for (const curWorker of [ - ...((renderWorkers.pages as any)?._workerPool?._workers || []), - ] as { - _child?: import('child_process').ChildProcess - }[]) { - curWorker._child?.kill('SIGINT') - } - - if (!process.env.__NEXT_PRIVATE_CPU_PROFILE) { - process.exit(0) - } - } - process.on('exit', cleanup) - process.on('SIGINT', cleanup) - process.on('SIGTERM', cleanup) process.on('uncaughtException', logError.bind(null, 'uncaughtException')) process.on('unhandledRejection', logError.bind(null, 'unhandledRejection')) @@ -259,8 +230,8 @@ export async function initialize(opts: { fsChecker, config, opts, - renderWorkers, - renderWorkerOpts, + renderServer.instance, + renderServerOpts, devInstance?.ensureMiddleware ) @@ -280,7 +251,6 @@ export async function initialize(opts: { async function invokeRender( parsedUrl: NextUrlWithParsedQuery, - type: keyof typeof renderWorkers, invokePath: string, handleIndex: number, additionalInvokeHeaders: Record = {} @@ -310,10 +280,8 @@ export async function initialize(opts: { return null } - const workerResult = initialized[type] - - if (!workerResult) { - throw new Error(`Failed to initialize render worker ${type}`) + if (!handlers) { + throw new Error('Failed to initialize render server') } const invokeHeaders: typeof req.headers = { @@ -328,10 +296,9 @@ export async function initialize(opts: { debug('invokeRender', req.url, invokeHeaders) try { - const initResult = await renderWorkers.pages?.initialize( - renderWorkerOpts + const initResult = await renderServer?.instance?.initialize( + renderServerOpts ) - try { await initResult?.requestHandler(req, res) } catch (err) { @@ -473,7 +440,7 @@ export async function initialize(opts: { fsChecker.pageFiles.has(matchedOutput.itemPath)) ) { res.statusCode = 500 - await invokeRender(parsedUrl, 'pages', '/_error', handleIndex, { + await invokeRender(parsedUrl, '/_error', handleIndex, { 'x-invoke-status': '500', 'x-invoke-error': JSON.stringify({ message: `A conflicting public file and page file was found for path ${matchedOutput.itemPath} https://nextjs.org/docs/messages/conflicting-public-file-page`, @@ -500,7 +467,6 @@ export async function initialize(opts: { res.statusCode = 405 return await invokeRender( url.parse('/405', true), - 'pages', '/405', handleIndex, { @@ -566,7 +532,6 @@ export async function initialize(opts: { res.statusCode = err.statusCode return await invokeRender( url.parse(invokePath, true), - 'pages', invokePath, handleIndex, { @@ -583,7 +548,6 @@ export async function initialize(opts: { return await invokeRender( parsedUrl, - matchedOutput.type === 'appFile' ? 'app' : 'pages', parsedUrl.pathname || '/', handleIndex, { @@ -614,7 +578,6 @@ export async function initialize(opts: { if (appNotFound) { return await invokeRender( parsedUrl, - 'app', opts.dev ? '/not-found' : '/_not-found', handleIndex, { @@ -623,7 +586,7 @@ export async function initialize(opts: { ) } - await invokeRender(parsedUrl, 'pages', '/404', handleIndex, { + await invokeRender(parsedUrl, '/404', handleIndex, { 'x-invoke-status': '404', }) } @@ -642,15 +605,9 @@ export async function initialize(opts: { console.error(err) } res.statusCode = Number(invokeStatus) - return await invokeRender( - url.parse(invokePath, true), - 'pages', - invokePath, - 0, - { - 'x-invoke-status': invokeStatus, - } - ) + return await invokeRender(url.parse(invokePath, true), invokePath, 0, { + 'x-invoke-status': invokeStatus, + }) } catch (err2) { console.error(err2) } diff --git a/packages/next/src/server/lib/router-utils/resolve-routes.ts b/packages/next/src/server/lib/router-utils/resolve-routes.ts index 7a89e9371cf75..7f6ce8f045cd1 100644 --- a/packages/next/src/server/lib/router-utils/resolve-routes.ts +++ b/packages/next/src/server/lib/router-utils/resolve-routes.ts @@ -2,7 +2,7 @@ import type { TLSSocket } from 'tls' import type { FsOutput } from './filesystem' import type { IncomingMessage, ServerResponse } from 'http' import type { NextConfigComplete } from '../../config-shared' -import type { RenderWorker, initialize } from '../router-server' +import type { RenderServer, initialize } from '../router-server' import type { PatchMatcher } from '../../../shared/lib/router/utils/path-match' import url from 'url' @@ -44,11 +44,8 @@ export function getResolveRoutes( >, config: NextConfigComplete, opts: Parameters[0], - renderWorkers: { - app?: RenderWorker - pages?: RenderWorker - }, - renderWorkerOpts: Parameters[0], + renderServer: RenderServer, + renderServerOpts: Parameters[0], ensureMiddleware?: () => Promise ) { type Route = { @@ -435,12 +432,12 @@ export function getResolveRoutes( .then(() => true) .catch(() => false))) ) { - const workerResult = await ( - renderWorkers.app || renderWorkers.pages - )?.initialize(renderWorkerOpts) + const serverResult = await renderServer?.initialize( + renderServerOpts + ) - if (!workerResult) { - throw new Error(`Failed to initialize render worker "middleware"`) + if (!serverResult) { + throw new Error(`Failed to initialize render server "middleware"`) } const invokeHeaders: typeof req.headers = { @@ -467,16 +464,12 @@ export function getResolveRoutes( }, }) - const initResult = await renderWorkers.pages?.initialize( - renderWorkerOpts - ) - mockedRes.on('close', () => { readableController.close() }) try { - await initResult?.requestHandler(req, res, parsedUrl) + await serverResult.requestHandler(req, res, parsedUrl) } catch (err: any) { if (!('result' in err) || !('response' in err.result)) { throw err diff --git a/packages/next/src/server/lib/router-utils/setup-dev.ts b/packages/next/src/server/lib/router-utils/setup-dev.ts index a1c526570e0f9..601250c247716 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev.ts @@ -93,7 +93,7 @@ import { srcEmptySsgManifest } from '../../../build/webpack/plugins/build-manife import { PropagateToWorkersField } from './types' import { MiddlewareManifest } from '../../../build/webpack/plugins/middleware-plugin' import { devPageFiles } from '../../../build/webpack/plugins/next-types-plugin/shared' -import type { RenderWorkers } from '../router-server' +import type { LazyRenderServerInstance } from '../router-server' import { pathToRegexp } from 'next/dist/compiled/path-to-regexp' import { HMR_ACTIONS_SENT_TO_BROWSER, @@ -113,7 +113,7 @@ import { normalizeMetadataRoute } from '../../../lib/metadata/get-metadata-route const wsServer = new ws.Server({ noServer: true }) type SetupOpts = { - renderWorkers: RenderWorkers + renderServer: LazyRenderServerInstance dir: string turbo?: boolean appDir?: string @@ -161,9 +161,15 @@ async function startWatcher(opts: SetupOpts) { appDir ) - async function propagateToWorkers(field: PropagateToWorkersField, args: any) { - await opts.renderWorkers.app?.propagateServerField(opts.dir, field, args) - await opts.renderWorkers.pages?.propagateServerField(opts.dir, field, args) + async function propagateServerField( + field: PropagateToWorkersField, + args: any + ) { + await opts.renderServer?.instance?.propagateServerField( + opts.dir, + field, + args + ) } const serverFields: { @@ -606,11 +612,11 @@ async function startWatcher(opts: SetupOpts) { serverFields.middleware = undefined prevMiddleware = false } - await propagateToWorkers( + await propagateServerField( 'actualMiddlewareFile', serverFields.actualMiddlewareFile ) - await propagateToWorkers('middleware', serverFields.middleware) + await propagateServerField('middleware', serverFields.middleware) currentEntriesHandlingResolve!() currentEntriesHandlingResolve = undefined @@ -1480,7 +1486,7 @@ async function startWatcher(opts: SetupOpts) { continue } serverFields.actualMiddlewareFile = rootFile - await propagateToWorkers( + await propagateServerField( 'actualMiddlewareFile', serverFields.actualMiddlewareFile ) @@ -1495,7 +1501,7 @@ async function startWatcher(opts: SetupOpts) { ) { NextBuildContext.hasInstrumentationHook = true serverFields.actualInstrumentationHookFile = rootFile - await propagateToWorkers( + await propagateServerField( 'actualInstrumentationHookFile', serverFields.actualInstrumentationHookFile ) @@ -1608,7 +1614,7 @@ async function startWatcher(opts: SetupOpts) { hotReloader.setHmrServerError(new Error(errorMessage)) } else if (numConflicting === 0) { hotReloader.clearHmrServerError() - await propagateToWorkers('reloadMatchers', undefined) + await propagateServerField('reloadMatchers', undefined) } } @@ -1652,7 +1658,7 @@ async function startWatcher(opts: SetupOpts) { loadEnvConfig(dir, true, Log, true, (envFilePath) => { Log.info(`Reload env: ${envFilePath}`) }) - await propagateToWorkers('loadEnvConfig', [ + await propagateServerField('loadEnvConfig', [ { dev: true, forceReload: true, silent: true }, ]) } @@ -1765,7 +1771,7 @@ async function startWatcher(opts: SetupOpts) { serverFields.appPathRoutes = Object.fromEntries( Object.entries(appPaths).map(([k, v]) => [k, v.sort()]) ) - await propagateToWorkers('appPathRoutes', serverFields.appPathRoutes) + await propagateServerField('appPathRoutes', serverFields.appPathRoutes) // TODO: pass this to fsChecker/next-dev-server? serverFields.middleware = middlewareMatchers @@ -1776,7 +1782,7 @@ async function startWatcher(opts: SetupOpts) { } : undefined - await propagateToWorkers('middleware', serverFields.middleware) + await propagateServerField('middleware', serverFields.middleware) serverFields.hasAppNotFound = hasRootAppNotFound opts.fsChecker.middlewareMatcher = serverFields.middleware?.matchers @@ -1913,7 +1919,7 @@ async function startWatcher(opts: SetupOpts) { } finally { // Reload the matchers. The filesystem would have been written to, // and the matchers need to re-scan it to update the router. - await propagateToWorkers('reloadMatchers', undefined) + await propagateServerField('reloadMatchers', undefined) } }) diff --git a/packages/next/src/server/lib/setup-server-worker.ts b/packages/next/src/server/lib/setup-server-worker.ts deleted file mode 100644 index 169f9863cd45d..0000000000000 --- a/packages/next/src/server/lib/setup-server-worker.ts +++ /dev/null @@ -1,120 +0,0 @@ -import v8 from 'v8' -import http, { IncomingMessage, ServerResponse } from 'http' - -// This is required before other imports to ensure the require hook is setup. -import '../require-hook' -import '../node-polyfill-fetch' - -import { warn } from '../../build/output/log' -import { Duplex } from 'stream' - -process.on('unhandledRejection', (err) => { - console.error(err) -}) - -process.on('uncaughtException', (err) => { - console.error(err) -}) - -export const RESTART_EXIT_CODE = 77 - -const MAXIMUM_HEAP_SIZE_ALLOWED = - (v8.getHeapStatistics().heap_size_limit / 1024 / 1024) * 0.9 - -export type WorkerRequestHandler = ( - req: IncomingMessage, - res: ServerResponse -) => Promise - -export type WorkerUpgradeHandler = ( - req: IncomingMessage, - socket: Duplex, - head: Buffer -) => any - -export async function initializeServerWorker( - requestHandler: WorkerRequestHandler, - upgradeHandler: WorkerUpgradeHandler, - opts: { - dir: string - port: number - dev: boolean - minimalMode?: boolean - hostname?: string - workerType: 'router' | 'render' - isNodeDebugging: boolean - keepAliveTimeout?: number - } -): Promise<{ - port: number - hostname: string - server: http.Server -}> { - const server = http.createServer((req, res) => { - 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(RESTART_EXIT_CODE) - } - }) - }) - - if (opts.keepAliveTimeout) { - server.keepAliveTimeout = opts.keepAliveTimeout - } - - return new Promise(async (resolve, reject) => { - server.on('error', (err: NodeJS.ErrnoException) => { - console.error(`Invariant: failed to start server worker`, err) - process.exit(1) - }) - - if (upgradeHandler) { - server.on('upgrade', (req, socket, upgrade) => { - upgradeHandler(req, socket, upgrade) - }) - } - let hostname = opts.hostname || 'localhost' - - server.on('listening', async () => { - try { - const addr = server.address() - const host = addr - ? typeof addr === 'object' - ? addr.address - : addr - : undefined - const port = addr && typeof addr === 'object' ? addr.port : 0 - - if (!port || !host) { - console.error( - `Invariant failed to detect render worker host/port`, - addr - ) - process.exit(1) - } - - resolve({ - server, - port, - hostname: host, - }) - } catch (err) { - return reject(err) - } - }) - server.listen(0, hostname) - }) -} diff --git a/packages/next/src/server/lib/start-server.ts b/packages/next/src/server/lib/start-server.ts index ba2368703bc43..7cb64a5703343 100644 --- a/packages/next/src/server/lib/start-server.ts +++ b/packages/next/src/server/lib/start-server.ts @@ -4,6 +4,7 @@ import '../require-hook' import type { IncomingMessage, ServerResponse } from 'http' import type { SelfSignedCertificate } from '../../lib/mkcert' +import { type WorkerRequestHandler, type WorkerUpgradeHandler } from './types' import fs from 'fs' import path from 'path' @@ -12,14 +13,9 @@ import https from 'https' import Watchpack from 'watchpack' import * as Log from '../../build/output/log' import setupDebug from 'next/dist/compiled/debug' -import { getDebugPort } from './utils' +import { RESTART_EXIT_CODE, getDebugPort } from './utils' import { formatHostname } from './format-hostname' import { initialize } from './router-server' -import { - RESTART_EXIT_CODE, - type WorkerRequestHandler, - type WorkerUpgradeHandler, -} from './setup-server-worker' import { checkIsNodeDebugging } from './is-node-debugging' import { CONFIG_FILES } from '../../shared/lib/constants' import chalk from '../../lib/chalk' @@ -73,7 +69,6 @@ export async function getRequestHandlers({ dev: isDev, minimalMode, server, - workerType: 'router', isNodeDebugging: isNodeDebugging || false, keepAliveTimeout, experimentalTestProxy, diff --git a/packages/next/src/server/lib/types.ts b/packages/next/src/server/lib/types.ts new file mode 100644 index 0000000000000..58ed0e201e4bf --- /dev/null +++ b/packages/next/src/server/lib/types.ts @@ -0,0 +1,14 @@ +import type { IncomingMessage, ServerResponse } from 'http' + +import { Duplex } from 'stream' + +export type WorkerRequestHandler = ( + req: IncomingMessage, + res: ServerResponse +) => Promise + +export type WorkerUpgradeHandler = ( + req: IncomingMessage, + socket: Duplex, + head: Buffer +) => any diff --git a/packages/next/src/server/lib/utils.ts b/packages/next/src/server/lib/utils.ts index 3615966f857df..1115d92d08bd6 100644 --- a/packages/next/src/server/lib/utils.ts +++ b/packages/next/src/server/lib/utils.ts @@ -40,3 +40,5 @@ export function getPort(args: arg.Result): number { return 3000 } + +export const RESTART_EXIT_CODE = 77 diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 8e9e26985bfba..317d01c834e18 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -149,9 +149,6 @@ export default class NextNodeServer extends BaseServer { private _serverDistDir: string | undefined private imageResponseCache?: ResponseCache protected renderWorkersPromises?: Promise - protected renderWorkerOpts?: Parameters< - typeof import('./lib/render-server').initialize - >[0] protected dynamicRoutes?: { match: import('../shared/lib/router/utils/route-matcher').RouteMatchFn page: string @@ -521,52 +518,43 @@ export default class NextNodeServer extends BaseServer { paramsResult, this.nextConfig, this.renderOpts.dev, - async (newReq, newRes, newParsedUrl) => { + async (newReq, newRes) => { if (newReq.url === req.url) { throw new Error( `Invariant attempted to optimize _next/image itself` ) } - if (this.isRenderWorker) { - const protocol = - this.serverOptions.experimentalHttpsServer || - this.renderWorkerOpts?.experimentalHttpsServer - ? 'https' - : 'http' - - const invokeRes = await invokeRequest( - `${protocol}://${this.fetchHostname || 'localhost'}:${this.port}${ - newReq.url || '' - }`, - { - method: newReq.method || 'GET', - headers: newReq.headers, - signal: signalFromNodeResponse(res.originalResponse), - } - ) - const filteredResHeaders = filterReqHeaders( - toNodeOutgoingHttpHeaders(invokeRes.headers), - ipcForbiddenHeaders - ) - - for (const key of Object.keys(filteredResHeaders)) { - newRes.setHeader(key, filteredResHeaders[key] || '') + const protocol = this.serverOptions.experimentalHttpsServer + ? 'https' + : 'http' + + const invokeRes = await invokeRequest( + `${protocol}://${this.fetchHostname || 'localhost'}:${this.port}${ + newReq.url || '' + }`, + { + method: newReq.method || 'GET', + headers: newReq.headers, + signal: signalFromNodeResponse(res.originalResponse), } - newRes.statusCode = invokeRes.status || 200 + ) + const filteredResHeaders = filterReqHeaders( + toNodeOutgoingHttpHeaders(invokeRes.headers), + ipcForbiddenHeaders + ) - if (invokeRes.body) { - await pipeReadable(invokeRes.body, newRes) - } else { - res.send() - } - return + for (const key of Object.keys(filteredResHeaders)) { + newRes.setHeader(key, filteredResHeaders[key] || '') } - return this.getRequestHandler()( - new NodeNextRequest(newReq), - new NodeNextResponse(newRes), - newParsedUrl - ) + newRes.statusCode = invokeRes.status || 200 + + if (invokeRes.body) { + await pipeReadable(invokeRes.body, newRes) + } else { + res.send() + } + return } ) } @@ -927,13 +915,7 @@ export default class NextNodeServer extends BaseServer { } } catch (err: any) { if (err instanceof NoFallbackError) { - if (this.isRenderWorker) { - throw err - } - - return { - finished: false, - } + throw err } try { @@ -1267,7 +1249,7 @@ export default class NextNodeServer extends BaseServer { const { req, res, query } = ctx const is404 = res.statusCode === 404 - if (is404 && this.hasAppDir && this.isRenderWorker) { + if (is404 && this.hasAppDir) { const notFoundPathname = this.renderOpts.dev ? '/not-found' : '/_not-found' @@ -1583,8 +1565,7 @@ export default class NextNodeServer extends BaseServer { res: BaseNextResponse, parsed: NextUrlWithParsedQuery ) { - const isMiddlewareInvoke = - this.isRenderWorker && req.headers['x-middleware-invoke'] + const isMiddlewareInvoke = req.headers['x-middleware-invoke'] const handleFinished = (finished: boolean = false) => { if (isMiddlewareInvoke && !finished) { @@ -1595,7 +1576,7 @@ export default class NextNodeServer extends BaseServer { return { finished } } - if (this.isRenderWorker && !isMiddlewareInvoke) { + if (!isMiddlewareInvoke) { return { finished: false } } @@ -1702,7 +1683,6 @@ export default class NextNodeServer extends BaseServer { if ( this.renderOpts?.dev || this.serverOptions?.dev || - this.renderWorkerOpts?.dev || process.env.NODE_ENV === 'development' || process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD ) { diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts index 61f096f25ad73..20d514929ead3 100644 --- a/packages/next/src/server/next.ts +++ b/packages/next/src/server/next.ts @@ -4,10 +4,7 @@ import type { UrlWithParsedQuery } from 'url' import type { NextConfigComplete } from './config-shared' import type { IncomingMessage, ServerResponse } from 'http' import type { NextUrlWithParsedQuery } from './request-meta' -import { - WorkerRequestHandler, - WorkerUpgradeHandler, -} from './lib/setup-server-worker' +import type { WorkerRequestHandler, WorkerUpgradeHandler } from './lib/types' import './require-hook' import './node-polyfill-fetch' @@ -173,7 +170,7 @@ export class NextServer { resolve(this.options.dir || '.'), { customConfig: this.options.conf, - silent: !!this.options._renderWorker, + silent: true, } ) )