mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2025-12-06 19:25:32 -05:00
parent
440834ca13
commit
4aaf77b261
3 changed files with 117 additions and 9 deletions
90
src/Tiny_httpd_util.ml
Normal file
90
src/Tiny_httpd_util.ml
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
let percent_encode s =
|
||||||
|
let buf = Buffer.create (String.length s) in
|
||||||
|
String.iter
|
||||||
|
(function
|
||||||
|
| ' ' -> Buffer.add_string buf "%20"
|
||||||
|
| '!' -> Buffer.add_string buf "%21"
|
||||||
|
| '"' -> Buffer.add_string buf "%22"
|
||||||
|
| '#' -> Buffer.add_string buf "%23"
|
||||||
|
| '$' -> Buffer.add_string buf "%24"
|
||||||
|
| '%' -> Buffer.add_string buf "%25"
|
||||||
|
| '&' -> Buffer.add_string buf "%26"
|
||||||
|
| '\'' -> Buffer.add_string buf "%27"
|
||||||
|
| '(' -> Buffer.add_string buf "%28"
|
||||||
|
| ')' -> Buffer.add_string buf "%29"
|
||||||
|
| '*' -> Buffer.add_string buf "%2A"
|
||||||
|
| '+' -> Buffer.add_string buf "%2B"
|
||||||
|
| ',' -> Buffer.add_string buf "%2C"
|
||||||
|
| '/' -> Buffer.add_string buf "%2F"
|
||||||
|
| ':' -> Buffer.add_string buf "%3A"
|
||||||
|
| ';' -> Buffer.add_string buf "%3B"
|
||||||
|
| '=' -> Buffer.add_string buf "%3D"
|
||||||
|
| '?' -> Buffer.add_string buf "%3F"
|
||||||
|
| '@' -> Buffer.add_string buf "%40"
|
||||||
|
| '[' -> Buffer.add_string buf "%5B"
|
||||||
|
| ']' -> Buffer.add_string buf "%5D"
|
||||||
|
| c -> Buffer.add_char buf c)
|
||||||
|
s;
|
||||||
|
Buffer.contents buf
|
||||||
|
|
||||||
|
let percent_decode (s:string) : _ option =
|
||||||
|
let buf = Buffer.create (String.length s) in
|
||||||
|
let i = ref 0 in
|
||||||
|
try
|
||||||
|
while !i < String.length s do
|
||||||
|
match String.get s !i with
|
||||||
|
| '%' ->
|
||||||
|
if !i+2 < String.length s then (
|
||||||
|
begin match String.sub s (!i+1) 2 with
|
||||||
|
| "20" -> Buffer.add_char buf ' '
|
||||||
|
| "21" -> Buffer.add_char buf '!'
|
||||||
|
| "22" -> Buffer.add_char buf '"'
|
||||||
|
| "23" -> Buffer.add_char buf '#'
|
||||||
|
| "24" -> Buffer.add_char buf '$'
|
||||||
|
| "25" -> Buffer.add_char buf '%'
|
||||||
|
| "26" -> Buffer.add_char buf '&'
|
||||||
|
| "27" -> Buffer.add_char buf '\''
|
||||||
|
| "28" -> Buffer.add_char buf '('
|
||||||
|
| "29" -> Buffer.add_char buf ')'
|
||||||
|
| "2A" -> Buffer.add_char buf '*'
|
||||||
|
| "2B" -> Buffer.add_char buf '+'
|
||||||
|
| "2C" -> Buffer.add_char buf ','
|
||||||
|
| "2F" -> Buffer.add_char buf '/'
|
||||||
|
| "3A" -> Buffer.add_char buf ':'
|
||||||
|
| "3B" -> Buffer.add_char buf ';'
|
||||||
|
| "3D" -> Buffer.add_char buf '='
|
||||||
|
| "3F" -> Buffer.add_char buf '?'
|
||||||
|
| "40" -> Buffer.add_char buf '@'
|
||||||
|
| "5B" -> Buffer.add_char buf '['
|
||||||
|
| "5D" -> Buffer.add_char buf ']'
|
||||||
|
| _ -> raise Exit
|
||||||
|
end;
|
||||||
|
i := !i + 3;
|
||||||
|
) else (
|
||||||
|
raise Exit (* truncated *)
|
||||||
|
)
|
||||||
|
| c -> Buffer.add_char buf c; incr i
|
||||||
|
done;
|
||||||
|
Some (Buffer.contents buf)
|
||||||
|
with Exit -> None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
13
src/Tiny_httpd_util.mli
Normal file
13
src/Tiny_httpd_util.mli
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
(** {1 Some utils for writing web servers}
|
||||||
|
|
||||||
|
@since NEXT_RELEASE
|
||||||
|
*)
|
||||||
|
|
||||||
|
val percent_encode : string -> string
|
||||||
|
(** Encode the string into a valid path following
|
||||||
|
https://tools.ietf.org/html/rfc3986#section-2.1
|
||||||
|
*)
|
||||||
|
|
||||||
|
val percent_decode : string -> string option
|
||||||
|
(** Inverse operation of {!percent_encode}.
|
||||||
|
Can fail since some strings are not valid percent encodings. *)
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
module S = Tiny_httpd
|
module S = Tiny_httpd
|
||||||
|
module U = Tiny_httpd_util
|
||||||
module Pf = Printf
|
module Pf = Printf
|
||||||
|
|
||||||
type config = {
|
type config = {
|
||||||
|
|
@ -38,8 +39,6 @@ let human_size (x:int) : string =
|
||||||
let header_html = "Content-Type", "text/html"
|
let header_html = "Content-Type", "text/html"
|
||||||
let (//) = Filename.concat
|
let (//) = Filename.concat
|
||||||
|
|
||||||
(* TODO: percent encoding/decoding *)
|
|
||||||
|
|
||||||
let html_list_dir ~top ~parent d : string =
|
let html_list_dir ~top ~parent d : string =
|
||||||
let entries = Sys.readdir @@ (top // d) in
|
let entries = Sys.readdir @@ (top // d) in
|
||||||
Array.sort compare entries;
|
Array.sort compare entries;
|
||||||
|
|
@ -66,7 +65,7 @@ let html_list_dir ~top ~parent d : string =
|
||||||
with _ -> ""
|
with _ -> ""
|
||||||
in
|
in
|
||||||
Printf.bprintf body " <li> <a href=\"/%s\"> %s </a> %s%s </li>\n"
|
Printf.bprintf body " <li> <a href=\"/%s\"> %s </a> %s%s </li>\n"
|
||||||
(d // f) f (if Sys.is_directory fpath then "[dir]" else "") size
|
(U.percent_encode (d // f)) f (if Sys.is_directory fpath then "[dir]" else "") size
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -89,13 +88,15 @@ let serve ~config (dir:string) : _ result =
|
||||||
if config.delete then (
|
if config.delete then (
|
||||||
S.add_path_handler server ~meth:`DELETE "/%s"
|
S.add_path_handler server ~meth:`DELETE "/%s"
|
||||||
(fun path _req ->
|
(fun path _req ->
|
||||||
if contains_dot_dot path then (
|
match U.percent_decode path with
|
||||||
|
| None -> S.Response.fail_raise ~code:404 "invalid percent encoding"
|
||||||
|
| Some path when contains_dot_dot path ->
|
||||||
S.Response.fail_raise ~code:403 "invalid path in delete"
|
S.Response.fail_raise ~code:403 "invalid path in delete"
|
||||||
);
|
| Some path ->
|
||||||
S.Response.make_string
|
S.Response.make_string
|
||||||
(try
|
(try
|
||||||
Sys.remove (dir // path); Ok "file deleted successfully"
|
Sys.remove (dir // path); Ok "file deleted successfully"
|
||||||
with e -> Error (500, Printexc.to_string e))
|
with e -> Error (500, Printexc.to_string e))
|
||||||
);
|
);
|
||||||
) else (
|
) else (
|
||||||
S.add_path_handler server ~meth:`DELETE "/%s"
|
S.add_path_handler server ~meth:`DELETE "/%s"
|
||||||
|
|
@ -133,6 +134,10 @@ 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 path = match U.percent_decode path with
|
||||||
|
| None -> S.Response.fail_raise ~code:404 "invalid path"
|
||||||
|
| Some p -> p
|
||||||
|
in
|
||||||
let full_path = dir // path in
|
let full_path = dir // path in
|
||||||
let mtime = lazy (
|
let mtime = lazy (
|
||||||
try Printf.sprintf "mtime: %f" (Unix.stat full_path).Unix.st_mtime
|
try Printf.sprintf "mtime: %f" (Unix.stat full_path).Unix.st_mtime
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue