mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2025-12-06 11:15:35 -05:00
feat: allow delete and use it in http_of_dir
This commit is contained in:
parent
7b59670d60
commit
7f68812312
3 changed files with 41 additions and 16 deletions
|
|
@ -46,6 +46,7 @@ module Meth = struct
|
||||||
| `PUT
|
| `PUT
|
||||||
| `POST
|
| `POST
|
||||||
| `HEAD
|
| `HEAD
|
||||||
|
| `DELETE
|
||||||
]
|
]
|
||||||
|
|
||||||
let to_string = function
|
let to_string = function
|
||||||
|
|
@ -53,6 +54,7 @@ module Meth = struct
|
||||||
| `PUT -> "PUT"
|
| `PUT -> "PUT"
|
||||||
| `HEAD -> "HEAD"
|
| `HEAD -> "HEAD"
|
||||||
| `POST -> "POST"
|
| `POST -> "POST"
|
||||||
|
| `DELETE -> "DELETE"
|
||||||
let pp out s = Format.pp_print_string out (to_string s)
|
let pp out s = Format.pp_print_string out (to_string s)
|
||||||
|
|
||||||
let of_string = function
|
let of_string = function
|
||||||
|
|
@ -60,6 +62,7 @@ module Meth = struct
|
||||||
| "PUT" -> `PUT
|
| "PUT" -> `PUT
|
||||||
| "POST" -> `POST
|
| "POST" -> `POST
|
||||||
| "HEAD" -> `HEAD
|
| "HEAD" -> `HEAD
|
||||||
|
| "DELETE" -> `DELETE
|
||||||
| s -> bad_reqf 400 "unknown method %S" s
|
| s -> bad_reqf 400 "unknown method %S" s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -388,7 +391,9 @@ let handle_client_ (self:t) (client_sock:Unix.file_descr) : unit =
|
||||||
(fun resp cb -> match cb req resp with None -> resp | Some r' -> r')
|
(fun resp cb -> match cb req resp with None -> resp | Some r' -> r')
|
||||||
resp self.cb_encode_resp
|
resp self.cb_encode_resp
|
||||||
with
|
with
|
||||||
| Bad_req _ as e -> raise e
|
| Bad_req (code,s) ->
|
||||||
|
continue := false;
|
||||||
|
Response.make_raw ~code s
|
||||||
| e ->
|
| e ->
|
||||||
Response.fail ~code:500 "server error: %s" (Printexc.to_string e)
|
Response.fail ~code:500 "server error: %s" (Printexc.to_string e)
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ module Meth : sig
|
||||||
| `PUT
|
| `PUT
|
||||||
| `POST
|
| `POST
|
||||||
| `HEAD
|
| `HEAD
|
||||||
|
| `DELETE
|
||||||
]
|
]
|
||||||
|
|
||||||
val pp : Format.formatter -> t -> unit
|
val pp : Format.formatter -> t -> unit
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,13 @@ type config = {
|
||||||
mutable port: int;
|
mutable port: int;
|
||||||
mutable upload: bool;
|
mutable upload: bool;
|
||||||
mutable max_upload_size: int;
|
mutable max_upload_size: int;
|
||||||
|
mutable delete: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
let default_config () : config = {
|
let default_config () : config = {
|
||||||
addr="127.0.0.1";
|
addr="127.0.0.1";
|
||||||
port=8080;
|
port=8080;
|
||||||
|
delete=false;
|
||||||
upload=true;
|
upload=true;
|
||||||
max_upload_size = 10 * 1024 * 1024;
|
max_upload_size = 10 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
@ -26,9 +28,10 @@ let contains_dot_dot s =
|
||||||
with Exit -> true
|
with Exit -> true
|
||||||
|
|
||||||
let header_html = "Content-Type", "text/html"
|
let header_html = "Content-Type", "text/html"
|
||||||
|
let (//) = Filename.concat
|
||||||
|
|
||||||
let html_list_dir ~parent d : string =
|
let html_list_dir ~top ~parent d : string =
|
||||||
let entries = Sys.readdir d in
|
let entries = Sys.readdir @@ (top // d) in
|
||||||
let body = Buffer.create 256 in
|
let body = Buffer.create 256 in
|
||||||
Printf.bprintf body "<ul>\n";
|
Printf.bprintf body "<ul>\n";
|
||||||
begin match parent with
|
begin match parent with
|
||||||
|
|
@ -38,10 +41,9 @@ let html_list_dir ~parent d : string =
|
||||||
end;
|
end;
|
||||||
Array.iter
|
Array.iter
|
||||||
(fun f ->
|
(fun f ->
|
||||||
let full = Filename.concat d f in
|
if not @@ contains_dot_dot (d // f) then (
|
||||||
if not @@ contains_dot_dot f then (
|
|
||||||
Printf.bprintf body " <li> <a href=\"/%s\"> %s %s </a> </li>\n"
|
Printf.bprintf body " <li> <a href=\"/%s\"> %s %s </a> </li>\n"
|
||||||
full (if Sys.is_directory full then "[dir]" else "") f;
|
(d // f) f (if Sys.is_directory (top // d // f) then "[dir]" else "");
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entries;
|
entries;
|
||||||
|
|
@ -53,7 +55,20 @@ let same_path a b =
|
||||||
Filename.basename a = Filename.basename b
|
Filename.basename a = Filename.basename b
|
||||||
|
|
||||||
let serve ~config (dir:string) : _ result =
|
let serve ~config (dir:string) : _ result =
|
||||||
|
Printf.printf "serve directory %s on http://%s:%d\n%!" dir config.addr config.port;
|
||||||
let server = S.create ~addr:config.addr ~port:config.port () in
|
let server = S.create ~addr:config.addr ~port:config.port () in
|
||||||
|
if config.delete then (
|
||||||
|
S.add_path_handler server ~meth:`DELETE "/%s"
|
||||||
|
(fun path _req ->
|
||||||
|
if contains_dot_dot path then (
|
||||||
|
S.Response.fail_raise ~code:403 "invalid path in delete"
|
||||||
|
);
|
||||||
|
S.Response.make
|
||||||
|
(try
|
||||||
|
Sys.remove (dir // path); Ok "file deleted successfully"
|
||||||
|
with e -> Error (500, Printexc.to_string e))
|
||||||
|
);
|
||||||
|
);
|
||||||
if config.upload then (
|
if config.upload then (
|
||||||
S.add_path_handler server ~meth:`PUT "/%s"
|
S.add_path_handler server ~meth:`PUT "/%s"
|
||||||
~accept:(fun req ->
|
~accept:(fun req ->
|
||||||
|
|
@ -68,7 +83,7 @@ let serve ~config (dir:string) : _ result =
|
||||||
string_of_int config.max_upload_size)
|
string_of_int config.max_upload_size)
|
||||||
)
|
)
|
||||||
(fun path req ->
|
(fun path req ->
|
||||||
let fpath = Filename.concat dir path in
|
let fpath = dir // path in
|
||||||
let oc =
|
let oc =
|
||||||
try open_out fpath
|
try open_out fpath
|
||||||
with e ->
|
with e ->
|
||||||
|
|
@ -83,21 +98,23 @@ let serve ~config (dir:string) : _ result =
|
||||||
);
|
);
|
||||||
S.add_path_handler server ~meth:`GET "/%s"
|
S.add_path_handler server ~meth:`GET "/%s"
|
||||||
(fun path _req ->
|
(fun path _req ->
|
||||||
let f = Filename.concat dir path in
|
let path_dir = Filename.dirname path in
|
||||||
if contains_dot_dot f then (
|
let path_f = Filename.basename path in
|
||||||
|
let full_path = dir // path_dir // path_f in
|
||||||
|
if contains_dot_dot full_path then (
|
||||||
S.Response.fail ~code:403 "Path is forbidden";
|
S.Response.fail ~code:403 "Path is forbidden";
|
||||||
) else if not (Sys.file_exists f) then (
|
) else if not (Sys.file_exists full_path) then (
|
||||||
S.Response.fail ~code:404 "file not found";
|
S.Response.fail ~code:404 "file not found";
|
||||||
) else if Sys.is_directory f then (
|
) else if Sys.is_directory full_path then (
|
||||||
S._debug (fun k->k "list dir %S (topdir %S)" f dir);
|
S._debug (fun k->k "list dir %S (topdir %S)" full_path dir);
|
||||||
let body =
|
let body =
|
||||||
html_list_dir f
|
html_list_dir ~top:dir path
|
||||||
~parent:(if same_path f dir then None else Some dir)
|
~parent:(if same_path full_path dir then None else Some dir)
|
||||||
in
|
in
|
||||||
S.Response.make ~headers:[header_html] (Ok body)
|
S.Response.make ~headers:[header_html] (Ok body)
|
||||||
) else (
|
) else (
|
||||||
try
|
try
|
||||||
let ic = open_in path in
|
let ic = open_in full_path in
|
||||||
S.Response.make_raw_chunked ~code:200 (input ic)
|
S.Response.make_raw_chunked ~code:200 (input ic)
|
||||||
with e ->
|
with e ->
|
||||||
S.Response.fail ~code:500 "error while reading file: %s" (Printexc.to_string e)
|
S.Response.fail ~code:500 "error while reading file: %s" (Printexc.to_string e)
|
||||||
|
|
@ -118,7 +135,9 @@ let main () =
|
||||||
"--max-upload", Int (fun i -> config.max_upload_size <- 1024 * 1024 * i),
|
"--max-upload", Int (fun i -> config.max_upload_size <- 1024 * 1024 * i),
|
||||||
"maximum size of files that can be uploaded, in MB";
|
"maximum size of files that can be uploaded, in MB";
|
||||||
"--debug", Unit (fun () -> S._enable_debug true), " debug mode";
|
"--debug", Unit (fun () -> S._enable_debug true), " debug mode";
|
||||||
]) (fun _ -> raise (Arg.Bad "no positional arguments")) "http_of_dir [options]";
|
"--delete", Unit (fun () -> config.delete <- true), " enable `delete` on files";
|
||||||
|
"--no-delete", Unit (fun () -> config.delete <- false), " disable `delete` on files";
|
||||||
|
]) (fun s -> dir_ := s) "http_of_dir [options] [dir]";
|
||||||
match serve ~config !dir_ with
|
match serve ~config !dir_ with
|
||||||
| Ok () -> ()
|
| Ok () -> ()
|
||||||
| Error e ->
|
| Error e ->
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue