Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
vif_handler_unix.ml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144open Vif_core type ('c, 'value) t = (Httpcats.Server.flow, 'c, 'value) Handler.t let pwd = Fpath.v (Unix.getcwd ()) let tree = Conan_light.tree let ( let@ ) finally fn = Fun.protect ~finally fn let file ?offset path = let open Flux in let buf = Bytes.create 0x7ff in let fn bqueue = let fd = Unix.openfile path Unix.[ O_RDONLY ] 0o644 in let resource = Miou.Ownership.create ~finally:Unix.close fd in Miou.Ownership.own resource; let@ () = fun () -> Miou.Ownership.release resource; Bqueue.close bqueue in let _ = match offset with | Some offset -> Unix.lseek fd offset Unix.SEEK_SET | None -> 0 in let rec go () = let len = Unix.read fd buf 0 (Bytes.length buf) in if len > 0 then ( let str = Bytes.sub_string buf 0 len in Bqueue.put bqueue str; go ()) in go () in Source.with_task ~size:0x7ff fn let sha256sum path = let path = Fpath.to_string path in if Sys.file_exists path = false || Sys.is_directory path then invalid_arg "sha256sum"; let fd = Unix.openfile path Unix.[ O_RDONLY ] 0o644 in let finally () = Unix.close fd in Fun.protect ~finally @@ fun () -> let stat = Unix.fstat fd in let ba = Unix.map_file fd Bigarray.char Bigarray.c_layout false [| stat.Unix.st_size |] in let ba = Bigarray.array1_of_genarray ba in let hash = Digestif.SHA256.digest_bigstring ba in Digestif.SHA256.to_hex hash let mime_type path = match Conan_unix.run_with_tree Conan_light.tree (Fpath.to_string path) with | Ok m -> Option.value ~default:"application/octet-stream" (Conan.Metadata.mime m) | Error _ -> "application/octet-stream" | exception _ -> "application/octet-stream" let cached_on_client_side ?etag req target = let hdrs = Request.headers req in match (Headers.get hdrs "if-none-match", etag) with | Some hash', None -> let hash = sha256sum target in String.equal hash hash' | Some hash', Some hash -> String.equal hash hash' | None, _ -> false let valid ~top target = Fpath.is_prefix top target && Sys.file_exists (Fpath.to_string target) && Sys.is_directory (Fpath.to_string target) = false let pp_msg ppf (`Msg msg) = Fmt.string ppf msg let trim lst = let lst = List.drop_while (( = ) "") lst in let lst = List.drop_while (( = ) "") (List.rev lst) in List.rev lst module K = struct type t = Fpath.t let equal = Fpath.equal let hash = Hashtbl.hash end module V = struct type t = { mtime: float; mime: string } let weight _ = 1 end module Cache = Lru.M.Make (K) (V) let cached_on_server_size stat abs_path cache = match Cache.find abs_path cache with | Some { mtime; mime } when mtime >= stat.Unix.st_mtime -> Some mime | Some _ -> Cache.remove abs_path cache; None | None -> None let static ?(top = pwd) = (); let cache = Cache.create ~random:true 0x100 in fun req target _server _ -> let target = String.split_on_char '/' target in let target = trim target in let target = String.concat "/" target in let abs_path = let ( let* ) = Result.bind in let* x = Fpath.of_string target in Ok Fpath.(normalize (top // x)) in match (Request.meth req, abs_path) with | `GET, Ok abs_path when valid ~top abs_path -> begin let ( let* ) = Response.bind in let process = if cached_on_client_side req abs_path then let* () = Response.with_string req "" in Response.respond `Not_modified else let stat = Unix.stat (Fpath.to_string abs_path) in let mime = match cached_on_server_size stat abs_path cache with | Some mime -> mime | None -> let mime = mime_type abs_path in let value = { V.mtime= stat.Unix.st_mtime; mime } in Cache.add abs_path value cache; mime in let src = file (Fpath.to_string abs_path) in let* _ = Response.content_length stat.Unix.st_size in let field = "content-type" in let* () = Response.add ~field mime in let field = "etag" in let* () = Response.add ~field (sha256sum abs_path) in let* () = Response.with_source req src in Response.respond `OK in Some process end | _ -> None