Skip to content

v0.4.0 — We Put a Scripting Language Inside a Static File Server

Latest

Choose a tag to compare

@Bonuspunkt Bonuspunkt released this 28 Feb 00:09

We embedded Lua 5.4 into a static file server. On purpose.

🌙 Lua URL Rewriting Engine

CaseDropper now ships with an embedded Lua 5.4.7 scripting engine for URL rewriting. Set LUA_SCRIPT to a Lua file that defines a rewrite(path) function, and CaseDropper will generate URL aliases at index build time. Both the original path and the rewritten alias serve the same file.

The key architectural decision: Lua runs at index time, not per-request. The path map is built once (and rebuilt on file changes), so request handling is still just a hash map lookup. We added a scripting engine and it costs exactly zero nanoseconds at runtime.

LUA_SCRIPT=scripts/smallcaps.lua WWWROOT=wwwroot ./zig-out/bin/casedropper

# /index.html  → 200 ✅
# /ɪɴᴅᴇx.html  → 200 ✅

📜 Write Your Own

function rewrite(path)
    -- return string for alias, nil to skip
    return path:gsub("hello", "привет")
end

Drop it in scripts/, set LUA_SCRIPT, restart. The path map rebuilds with your aliases. File watching picks up changes automatically.

Example scripts ship in scripts/ — discoverable by the curious.

🔗 URL Percent-Decoding

PathMap.normalizePath now percent-decodes URLs before lookup. Unicode characters in URLs work correctly when the browser percent-encodes them. This also fixes edge cases with filenames containing spaces or special characters.

Breaking Changes

  • musl libc is now statically linked — Lua needs libc. The binary is still fully static and the scratch container still works, but the binary is slightly larger.
  • PathMap.init() now takes an optional LuaEngine pointer as its third argument.

Technical Details

  • Lua 5.4.7 source vendored in deps/lua/src/ (32 C files, compiled by Zig's build system)
  • Zero external dependencies at build time — Zig compiles the Lua C sources alongside the Zig code
  • Cross-compilation to all 9 architectures still works (Lua is pure C99)
  • Lua engine lifecycle: init once at startup, called during path map build/rebuild, deinit at shutdown
  • Error handling: script load failures are fatal; runtime errors in rewrite() are logged and skipped
  • File extensions are preserved by bundled scripts (only stems are transformed)
  • First-write-wins collision handling — real paths always beat Lua aliases