UNPKG

next

Version:

The React Framework

434 lines (433 loc) • 18.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { checkIsNodeDebugging: null, createRouterWorker: null, startServer: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { checkIsNodeDebugging: function() { return checkIsNodeDebugging; }, createRouterWorker: function() { return createRouterWorker; }, startServer: function() { return startServer; } }); require("../node-polyfill-fetch"); const _http = /*#__PURE__*/ _interop_require_default(require("http")); const _net = require("net"); const _env = require("@next/env"); const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../build/output/log")); const _debug = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/debug")); const _utils = require("../web/utils"); const _bodystreams = require("../body-streams"); const _utils1 = require("./server-ipc/utils"); const _compression = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/compression")); const _utils2 = require("../../shared/lib/utils"); const _invokerequest = require("./server-ipc/invoke-request"); const _pipereadable = require("../pipe-readable"); const _utils3 = require("./utils"); const _nextrequest = require("../web/spec-extension/adapters/next-request"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const debug = (0, _debug.default)("next:start-server"); const checkIsNodeDebugging = ()=>{ var _process_env_NODE_OPTIONS, _process_env_NODE_OPTIONS1; let isNodeDebugging = !!(process.execArgv.some((localArg)=>localArg.startsWith("--inspect")) || ((_process_env_NODE_OPTIONS = process.env.NODE_OPTIONS) == null ? void 0 : _process_env_NODE_OPTIONS.match == null ? void 0 : _process_env_NODE_OPTIONS.match(/--inspect(=\S+)?( |$)/))); if (process.execArgv.some((localArg)=>localArg.startsWith("--inspect-brk")) || ((_process_env_NODE_OPTIONS1 = process.env.NODE_OPTIONS) == null ? void 0 : _process_env_NODE_OPTIONS1.match == null ? void 0 : _process_env_NODE_OPTIONS1.match(/--inspect-brk(=\S+)?( |$)/))) { isNodeDebugging = "brk"; } return isNodeDebugging; }; const createRouterWorker = async (routerServerPath, isNodeDebugging, jestWorkerPath = require.resolve("next/dist/compiled/jest-worker"))=>{ const { Worker } = require(jestWorkerPath); return new Worker(routerServerPath, { numWorkers: 1, // TODO: do we want to allow more than 8 OOM restarts? maxRetries: 8, forkOptions: { execArgv: await (0, _utils3.genRouterWorkerExecArgv)(isNodeDebugging === undefined ? false : isNodeDebugging), env: { FORCE_COLOR: "1", ..._env.initialEnv || process.env, NODE_OPTIONS: (0, _utils3.getNodeOptionsWithoutInspect)(), ...process.env.NEXT_CPU_PROF ? { __NEXT_PRIVATE_CPU_PROFILE: `CPU.router` } : {}, WATCHPACK_WATCHER_LIMIT: "20", EXPERIMENTAL_TURBOPACK: process.env.EXPERIMENTAL_TURBOPACK } }, exposedMethods: [ "initialize" ] }); }; async function startServer({ dir , nextConfig , prevDir , port , isDev , hostname , useWorkers , minimalMode , allowRetry , keepAliveTimeout , onStdout , onStderr , logReady =true }) { const sockets = new Set(); let worker; let routerPort; let handlersReady = ()=>{}; let handlersError = ()=>{}; let handlersPromise = new Promise((resolve, reject)=>{ handlersReady = resolve; handlersError = reject; }); let requestHandler = async (_req, _res)=>{ if (handlersPromise) { await handlersPromise; return requestHandler(_req, _res); } throw new Error("Invariant request handler was not setup"); }; let upgradeHandler = async (_req, _socket, _head)=>{ if (handlersPromise) { await handlersPromise; return upgradeHandler(_req, _socket, _head); } throw new Error("Invariant upgrade handler was not setup"); }; // setup server listener as fast as possible const server = _http.default.createServer(async (req, res)=>{ try { if (handlersPromise) { await handlersPromise; handlersPromise = undefined; } sockets.add(res); res.on("close", ()=>sockets.delete(res)); await requestHandler(req, res); } catch (err) { res.statusCode = 500; res.end("Internal Server Error"); _log.error(`Failed to handle request for ${req.url}`); console.error(err); } }); if (keepAliveTimeout) { server.keepAliveTimeout = keepAliveTimeout; } server.on("upgrade", async (req, socket, head)=>{ try { sockets.add(socket); socket.on("close", ()=>sockets.delete(socket)); await upgradeHandler(req, socket, head); } catch (err) { socket.destroy(); _log.error(`Failed to handle request for ${req.url}`); console.error(err); } }); let portRetryCount = 0; server.on("error", (err)=>{ if (allowRetry && port && isDev && err.code === "EADDRINUSE" && portRetryCount < 10) { _log.warn(`Port ${port} is in use, trying ${port + 1} instead.`); port += 1; portRetryCount += 1; server.listen(port, hostname); } else { _log.error(`Failed to start server`); console.error(err); process.exit(1); } }); let targetHost = hostname; const isNodeDebugging = checkIsNodeDebugging(); await new Promise((resolve)=>{ server.on("listening", ()=>{ const addr = server.address(); port = typeof addr === "object" ? (addr == null ? void 0 : addr.port) || port : port; let host = !hostname || hostname === "0.0.0.0" ? "localhost" : hostname; let normalizedHostname = hostname || "0.0.0.0"; if ((0, _net.isIPv6)(hostname)) { host = host === "::" ? "[::1]" : `[${host}]`; normalizedHostname = `[${hostname}]`; } targetHost = host; const appUrl = `http://${host}:${port}`; if (isNodeDebugging) { const debugPort = (0, _utils3.getDebugPort)(); _log.info(`the --inspect${isNodeDebugging === "brk" ? "-brk" : ""} option was detected, the Next.js proxy server should be inspected at port ${debugPort}.`); } if (logReady) { _log.ready(`started server on ${normalizedHostname}${(port + "").startsWith(":") ? "" : ":"}${port}, url: ${appUrl}`); // expose the main port to render workers process.env.PORT = port + ""; } resolve(); }); server.listen(port, hostname === "localhost" ? "0.0.0.0" : hostname); }); try { if (useWorkers) { var _routerWorker__workerPool; const httpProxy = require("next/dist/compiled/http-proxy"); let routerServerPath = require.resolve("./router-server"); let jestWorkerPath = require.resolve("next/dist/compiled/jest-worker"); if (prevDir) { jestWorkerPath = jestWorkerPath.replace(prevDir, dir); routerServerPath = routerServerPath.replace(prevDir, dir); } const routerWorker = await createRouterWorker(routerServerPath, isNodeDebugging, jestWorkerPath); const cleanup = ()=>{ var _routerWorker__workerPool; debug("start-server process cleanup"); for (const curWorker of ((_routerWorker__workerPool = routerWorker._workerPool) == null ? void 0 : _routerWorker__workerPool._workers) || []){ var _curWorker__child; (_curWorker__child = curWorker._child) == null ? void 0 : _curWorker__child.kill("SIGINT"); } process.exit(0); }; process.on("exit", cleanup); process.on("SIGINT", cleanup); process.on("SIGTERM", cleanup); process.on("uncaughtException", cleanup); process.on("unhandledRejection", cleanup); let didInitialize = false; for (const _worker of ((_routerWorker__workerPool = routerWorker._workerPool) == null ? void 0 : _routerWorker__workerPool._workers) || []){ // eslint-disable-next-line no-loop-func _worker._child.on("exit", (code, signal)=>{ // catch failed initializing without retry if ((code || signal) && !didInitialize) { routerWorker == null ? void 0 : routerWorker.end(); process.exit(1); } }); } const workerStdout = routerWorker.getStdout(); const workerStderr = routerWorker.getStderr(); workerStdout.on("data", (data)=>{ if (typeof onStdout === "function") { onStdout(data); } else { process.stdout.write(data); } }); workerStderr.on("data", (data)=>{ if (typeof onStderr === "function") { onStderr(data); } else { process.stderr.write(data); } }); const initializeResult = await routerWorker.initialize({ dir, port, hostname, dev: !!isDev, minimalMode, workerType: "router", isNodeDebugging: !!isNodeDebugging, keepAliveTimeout }); routerPort = initializeResult.port; didInitialize = true; let compress; if ((nextConfig == null ? void 0 : nextConfig.compress) !== false) { compress = (0, _compression.default)(); } const getProxyServer = (pathname)=>{ const targetUrl = `http://${targetHost === "localhost" ? "127.0.0.1" : targetHost}:${routerPort}${pathname}`; const proxyServer = httpProxy.createProxy({ target: targetUrl, changeOrigin: false, ignorePath: true, xfwd: true, ws: true, followRedirects: false }); // add error listener to prevent uncaught exceptions proxyServer.on("error", (_err)=>{ // TODO?: enable verbose error logs with --debug flag? }); proxyServer.on("proxyRes", (proxyRes, innerReq, innerRes)=>{ const cleanupProxy = (err)=>{ // cleanup event listeners to allow clean garbage collection proxyRes.removeListener("error", cleanupProxy); proxyRes.removeListener("close", cleanupProxy); innerRes.removeListener("error", cleanupProxy); innerRes.removeListener("close", cleanupProxy); // destroy all source streams to propagate the caught event backward innerReq.destroy(err); proxyRes.destroy(err); }; proxyRes.once("error", cleanupProxy); proxyRes.once("close", cleanupProxy); innerRes.once("error", cleanupProxy); innerRes.once("close", cleanupProxy); }); return proxyServer; }; // proxy to router worker requestHandler = 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 == null ? void 0 : urlNoQuery.match(/(\\|\/\/)/)) { const cleanUrl = (0, _utils2.normalizeRepeatedSlashes)(req.url); res.statusCode = 308; res.setHeader("Location", cleanUrl); res.end(cleanUrl); return; } if (typeof compress === "function") { // @ts-expect-error not express req/res compress(req, res, ()=>{}); } const targetUrl = `http://${targetHost === "localhost" ? "127.0.0.1" : targetHost}:${routerPort}${req.url || "/"}`; let invokeRes; try { invokeRes = await (0, _invokerequest.invokeRequest)(targetUrl, { headers: req.headers, method: req.method, signal: (0, _nextrequest.signalFromNodeResponse)(res) }, (0, _bodystreams.getCloneableBody)(req).cloneBodyStream()); } catch (e) { // If the client aborts before we can receive a response object (when // the headers are flushed), then we can early exit without further // processing. if ((0, _pipereadable.isAbortError)(e)) { return; } throw e; } res.statusCode = invokeRes.status; res.statusMessage = invokeRes.statusText; for (const [key, value] of Object.entries((0, _utils1.filterReqHeaders)((0, _utils.toNodeOutgoingHttpHeaders)(invokeRes.headers), _utils1.ipcForbiddenHeaders))){ if (value !== undefined) { if (key === "set-cookie") { const curValue = res.getHeader(key); const newValue = []; for (const cookie of Array.isArray(curValue) ? curValue : (0, _utils.splitCookiesString)(curValue || "")){ newValue.push(cookie); } for (const val of Array.isArray(value) ? value : value ? [ value ] : []){ newValue.push(val); } res.setHeader(key, newValue); } else { res.setHeader(key, value); } } } if (invokeRes.body) { await (0, _pipereadable.pipeReadable)(invokeRes.body, res); } else { res.end(); } }; upgradeHandler = async (req, socket, head)=>{ // add error listeners to prevent uncaught exceptions on socket errors req.on("error", (_err)=>{ // TODO: log socket errors? // console.log(_err) }); socket.on("error", (_err)=>{ // TODO: log socket errors? // console.log(_err) }); const proxyServer = getProxyServer(req.url || "/"); proxyServer.on("proxyReqWs", (proxyReq)=>{ socket.on("close", ()=>proxyReq.destroy()); }); proxyServer.ws(req, socket, head); }; handlersReady(); } else { // when not using a worker start next in main process const next = require("../next"); const addr = server.address(); const app = next({ dir, hostname, dev: isDev, isNodeDebugging, httpServer: server, customServer: false, port: addr && typeof addr === "object" ? addr.port : port }); // handle in process requestHandler = app.getRequestHandler(); upgradeHandler = app.getUpgradeHandler(); await app.prepare(); handlersReady(); } } catch (err) { // fatal error if we can't setup handlersError(); console.error(err); process.exit(1); } // return teardown function for destroying the server async function teardown() { server.close(); sockets.forEach((socket)=>{ sockets.delete(socket); socket.destroy(); }); if (worker) { await worker.end(); } } teardown.port = routerPort; return teardown; } //# sourceMappingURL=start-server.js.map