next
Version:
The React Framework
434 lines (433 loc) • 18.8 kB
JavaScript
;
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