Skip to content

Commit f8a7a80

Browse files
committed
Add support for directory listings in Staticmod
Add a simple generator for directory listings: - the listings are unstyled - the generation must load the full directory (so that the entries can be sorted) - consequently the generation should probably not be enabled when there are huge directories (which are usually a bad idea anyhow)
1 parent 7950e0a commit f8a7a80

File tree

2 files changed

+59
-12
lines changed

2 files changed

+59
-12
lines changed

src/extensions/dune

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
(name staticmod)
5757
(public_name ocsigenserver.ext.staticmod)
5858
(modules staticmod)
59-
(libraries ocsigenserver))
59+
(libraries tyxml ocsigenserver))
6060

6161
(library
6262
(name userconf)

src/extensions/staticmod.ml

+58-11
Original file line numberDiff line numberDiff line change
@@ -116,27 +116,74 @@ let find_static_page ~request ~usermode ~dir ~(err : Cohttp.Code.status)
116116
(Ocsigen_extensions.Error_in_user_config_file
117117
"Staticmod: cannot use '..' in user paths")
118118

119+
let respond_dir relpath dname : (Cohttp.Response.t * Cohttp_lwt.Body.t) Lwt.t =
120+
let readsortdir =
121+
(* Read a complete directory and sort its entries *)
122+
let chunk_size = 1024 in
123+
let rec aux entries dir =
124+
Lwt_unix.readdir_n dir chunk_size >>= fun chunk ->
125+
let entries = chunk :: entries in
126+
if Array.length chunk < chunk_size
127+
then Lwt.return entries
128+
else aux entries dir
129+
in
130+
Lwt_unix.opendir dname >>= fun dir ->
131+
Lwt.finalize
132+
(fun () ->
133+
aux [] dir >|= fun entries ->
134+
List.sort compare (List.concat_map Array.to_list entries))
135+
(fun () -> Lwt_unix.closedir dir)
136+
in
137+
Lwt.catch
138+
(fun () ->
139+
readsortdir >>= fun entries ->
140+
let render e = Format.asprintf "%a" (Tyxml.Html.pp_elt ()) e in
141+
let t = render (Tyxml.Html.txt ("Directory listing for " ^ relpath)) in
142+
let entries =
143+
let open Tyxml.Html in
144+
List.filter_map
145+
(function
146+
| "." | ".." -> None
147+
| e -> Some (render (li [a ~a:[a_href e] [txt e]])))
148+
entries
149+
in
150+
(* Chunks of [html (head (title t) []) (body [h1 [t]; ul entries])] *)
151+
let chunk1 =
152+
{|<!DOCTYPE html>
153+
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>|}
154+
and chunk2 = {|</title></head><body><h1>|}
155+
and chunk3 = {|</h1><ul>|}
156+
and chunkend = {|</ul></body></html>|} in
157+
let doc =
158+
chunk1 :: t :: chunk2 :: t :: chunk3 :: (entries @ [chunkend])
159+
in
160+
let headers = Cohttp.Header.init_with "content-type" "text/html" in
161+
Lwt.return
162+
( Cohttp.Response.make ~status:`OK ~headers ()
163+
, Cohttp_lwt.Body.of_string_list doc ))
164+
(function
165+
| Unix.Unix_error _ -> Cohttp_lwt_unix.Server.respond_not_found ()
166+
| exn -> Lwt.fail exn)
167+
119168
let gen ~usermode ?cache dir = function
120169
| Ocsigen_extensions.Req_found _ ->
121170
Lwt.return Ocsigen_extensions.Ext_do_nothing
122171
| Ocsigen_extensions.Req_not_found
123172
(err, ({Ocsigen_extensions.request_info; _} as request)) ->
124173
let try_block () =
125174
Lwt_log.ign_info ~section "Is it a static file?";
175+
let pathstring =
176+
Ocsigen_lib.Url.string_of_url_path ~encode:false
177+
(Ocsigen_request.sub_path request_info)
178+
in
126179
let status_filter, page =
127-
let pathstring =
128-
Ocsigen_lib.Url.string_of_url_path ~encode:false
129-
(Ocsigen_request.sub_path request_info)
130-
in
131180
find_static_page ~request ~usermode ~dir ~err ~pathstring
132181
in
133-
let fname =
134-
match page with
135-
| Ocsigen_local_files.RFile fname -> fname
136-
| Ocsigen_local_files.RDir _ ->
137-
failwith "FIXME: staticmod dirs not implemented"
138-
in
139-
Cohttp_lwt_unix.Server.respond_file ~fname () >>= fun answer ->
182+
(match page with
183+
| Ocsigen_local_files.RFile fname ->
184+
Cohttp_lwt_unix.Server.respond_file ~fname ()
185+
| Ocsigen_local_files.RDir dname -> respond_dir pathstring dname)
186+
>>= fun answer ->
140187
let answer = Ocsigen_response.of_cohttp answer in
141188
let answer =
142189
if not status_filter

0 commit comments

Comments
 (0)