This commit is contained in:
parent
0e80877dca
commit
2b7e37e3b9
3 changed files with 311 additions and 46 deletions
|
|
@ -1125,9 +1125,20 @@ void Crafter::EnableWasiBrowserRuntime(Configuration& cfg) {
|
|||
};
|
||||
walk(&cfg);
|
||||
|
||||
// Per-build cache-busting token. Stamped onto every script src + the
|
||||
// wasm URL so a regular browser reload sees fresh files even though
|
||||
// the dev server (python -m http.server) sends no Cache-Control
|
||||
// headers. Using ms-since-epoch is enough to be unique per build
|
||||
// without invoking any version-control machinery.
|
||||
const std::string buildId = std::to_string(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count());
|
||||
|
||||
std::string envScriptTags;
|
||||
for (const std::string& name : envScripts) {
|
||||
envScriptTags += std::format(" <script src=\"{}\" type=\"module\"></script>\n", name);
|
||||
envScriptTags += std::format(
|
||||
" <script src=\"{}?v={}\" type=\"module\"></script>\n",
|
||||
name, buildId);
|
||||
}
|
||||
|
||||
// Walk the dep graph again for non-JS assets — these get pre-loaded by
|
||||
|
|
@ -1215,6 +1226,7 @@ void Crafter::EnableWasiBrowserRuntime(Configuration& cfg) {
|
|||
std::string html = buf.str();
|
||||
html = std::regex_replace(html, std::regex(R"(\{\{WASM\}\})"), cfg.outputName + ".wasm");
|
||||
html = std::regex_replace(html, std::regex(R"(\{\{ENV_SCRIPTS\}\})"), envScriptTags);
|
||||
html = std::regex_replace(html, std::regex(R"(\{\{BUILDID\}\})"), buildId);
|
||||
std::ofstream out(htmlPath);
|
||||
out << html;
|
||||
out.close();
|
||||
|
|
@ -1467,20 +1479,81 @@ int Crafter::Run(int argc, char** argv) {
|
|||
return basePort;
|
||||
};
|
||||
const int port = findFreePort(8080, 16);
|
||||
// Cross-origin isolation: the browser coarsens
|
||||
// performance.now() (and chrono::steady_clock under wasi)
|
||||
// to ~0.1ms unless the response carries COOP/COEP, which
|
||||
// floors the --timing overlay's sub-ms phase counters to
|
||||
// zero. Emitting same-origin / require-corp drops the
|
||||
// resolution to ~5µs and also unlocks SharedArrayBuffer
|
||||
// for any future threading work. CORP keeps the local
|
||||
// asset fetches passing under require-corp.
|
||||
auto writeFile = [](const fs::path& p, std::string_view contents) {
|
||||
std::ofstream f(p, std::ios::binary | std::ios::trunc);
|
||||
f.write(contents.data(), static_cast<std::streamsize>(contents.size()));
|
||||
};
|
||||
std::string cmd;
|
||||
std::string_view picked;
|
||||
bool isolated = false;
|
||||
if (have("caddy")) {
|
||||
picked = "caddy";
|
||||
cmd = std::format("caddy file-server --listen :{} --root {}", port, absDir.string());
|
||||
} else if (have("python3")) {
|
||||
picked = "python3";
|
||||
cmd = std::format("python3 -m http.server --directory {} {}", absDir.string(), port);
|
||||
} else if (have("python")) {
|
||||
picked = "python";
|
||||
cmd = std::format("python -m http.server --directory {} {}", absDir.string(), port);
|
||||
isolated = true;
|
||||
// caddy file-server has no --header flag; write a
|
||||
// Caddyfile next to the build output. Adapter is
|
||||
// inferred from the .caddyfile extension when run
|
||||
// via `caddy run`.
|
||||
fs::path cf = absDir / "Caddyfile.coi";
|
||||
writeFile(cf, std::format(
|
||||
":{} {{\n"
|
||||
" root * {}\n"
|
||||
" header Cross-Origin-Opener-Policy \"same-origin\"\n"
|
||||
" header Cross-Origin-Embedder-Policy \"require-corp\"\n"
|
||||
" header Cross-Origin-Resource-Policy \"same-origin\"\n"
|
||||
" header Cache-Control \"no-store\"\n"
|
||||
" file_server\n"
|
||||
"}}\n",
|
||||
port, absDir.string()));
|
||||
cmd = std::format("caddy run --config {} --adapter caddyfile",
|
||||
cf.string());
|
||||
} else if (have("python3") || have("python")) {
|
||||
std::string_view py = have("python3") ? "python3" : "python";
|
||||
picked = py;
|
||||
isolated = true;
|
||||
// Inline a tiny SimpleHTTPRequestHandler subclass
|
||||
// that appends the COI headers on every response.
|
||||
// Lives in absDir so the user can re-run it manually
|
||||
// (`python3 absDir/.serve-coi.py 8080`).
|
||||
fs::path sp = absDir / ".serve-coi.py";
|
||||
writeFile(sp,
|
||||
"import http.server, socketserver, sys, os\n"
|
||||
"class H(http.server.SimpleHTTPRequestHandler):\n"
|
||||
" def end_headers(self):\n"
|
||||
" self.send_header('Cross-Origin-Opener-Policy', 'same-origin')\n"
|
||||
" self.send_header('Cross-Origin-Embedder-Policy', 'require-corp')\n"
|
||||
" self.send_header('Cross-Origin-Resource-Policy', 'same-origin')\n"
|
||||
" self.send_header('Cache-Control', 'no-store')\n"
|
||||
" super().end_headers()\n"
|
||||
"socketserver.TCPServer.allow_reuse_address = True\n"
|
||||
"port = int(sys.argv[1]) if len(sys.argv) > 1 else 8080\n"
|
||||
"os.chdir(os.path.dirname(os.path.abspath(__file__)))\n"
|
||||
"with socketserver.TCPServer(('', port), H) as s:\n"
|
||||
" s.serve_forever()\n");
|
||||
cmd = std::format("{} {} {}", py, sp.string(), port);
|
||||
} else if (have("php")) {
|
||||
picked = "php";
|
||||
cmd = std::format("php -S 0.0.0.0:{} -t {}", port, absDir.string());
|
||||
// php -S supports a router script — emit one that
|
||||
// sets COI headers then delegates to the built-in
|
||||
// static-file handler by returning false.
|
||||
fs::path rp = absDir / ".serve-coi.php";
|
||||
writeFile(rp,
|
||||
"<?php\n"
|
||||
"header('Cross-Origin-Opener-Policy: same-origin');\n"
|
||||
"header('Cross-Origin-Embedder-Policy: require-corp');\n"
|
||||
"header('Cross-Origin-Resource-Policy: same-origin');\n"
|
||||
"header('Cache-Control: no-store');\n"
|
||||
"return false;\n");
|
||||
isolated = true;
|
||||
cmd = std::format("php -S 0.0.0.0:{} -t {} {}",
|
||||
port, absDir.string(), rp.string());
|
||||
} else if (have("ruby")) {
|
||||
picked = "ruby";
|
||||
cmd = std::format("ruby -run -e httpd {} -p{}", absDir.string(), port);
|
||||
|
|
@ -1496,7 +1569,16 @@ int Crafter::Run(int argc, char** argv) {
|
|||
"caddy, python3, python, php, ruby, busybox, npx (Node.js).");
|
||||
return 1;
|
||||
}
|
||||
std::println("serving {} via {} at http://localhost:{}/", absDir.string(), picked, port);
|
||||
if (isolated) {
|
||||
std::println("serving {} via {} at http://localhost:{}/ (cross-origin isolated)",
|
||||
absDir.string(), picked, port);
|
||||
} else {
|
||||
std::println("serving {} via {} at http://localhost:{}/", absDir.string(), picked, port);
|
||||
std::println(std::cerr,
|
||||
"warning: {} does not emit COOP/COEP — performance.now() will be coarse "
|
||||
"(~0.1ms). Install caddy, python3, or php for cross-origin isolation.",
|
||||
picked);
|
||||
}
|
||||
return std::system(cmd.c_str()) == 0 ? 0 : 1;
|
||||
}
|
||||
// wasi-cli wasm — needs a standalone runtime.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue