(** Serving the static assets embedded at build time. Assets are compiled into the executable by [ocaml-crunch] (see the rule in [lib/dune]), so a deployment is a single binary with no asset directory to keep in sync. Responses are cached for a day; because the content ships with the binary, a new release is the only thing that can change them. TODO: the cache lifetime is hard-coded. Make it configurable, or switch to content-hashed asset URLs so the lifetime can be raised safely. *) let content_type_of_ext = function | ".css" -> "text/css; charset=utf-8" | ".svg" -> "image/svg+xml" | ".html" -> "text/html; charset=utf-8" | ".js" -> "application/javascript" | ".png" -> "image/png" | ".ico" -> "image/x-icon" | _ -> "application/octet-stream" let handler request = let target = Dream.target request in (* Strip "/static/" prefix *) let path = if String.starts_with ~prefix:"/static/" target then String.sub target 8 (String.length target - 8) else "" in match Static_assets.read path with | Some content -> let content_type = Filename.extension path |> content_type_of_ext in Dream.respond ~headers: [ ("Content-Type", content_type); ("Cache-Control", "public, max-age=86400"); ] content | None -> Dream.respond ~status:`Not_Found "Not found"