View raw

1 (** Serving the static assets embedded at build time. 2 3 Assets are compiled into the executable by [ocaml-crunch] (see the rule in 4 [lib/dune]), so a deployment is a single binary with no asset directory to 5 keep in sync. Responses are cached for a day; because the content ships with 6 the binary, a new release is the only thing that can change them. 7 8 TODO: the cache lifetime is hard-coded. Make it configurable, or switch to 9 content-hashed asset URLs so the lifetime can be raised safely. *) 10 11 let content_type_of_ext = function 12 | ".css" -> "text/css; charset=utf-8" 13 | ".svg" -> "image/svg+xml" 14 | ".html" -> "text/html; charset=utf-8" 15 | ".js" -> "application/javascript" 16 | ".png" -> "image/png" 17 | ".ico" -> "image/x-icon" 18 | _ -> "application/octet-stream" 19 20 let handler request = 21 let target = Dream.target request in 22 (* Strip "/static/" prefix *) 23 let path = 24 if String.starts_with ~prefix:"/static/" target then 25 String.sub target 8 (String.length target - 8) 26 else "" 27 in 28 match Static_assets.read path with 29 | Some content -> 30 let content_type = Filename.extension path |> content_type_of_ext in 31 Dream.respond 32 ~headers: 33 [ 34 ("Content-Type", content_type); 35 ("Cache-Control", "public, max-age=86400"); 36 ] 37 content 38 | None -> Dream.respond ~status:`Not_Found "Not found" 39