Skip to content

Commit 4c79e09

Browse files
authored
Merge pull request #268 from Julow/ocsigen_respond_file
Add Ocsigen_response.respond_file
2 parents ed1160c + f652f84 commit 4c79e09

File tree

4 files changed

+92
-2
lines changed

4 files changed

+92
-2
lines changed

src/extensions/staticmod.ml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ let gen ~usermode ?cache dir = function
142142
| Ocsigen_local_files.RDir _ ->
143143
failwith "FIXME: staticmod dirs not implemented"
144144
in
145-
Cohttp_lwt_unix.Server.respond_file ~fname () >>= fun answer ->
146-
let answer = Ocsigen_response.of_cohttp answer in
145+
Ocsigen_response.respond_file fname >>= fun answer ->
147146
let answer =
148147
if not status_filter
149148
then answer

src/server/ocsigen_response.ml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,63 @@ let respond_string ?headers ~status ~body () =
5050
let respond_error ?headers ?(status = `Internal_server_error) ~body () =
5151
respond_string ?headers ~status ~body:("Error: " ^ body) ()
5252

53+
let respond_not_found ?uri () =
54+
let body =
55+
match uri with
56+
| None -> "Not found"
57+
| Some uri -> "Not found: " ^ Uri.to_string uri
58+
in
59+
respond_string ~status:`Not_found ~body ()
60+
61+
let respond_file ?headers ?(status = `OK) fname =
62+
let exception Isnt_a_file in
63+
(* Copied from [cohttp-lwt-unix] and adapted to [Body]. *)
64+
Lwt.catch
65+
(fun () ->
66+
(* Check this isn't a directory first *)
67+
let* () =
68+
let* s = Lwt_unix.stat fname in
69+
if Unix.(s.st_kind <> S_REG)
70+
then raise Isnt_a_file
71+
else Lwt.return_unit
72+
in
73+
let count = 16384 in
74+
let* ic =
75+
Lwt_io.open_file ~buffer:(Lwt_bytes.create count) ~mode:Lwt_io.input
76+
fname
77+
in
78+
let* len = Lwt_io.length ic in
79+
let encoding = Http.Transfer.Fixed len in
80+
let stream write =
81+
let rec cat_loop () =
82+
Lwt.bind (Lwt_io.read ~count ic) (function
83+
| "" -> Lwt.return_unit
84+
| buf -> Lwt.bind (write buf) cat_loop)
85+
in
86+
let* () =
87+
Lwt.catch cat_loop (fun exn ->
88+
Logs.warn (fun m ->
89+
m "Error resolving file %s (%s)" fname (Printexc.to_string exn));
90+
Lwt.return_unit)
91+
in
92+
Lwt.catch
93+
(fun () -> Lwt_io.close ic)
94+
(fun e ->
95+
Logs.warn (fun f ->
96+
f "Closing channel failed: %s" (Printexc.to_string e));
97+
Lwt.return_unit)
98+
in
99+
let body = Body.make encoding stream in
100+
let mime_type = Magic_mime.lookup fname in
101+
let headers =
102+
Http.Header.add_opt_unless_exists headers "content-type" mime_type
103+
in
104+
Lwt.return (respond ~headers ~status ~encoding ~body ()))
105+
(function
106+
| Unix.Unix_error (Unix.ENOENT, _, _) | Isnt_a_file ->
107+
Lwt.return (respond_not_found ())
108+
| exn -> Lwt.reraise exn)
109+
53110
let update ?response ?body ?cookies {a_response; a_body; a_cookies} =
54111
let a_response =
55112
match response with Some response -> response | None -> a_response

src/server/ocsigen_response.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ val respond_error :
5353
5454
@deprecated Use [respond_string] with a [~status] argument instead. *)
5555

56+
val respond_file :
57+
?headers:Cohttp.Header.t
58+
-> ?status:Http.Status.t
59+
-> string
60+
-> t Lwt.t
61+
(** Respond with the content of a file. The content type is guessed using
62+
[Magic_mime]. *)
63+
5664
val update :
5765
?response:Cohttp.Response.t
5866
-> ?body:Body.t

test/extensions/deflatemod.t/run.t

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
ocsigen:local-file: [INFO] Testing "./index.html".
1515
ocsigen:local-file: [INFO] checking if file index.html can be sent
1616
ocsigen:local-file: [INFO] Returning "./index.html".
17+
ocsigen:access: connection for local-test from unix:// (): /empty_dir/
18+
ocsigen:ext: [INFO] host found! local-test:0 matches .*
19+
ocsigen:ext:staticmod: [INFO] Is it a static file?
20+
ocsigen:local-file: [INFO] Testing "./empty_dir/".
21+
ocsigen:local-file: [INFO] Testing "./empty_dir/index.html" as possible index.
22+
ocsigen:local-file: [INFO] No index and no listing
23+
ocsigen:access: connection for local-test from unix:// (): /doesnt_exists.html
24+
ocsigen:ext: [INFO] host found! local-test:0 matches .*
25+
ocsigen:ext:staticmod: [INFO] Is it a static file?
26+
ocsigen:local-file: [INFO] Testing "./doesnt_exists.html".
1727
application: [WARNING] Command received: shutdown
1828

1929
First response is not compressed:
@@ -37,3 +47,19 @@ Second response is compressed:
3747
transfer-encoding: chunked
3848

3949
Hello world
50+
51+
Querying a directory or a non-existing file should give "Not found" without
52+
compression:
53+
54+
$ mkdir empty_dir && curl_ empty_dir/ --compressed
55+
HTTP/1.1 404 Not Found
56+
content-length: 16
57+
server: Ocsigen
58+
59+
Error: Not Found
60+
$ curl_ doesnt_exists.html --compressed
61+
HTTP/1.1 404 Not Found
62+
content-length: 16
63+
server: Ocsigen
64+
65+
Error: Not Found

0 commit comments

Comments
 (0)