package cohttp-lwt-unix

  1. Overview
  2. Docs

Source file server.ml

1
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

module Server_core = Cohttp_lwt.Make_server (Io)

include Server_core
open Lwt.Infix

let src = Logs.Src.create "cohttp.lwt.server" ~doc:"Cohttp Lwt server module"
module Log = (val Logs.src_log src : Logs.LOG)

let blank_uri = Uri.of_string ""

let resolve_file ~docroot ~uri =
  (* This normalises the Uri and strips out .. characters *)
  let frag = Uri.path (Uri.resolve "" blank_uri uri) in
  Filename.concat docroot frag

exception Isnt_a_file
let respond_file ?headers ~fname () =
  Lwt.catch (fun () ->
      (* Check this isnt a directory first *)
      (fname |> Lwt_unix.stat >>= fun s ->
       if Unix.(s.st_kind <> S_REG)
       then Lwt.fail Isnt_a_file
       else Lwt.return_unit) >>= fun () ->
      let count = 16384 in
      Lwt_io.open_file
        ~buffer:(Lwt_bytes.create count)
        ~mode:Lwt_io.input fname >>= fun ic ->
      Lwt_io.length ic >>= fun len ->
      let encoding = Cohttp.Transfer.Fixed len in
      let stream = Lwt_stream.from (fun () ->
          Lwt.catch (fun () ->
              Lwt_io.read ~count ic >|= function
              | "" -> None
              | buf -> Some buf)
            (fun exn ->
               Log.debug
                 (fun m -> m "Error resolving file %s (%s)"
                   fname
                   (Printexc.to_string exn));
               Lwt.return_none)
        ) in
      Lwt.on_success (Lwt_stream.closed stream) (fun () ->
        Lwt.ignore_result @@ Lwt.catch
          (fun () -> Lwt_io.close ic)
          (fun e ->
            Log.warn (fun f ->
              f "Closing channel failed: %s" (Printexc.to_string e));
            Lwt.return_unit
          )
      );
      let body = Cohttp_lwt.Body.of_stream stream in
      let mime_type = Magic_mime.lookup fname in
      let headers = Cohttp.Header.add_opt_unless_exists
          headers "content-type" mime_type in
      let res = Cohttp.Response.make ~status:`OK ~encoding ~headers () in
      Lwt.return (res, body)
    ) (function
      | Unix.Unix_error(Unix.ENOENT,_,_) | Isnt_a_file ->
        respond_not_found ()
      | exn -> Lwt.fail exn)

let create ?timeout ?backlog ?stop ?on_exn ?(ctx=Net.default_ctx)
    ?(mode=`TCP (`Port 8080)) spec =
  Conduit_lwt_unix.serve ?backlog ?timeout ?stop ?on_exn ~ctx:ctx.Net.ctx
    ~mode (callback spec)
OCaml

Innovation. Community. Security.