Skip to content

Instantly share code, notes, and snippets.

@jamesdaniels
Created October 29, 2024 15:18
Show Gist options
  • Save jamesdaniels/1c631886fb124b106c8ac48047b33f9b to your computer and use it in GitHub Desktop.
Save jamesdaniels/1c631886fb124b106c8ac48047b33f9b to your computer and use it in GitHub Desktop.
import { createServer, IncomingMessage, ServerResponse } from "node:http";
import { Socket } from "node:net";
import NextServer from "next/dist/server/next-server.js";
import next from "next";
import RequiredServerFiles from "./.next/required-server-files.json" assert { type: "json" };
process.setMaxListeners(1_000);
const hostname = process.env.HOSTNAME || "127.0.0.1";
const port = parseInt(process.env.PORT, 10) || 3000;
const keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10) || 10;
const nextConfig = RequiredServerFiles.config;
nextConfig.compress = false; // turn off compression for this demo
process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig);
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = 'experimental';
const nextMinimalServer = new NextServer.default({
conf: nextConfig,
customServer: false,
dev: false,
dir: process.cwd(),
hostname: "0.0.0.0",
port,
minimalMode: true,
keepAliveTimeout,
});
const nextFullServer = next({
conf: nextConfig,
dev: false,
dir: process.cwd(),
hostname: "0.0.0.0",
port,
keepAliveTimeout,
});
async function write(stream, ...args) {
const ok = stream.write(...args);
if (!ok) {
await new Promise((resolve) => stream.once("drain", resolve));
}
}
async function requestHandle(req, res) {
const { pathname } = new URL(`http://YOLO${req.url}`);
await nextMinimalServer.prepare();
const minimalMatch = await nextMinimalServer.matchers.match(pathname, {});
if (!minimalMatch) {
await nextFullServer.prepare();
return nextFullServer.getRequestHandler()(req, res);
}
const prerenderedRoute = nextMinimalServer.getPrerenderManifest().routes[pathname];
if (!prerenderedRoute) return nextMinimalServer.getRequestHandler()(req, res);
const kind = minimalMatch?.definition?.kind;
const isRoutePPREnabled = prerenderedRoute.experimentalPPR;
const incrementalCache = await nextMinimalServer.getIncrementalCache({ requestHeaders: req.headers, requestProtcol: req.protocol });
const cacheEntry = await incrementalCache.get(pathname, { kind, isRoutePPREnabled });
if (!cacheEntry) return nextMinimalServer.getRequestHandler()(req, res);
res.writeHead(res.status || 200, res.statusText, cacheEntry.value.headers);
await write(res, req.headers['rsc'] ? cacheEntry.value.rscData : cacheEntry.value.html || cacheEntry.value.body);
const postponed = !req.headers['next-router-prefetch'] && cacheEntry.value.postponed;
if (!postponed) return res.end();
// Trigger the postponed response, this has to be to next minimal
// it's a POST to the magic /_next/postponed/resume path
const resumeReq = new IncomingMessage(new Socket());
resumeReq.url = `/_next/postponed/resume${req.url}`;
resumeReq.method = "POST";
resumeReq.protocol = "http";
resumeReq.httpVersion = "1.1";
resumeReq.httpVersionMajor = 1;
resumeReq.httpVersionMinor = 1;
Object.assign(resumeReq.headers, req.headers);
resumeReq.headers["x-matched-path"] = `/_next/postponed/resume${pathname}`;
resumeReq.push(postponed);
resumeReq.push(null);
const resumeRes = new ServerResponse(resumeReq);
// it's only the body we care about, don't get clever with pipes :P
res.on("drain", () => resumeRes.emit("drain"));
resumeRes.write = res.write.bind(res);
resumeRes.end = res.end.bind(res);
nextMinimalServer.getRequestHandler()(resumeReq, resumeRes);
};
createServer({ keepAliveTimeout }, requestHandle).listen(port, hostname, () => {
console.log(`shield listening on http://${hostname}:${port}.`);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment