|
1 | 1 | open Tiny_httpd_core |
2 | 2 | module Log = Tiny_httpd.Log |
| 3 | +module MFD = Tiny_httpd_multipart_form_data |
3 | 4 |
|
4 | 5 | let now_ = Unix.gettimeofday |
5 | 6 |
|
@@ -78,6 +79,58 @@ let setup_logging () = |
78 | 79 | Logs.set_reporter @@ Logs.format_reporter (); |
79 | 80 | Logs.set_level ~all:true (Some Logs.Debug) |
80 | 81 |
|
| 82 | +let setup_upload server : unit = |
| 83 | + Server.add_route_handler_stream ~meth:`POST server |
| 84 | + Route.(exact "upload" @/ return) |
| 85 | + (fun req -> |
| 86 | + let (`boundary boundary) = |
| 87 | + match MFD.parse_content_type req.headers with |
| 88 | + | Some b -> b |
| 89 | + | None -> Response.fail_raise ~code:400 "no boundary found" |
| 90 | + in |
| 91 | + |
| 92 | + let st = MFD.create ~boundary req.body in |
| 93 | + let tbl = Hashtbl.create 16 in |
| 94 | + let cur = ref "" in |
| 95 | + let cur_kind = ref "" in |
| 96 | + let buf = Buffer.create 16 in |
| 97 | + let rec loop () = |
| 98 | + match MFD.next st with |
| 99 | + | End_of_input -> |
| 100 | + if !cur <> "" then |
| 101 | + Hashtbl.add tbl !cur (!cur_kind, Buffer.contents buf) |
| 102 | + | Part headers -> |
| 103 | + if !cur <> "" then |
| 104 | + Hashtbl.add tbl !cur (!cur_kind, Buffer.contents buf); |
| 105 | + (match MFD.Content_disposition.parse headers with |
| 106 | + | Some { kind; name = Some name; filename = _ } -> |
| 107 | + cur := name; |
| 108 | + cur_kind := kind; |
| 109 | + Buffer.clear buf; |
| 110 | + loop () |
| 111 | + | _ -> Response.fail_raise ~code:400 "content disposition missing") |
| 112 | + | Read sl -> |
| 113 | + Buffer.add_subbytes buf sl.bytes sl.off sl.len; |
| 114 | + loop () |
| 115 | + in |
| 116 | + loop (); |
| 117 | + |
| 118 | + let open Tiny_httpd_html in |
| 119 | + let data = |
| 120 | + Hashtbl.fold |
| 121 | + (fun name (kind, data) acc -> |
| 122 | + Printf.sprintf "%S (kind: %S): %S" name kind data :: acc) |
| 123 | + tbl [] |
| 124 | + in |
| 125 | + let html = |
| 126 | + body [] |
| 127 | + [ |
| 128 | + pre [] |
| 129 | + [ txt (Printf.sprintf "{\n%s\n}" @@ String.concat "\n" data) ]; |
| 130 | + ] |
| 131 | + in |
| 132 | + Response.make_string ~code:201 @@ Ok (to_string_top html)) |
| 133 | + |
81 | 134 | let () = |
82 | 135 | let port_ = ref 8080 in |
83 | 136 | let j = ref 32 in |
@@ -198,6 +251,8 @@ let () = |
198 | 251 | ~dir_behavior:Tiny_httpd.Dir.Index_or_lists ()) |
199 | 252 | ~vfs:Vfs.vfs ~prefix:"vfs"; |
200 | 253 |
|
| 254 | + setup_upload server; |
| 255 | + |
201 | 256 | (* main page *) |
202 | 257 | Server.add_route_handler server |
203 | 258 | Route.(return) |
@@ -267,6 +322,34 @@ let () = |
267 | 322 | txt " (POST) to log out"; |
268 | 323 | ]; |
269 | 324 | ]; |
| 325 | + li [] |
| 326 | + [ |
| 327 | + form |
| 328 | + [ |
| 329 | + A.action "/upload"; |
| 330 | + A.enctype "multipart/form-data"; |
| 331 | + A.target "_self"; |
| 332 | + A.method_ "POST"; |
| 333 | + ] |
| 334 | + [ |
| 335 | + label [] [ txt "my beautiful form" ]; |
| 336 | + input [ A.type_ "file"; A.name "file1" ]; |
| 337 | + input [ A.type_ "file"; A.name "file2" ]; |
| 338 | + input |
| 339 | + [ |
| 340 | + A.type_ "text"; |
| 341 | + A.name "a"; |
| 342 | + A.placeholder "text A"; |
| 343 | + ]; |
| 344 | + input |
| 345 | + [ |
| 346 | + A.type_ "text"; |
| 347 | + A.name "b"; |
| 348 | + A.placeholder "text B"; |
| 349 | + ]; |
| 350 | + input [ A.type_ "submit" ]; |
| 351 | + ]; |
| 352 | + ]; |
270 | 353 | ]; |
271 | 354 | ]; |
272 | 355 | ] |
|
0 commit comments