Server Functions Connection (server-function-proxy.js)
Explore how Dinou handles client-side Server Function invocations, request serialization, safe redirection execution, and live streaming command parsing.
Key File Location: ./dinou/core/server-function-proxy.js💡 Overview
Server Functions (functions tagged with the "use server" directive) reside on the backend. When client components invoke these functions, they need a proxy layer to translate parameters into HTTP network calls and parse incoming streaming responses. The server-function-proxy.js module manages this bridge.
📊 Physical File Structure
The file layout splits request serialization, redirect filtering, and stream line parsers:
⚛️ 1. Proxy Factory & Request Formatting
The createServerFunctionProxy(id) helper instantiates a JavaScript Proxy wrap. When invoked, it intercepts the argument list and normalizes the request structure:
- Form Data check: If the first argument is a
FormDatainstance (from a native form submit), the proxy appends__dinou_func_id(the Function ID) and serializes extra args into__dinou_args, submitting it as a multipart request body. - JSON check: For direct JS calls, it sets headers to
application/jsonand POSTs serialized { id, args }.
export function createServerFunctionProxy(id) {
return new Proxy(() => {}, {
apply: async (_target, _thisArg, args) => {
let body;
const headers = {
"x-server-function-call": "1",
};
if (args[0] instanceof FormData) {
const formData = args[0];
formData.append("__dinou_func_id", id);
if (args.length > 1) {
formData.append("__dinou_args", JSON.stringify(args.slice(1)));
}
body = formData;
} else {
headers["Content-Type"] = "application/json";
body = JSON.stringify({ id, args });
}
const res = await fetch("/____server_function____", {
method: "POST",
headers,
body,
});
if (!res.ok) throw new Error("Server function failed");
// ... process response ...
}
});
}🛡️ 2. Redirections & Security Checks
Server Functions might trigger redirect operations on completion. The proxy handles redirections securely:
- Open Redirect prevention: The helper
isSafeRedirect(url)asserts that redirection targets start with a single slash (/) and not a double slash (//). This prevents malicious actors from hijacking redirects to arbitrary external domains. - SPA transitions: If the target path is internal, it calls
window.__DINOU_ROUTER_NAVIGATE__to trigger a smooth SPA path change instead of reloading the page.
function isSafeRedirect(url) {
return typeof url === "string" && url.startsWith("/") && !url.startsWith("//");
}
function executeRedirect(url) {
const safeUrl = isSafeRedirect(url) ? url : "/";
const isInternal = safeUrl.startsWith("/") && !safeUrl.startsWith("//");
if (isInternal && typeof window !== "undefined" && window.__DINOU_ROUTER_NAVIGATE__) {
window.__DINOU_ROUTER_NAVIGATE__(safeUrl);
} else if (typeof window !== "undefined") {
window.location.href = safeUrl;
}
}⚙️ 3. RSC & Hybrid Stream Processing
If the server Function returns React node updates, the response carries a text/x-component header representing the RSC Flight stream. The proxy reads this incrementally:
- Line-by-line parsing: The stream chunk reader buffers incoming binaries, splitting them into text lines.
- Streaming Commands (
D:prefix): If a line begins withD:(e.g.D:{"type":"cookie", "cookie":"..."}), the proxy intercepts it immediately. It parses the JSON command and executes the metadata operation on the client:- Writes cookies to the document context (
document.cookie = payload.cookie). - Invokes safe SPA redirects (
executeRedirect(payload.url)).
- Writes cookies to the document context (
- Flight Stream piping: Non-command lines are forwarded to a custom
ReadableStream, which is then parsed by React'screateFromFetch()to stream UI changes directly.
// Inside response handling, if contentType is "text/x-component" (RSC Flight stream):
const reader = res.body.getReader();
const decoder = new TextDecoder();
const encoder = new TextEncoder();
const readableStream = new ReadableStream({
async start(controller) {
let buffer = ""; // Persistent state across chunk reads
let isRedirecting = false;
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
// Process final trailing data in buffer...
break;
}
buffer += decoder.decode(value, { stream: true });
// Process complete lines separated by newlines
const lastNewlineIndex = buffer.lastIndexOf("\n");
if (lastNewlineIndex !== -1) {
const completeChunk = buffer.slice(0, lastNewlineIndex + 1);
buffer = buffer.slice(lastNewlineIndex + 1);
const lines = completeChunk.split("\n");
let cleanChunk = "";
for (const line of lines) {
if (line.startsWith("D:")) {
// Intercept Dinou streaming command packet
const payload = JSON.parse(line.slice(2));
if (payload.type === "redirect") {
isRedirecting = true;
executeRedirect(payload.url);
} else if (payload.type === "cookie") {
document.cookie = payload.cookie; // Write cookie on client JIT
}
} else {
cleanChunk += line + "\n";
}
}
if (cleanChunk) controller.enqueue(encoder.encode(cleanChunk));
}
}
controller.close();
} catch (err) {
controller.error(err);
}
},
});
return createFromFetch(Promise.resolve(new Response(readableStream)));📦 Webpack Variant (server-function-proxy-webpack.js)
When Webpack is active, the bundler maps imports to server-function-proxy-webpack.js.
The Difference: The Webpack variant imports deserialization functions from the Webpack package:
import { createFromFetch } from "react-server-dom-webpack/client";It executes the exact same request format checks, safe redirect gates, and hybrid stream line interceptors.