From 731dd7de51279ce8685719bfdd2bc366dc7589de Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 3 Dec 2024 10:13:33 -0500 Subject: [PATCH] add a form to echo.ml for manual testing --- examples/dune | 2 +- examples/echo.ml | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/examples/dune b/examples/dune index d2c19915..08d06886 100644 --- a/examples/dune +++ b/examples/dune @@ -12,7 +12,7 @@ (name echo) (flags :standard -warn-error -a+8) (modules echo vfs) - (libraries tiny_httpd logs tiny_httpd_camlzip)) + (libraries tiny_httpd logs tiny_httpd_camlzip tiny_httpd.multipart-form-data)) (executable (name writer) diff --git a/examples/echo.ml b/examples/echo.ml index f3d0f2af..af133187 100644 --- a/examples/echo.ml +++ b/examples/echo.ml @@ -1,5 +1,6 @@ open Tiny_httpd_core module Log = Tiny_httpd.Log +module MFD = Tiny_httpd_multipart_form_data let now_ = Unix.gettimeofday @@ -78,6 +79,58 @@ let setup_logging () = Logs.set_reporter @@ Logs.format_reporter (); Logs.set_level ~all:true (Some Logs.Debug) +let setup_upload server : unit = + Server.add_route_handler_stream ~meth:`POST server + Route.(exact "upload" @/ return) + (fun req -> + let (`boundary boundary) = + match MFD.parse_content_type req.headers with + | Some b -> b + | None -> Response.fail_raise ~code:400 "no boundary found" + in + + let st = MFD.create ~boundary req.body in + let tbl = Hashtbl.create 16 in + let cur = ref "" in + let cur_kind = ref "" in + let buf = Buffer.create 16 in + let rec loop () = + match MFD.next st with + | End_of_input -> + if !cur <> "" then + Hashtbl.add tbl !cur (!cur_kind, Buffer.contents buf) + | Part headers -> + if !cur <> "" then + Hashtbl.add tbl !cur (!cur_kind, Buffer.contents buf); + (match MFD.Content_disposition.parse headers with + | Some { kind; name = Some name; filename = _ } -> + cur := name; + cur_kind := kind; + Buffer.clear buf; + loop () + | _ -> Response.fail_raise ~code:400 "content disposition missing") + | Read sl -> + Buffer.add_subbytes buf sl.bytes sl.off sl.len; + loop () + in + loop (); + + let open Tiny_httpd_html in + let data = + Hashtbl.fold + (fun name (kind, data) acc -> + Printf.sprintf "%S (kind: %S): %S" name kind data :: acc) + tbl [] + in + let html = + body [] + [ + pre [] + [ txt (Printf.sprintf "{\n%s\n}" @@ String.concat "\n" data) ]; + ] + in + Response.make_string ~code:201 @@ Ok (to_string_top html)) + let () = let port_ = ref 8080 in let j = ref 32 in @@ -198,6 +251,8 @@ let () = ~dir_behavior:Tiny_httpd.Dir.Index_or_lists ()) ~vfs:Vfs.vfs ~prefix:"vfs"; + setup_upload server; + (* main page *) Server.add_route_handler server Route.(return) @@ -267,6 +322,34 @@ let () = txt " (POST) to log out"; ]; ]; + li [] + [ + form + [ + A.action "/upload"; + A.enctype "multipart/form-data"; + A.target "_self"; + A.method_ "POST"; + ] + [ + label [] [ txt "my beautiful form" ]; + input [ A.type_ "file"; A.name "file1" ]; + input [ A.type_ "file"; A.name "file2" ]; + input + [ + A.type_ "text"; + A.name "a"; + A.placeholder "text A"; + ]; + input + [ + A.type_ "text"; + A.name "b"; + A.placeholder "text B"; + ]; + input [ A.type_ "submit" ]; + ]; + ]; ]; ]; ]