- cloudflares fork of V8
- config is capnproto (binary, rpc, text)
- extra changes for untrusted code
- compilation with v8 into native executable via cache
- regular restarts and load moving to protect tenants
- high cpu workers process/vm isolated
- changed V8 timing internals for spectre/meltdown mitigation.
- still vulnerable to things like 'net spectre'
- Single threaded to avoid racing workers
- compile v8 with
v8_untrusted_code_mitigations
- compile config to binary with
workerd compile myconfig.capnp constantName -o combinedBinary
- implicit "internet" service network with public network only (no 192.168, 10.x, 127.x )
# By default this will serve a subdirectory called `content-dir`
# workerd serve config.capnp --directory-path site-files=/path/to/files
# Wrangler.toml integration using: npx wrangler dev --experimental-local
#[site]
#bucket = "./content-dir"
using Workerd = import "/workerd/workerd.capnp";
const config :Workerd.Config = (
services = [
# JavaScript worker to serve static files from a directory and content-type, default to index.html, etc.
(name = "site-worker", worker = .siteWorker),
# service which provides direct access to files
(name = "__STATIC_CONTENT", disk = "content-dir"),
# service for reverse proxy
],
sockets = [ ( name = "http", address = "*:8080", http = (), service = "site-worker" ) ],
);
const siteWorker :Workerd.Worker = (
compatibilityDate = "2022-09-16",
modules = [
(name = "shim.mjs", esModule = embed "shim.mjs"),
(name = "index.wasm", wasm = embed "index.wasm"),
],
bindings = [
# request files on disk via the disk service
# bug that name and kvNamespace need to be the same?
(name = "__STATIC_CONTENT", kvNamespace = "__STATIC_CONTENT"),
# worker configuration options via JSON binding
],
);
// cf worker for fileserver in rust with worker-build
use worker::*;
//#[event(start)]// once per worker
#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
let router = Router::new();
router
.get_async("/", |_, context| serve(context)) // for index.html
.get_async("/:asset", |_, context| serve(context))
.run(req, env)
.await
}
/* Cloudflare page assets are hashed
*
* __STATIC_CONTENT_MANIFEST needs to be read into wasm from js
* and excluded as external in worker-build
*
#[wasm_bindgen(module = "__STATIC_CONTENT_MANIFEST")]
extern "C" {
#[wasm_bindgen(js_name = "default")]
static MANIFEST: String;
}
static MANIFEST_MAP: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
serde_json::from_str::<HashMap<&str, &str>>(&MANIFEST)
.unwrap_or_default()
});
*/
pub async fn serve(context: RouteContext<()>) -> worker::Result<Response> {
let assets = context.kv("__STATIC_CONTENT")?; // default key-value namespace
let asset = context.param("asset")
.map(String::as_str)
.unwrap_or("index.html");
// locally MANIFEST_MAP is empty; otherwise a hashed name
//let asset = MANIFEST_MAP.get(asset).unwrap_or(&asset)
// handle redirects/rewrites
// apply gzip encoding for html/css/js
// when Accept-Encoding includes gzip
// set Content-Encoding header to gzip
// set cache headers
// implement webseeding requests
// multi-file torrent magnet webseed uri must end in directory; the client will request a specific file
// magnet link w/ xs:file.torrent for metadata, v2 torrent to avoid file alignment padding, http range requests
match assets.get(asset).bytes().await? {
Some(value) => {
let mut response = Response::from_bytes(value)?;
response.headers_mut()
.set("Content-Type", asset.rsplit_once(".")
.map_or_else(|| "text/plain", |(_, ext)| match ext {
"html" => "text/html;charset=utf-8",
"css" => "text/css;charset=utf-8",
"js" => "text/javascript;charset=utf-8",
"json" => "application/json",
"png" => "image/png",
"jpeg" => "image/jpeg",
"ico" => "image/x-icon",
"wasm" => "application/wasm",
"gif" => "image/gif",
"ttf" => "font/ttf",
"gz" => "application/gzip", // do not unzip
_ => "application/octet-stream",
})
)
.map(|_| response)
}
None => Response::error("Not Found", 404),
}
}