HTML App Renderer (render-app-to-html.js)
Examine the server rendering orchestrator, parent-child fork tunnels, and late-header HTML script bypass injections.
Key File Location: ./dinou/core/render-app-to-html.js💡 Overview
Server-Side Rendering (SSR) needs to compile components into HTML and stream it to the client as fast as possible. In Dinou, render-app-to-html.js manages this process. It forks child worker processes to render pages, handles static payloads from disk, and maps inter-process (IPC) messages.
📊 HTML Renderer Sequence
The flowchart below traces parent-to-child data streams and Express process events:
⚡ Child IPC & Streaming Scripts
Streaming HTML chunks to the browser before completing the render cycle introduces a common framework problem: **late headers**. If a component deep inside the tree sets a cookie or triggers a redirect *after* Express has already sent the initial HTTP status and headers, normal HTTP header modification throws errors.
Dinou solves this using **HTML Script-Injection Bypasses**:
- Headers Not Sent: Executes standard Express cookie sets and redirect calls on the main thread.
- Headers Sent: Injects inline script tags directly into the active HTML stream:<script>document.cookie = "cookie_name=value; path=/";</script> <script>window.location.href = "/login";</script>These scripts run instantly on the browser upon parsing, simulating header redirections dynamically.
⚙️ Complete Code Walkthrough
Below is the full code of render-app-to-html.js:
const path = require("path");
const { fork } = require("child_process");
const url = require("url");
const fs = require("fs");
const getJSX = require("./get-jsx.js");
const { requestStorage } = require("./request-context.js");
const isDevelopment = process.env.NODE_ENV !== "production";
const isWebpack = process.env.DINOU_BUILD_TOOL === "webpack";
const { renderToPipeableStream } = isWebpack
? require("react-server-dom-webpack/server")
: require("@roggc/react-server-dom-esm/server");
const manifestPath = path.resolve(
process.cwd(),
isWebpack
? (isDevelopment ? "public/react-client-manifest.json" : "dist3/react-client-manifest.json")
: "react_client_manifest/react-client-manifest.json"
);
let cachedManifest = null;
function getManifest() {
if (!isDevelopment && cachedManifest) return cachedManifest;
try {
const content = fs.readFileSync(manifestPath, "utf8");
const parsed = JSON.parse(content);
if (parsed && Object.keys(parsed).length > 0) {
cachedManifest = parsed;
}
return cachedManifest || parsed;
} catch (e) {
return {};
}
}
const registerLoaderPath = url.pathToFileURL(path.join(__dirname, "register-loader.mjs")).href;
const renderHtmlPath = path.resolve(__dirname, "render-html.js");
const childExecArgv = [].concat(`--import=${registerLoaderPath}`);
const { resolveRelativeUrl } = require("./url-resolver");
function createParentResponseWrapper(reqPath, res, child) {
let hasRedirected = false;
const safeRedirect = (targetUrl) => {
if (hasRedirected) return;
hasRedirected = true;
const resolvedUrl = resolveRelativeUrl(targetUrl, reqPath);
let finalUrl = resolvedUrl.startsWith("/") && !resolvedUrl.startsWith("//") ? resolvedUrl : "/";
if (res.headersSent) {
res.write(`<script>window.location.href = ${JSON.stringify(finalUrl)};</script>`);
res.end();
child.stdout.unpipe(res);
child.kill();
} else {
res.redirect(302, finalUrl);
child.stdout.unpipe(res);
child.kill();
}
};
return {
setHeader: (name, value) => {
if (!res.headersSent) res.setHeader(name, value);
},
cookie: (name, value, options) => {
if (res.headersSent) {
if (options && options.httpOnly) return; // Cannot write HttpOnly in browser JS
let cookieStr = `${name}=${encodeURIComponent(value)}`;
if (options) {
if (options.path) cookieStr += `; path=${options.path}`;
if (options.maxAge) cookieStr += `; max-age=${options.maxAge}`;
if (options.secure) cookieStr += `; secure`;
}
res.write(`<script>document.cookie = ${JSON.stringify(cookieStr)};</script>`);
} else {
res.cookie(name, value, options);
}
},
clearCookie: (name, options) => {
if (res.headersSent) {
let cookieStr = `${name}=; Max-Age=0; path=${options?.path || "/"};`;
res.write(`<script>document.cookie = ${JSON.stringify(cookieStr)};</script>`);
} else {
res.clearCookie(name, options);
}
},
redirect: (arg1, arg2) => safeRedirect(arg2 || arg1),
status: (code) => {
if (!res.headersSent) res.status(code);
},
};
}
function renderAppToHtml(reqPath, paramsString, contextForChild, res, capturedStatus = null, isDynamic = false) {
// 1. Fork render-html.js child worker process with ESM custom loader imports
const child = fork(
renderHtmlPath,
[reqPath, paramsString, contextForChild ? JSON.stringify(contextForChild) : "{}", isDynamic ? "true" : "false"],
{
execArgv: childExecArgv,
stdio: ["ignore", "pipe", "pipe", "ipc", "pipe"], // stdio[4] is the RSC stream pipe
}
);
const query = JSON.parse(paramsString || "{}");
const rscPath = path.resolve(process.cwd(), "dist2", reqPath.replace(/^//, ""), "rsc.rsc");
const hasStaticRsc = !isDynamic && fs.existsSync(rscPath);
// 2. Fetch or Compile RSC elements and write them to child process stdio[4] channel
if (hasStaticRsc) {
child.stdio[4].write(fs.readFileSync(rscPath));
child.stdio[4].end();
} else {
const parentRes = createParentResponseWrapper(reqPath, res, child);
const context = { req: contextForChild ? contextForChild.req : {}, res: parentRes };
requestStorage.run(context, () => {
getJSX(reqPath, query, {}, isDevelopment)
.then((jsx) => {
const manifest = getManifest();
const { pipe } = isWebpack
? renderToPipeableStream(jsx, manifest)
: renderToPipeableStream(jsx, url.pathToFileURL(process.cwd()).href + "/");
pipe(child.stdio[4]);
})
.catch(() => child.stdio[4].destroy());
});
}
// 3. Listen to context proxy updates from child process
child.on("message", (message) => {
if (message && message.type === "DINOU_CONTEXT_COMMAND") {
const { command, args } = message;
if (res.headersSent) {
// Late changes: inject inline JavaScript hacks in HTML output
if (command === "redirect") {
const rawUrl = args.length === 1 ? args[0] : args[1];
const resolved = resolveRelativeUrl(rawUrl, reqPath);
res.write(`<script>window.location.href = ${JSON.stringify(resolved)};</script>`);
res.end();
child.stdout.unpipe(res);
child.kill();
} else if (command === "cookie") {
const [name, value, options] = args;
if (options && options.httpOnly) return;
let cookieStr = `${name}=${encodeURIComponent(value)}`;
if (options?.path) cookieStr += `; path=${options.path}`;
res.write(`<script>document.cookie = ${JSON.stringify(cookieStr)};</script>`);
}
} else {
// Normal Express header assignment
if (typeof res[command] === "function") {
res[command].apply(res, args);
}
}
}
});
return child.stdout;
}
module.exports = renderAppToHtml;