1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283(* This file is part of Dream, released under the MIT license. See
LICENSE.md for details, or visit https://github.com/aantron/dream.
Copyright 2021 Anton Bachin *)moduleDream=Dream__pure.Inmost(* TODO Not at all efficient; can at least stream the file, maybe even cache. *)(* TODO Also mind newlines on Windows. *)(* TODO NOTE Using Lwt_io because it has a nice "read the whole thing"
function. *)letdefault_loaderlocal_rootpath_=letfile=Filename.concatlocal_rootpathinLwt.catch(fun()->Lwt_io.(with_file~mode:Inputfile)(funchannel->Lwt_io.readchannel|>Lwt.mapDream.response))(fun_exn->Dream.empty`Not_Found)(* TODO Add ETag handling. *)(* TODO Add automatic Content-Type handling. *)(* TODO Add Content-Length handling? *)(* TODO Support HEAD requests? *)(* The path must:
- Not have any .. or . components.
- Not have any empty components. This should not be possible in Dream except
for the last component, which, if empty, indicates a directory. We still
check all components for robustness' sake.
- Not be empty.
- Not have the prefix /. Dream's path function generates a path with such a
prefix, with the meaning that it is the site root. We remove that. The
remaining path must not be an absolute path. *)(* TODO On Windows, should we also check for \ and drive letters? *)(* TODO Not an efficient implementation at the moment. *)(* TODO It may be better to convert Dream's string list to a path first and then
re-parse it, to avoid any potential issues with nested / due to any bugs that
may be introduced. *)letvalidate_pathrequest=letpath=Dream.pathrequestinlethas_dot=List.exists((=)Filename.current_dir_name)pathinlethas_dotdot=List.exists((=)Filename.parent_dir_name)pathinlethas_empty=List.exists((=)"")pathinletis_empty=path=[]inifhas_dot||has_dotdot||has_empty||is_emptythenNoneelseletpath=String.concatFilename.dir_seppathinifFilename.is_relativepaththenSomepathelseNoneletstatic?(loader=default_loader)local_root=funrequest->ifnot@@Dream.methods_equal(Dream.method_request)`GETthenDream.empty`Not_Foundelsematchvalidate_pathrequestwith|None->Dream.empty`Not_Found|Somepath->let%lwtresponse=loaderlocal_rootpathrequestin(* TODO Can use a concise helper here. *)letresponse=ifDream.has_header"Content-Type"responsethenresponseelseDream.add_header"Content-Type"(Magic_mime.lookuppath)responseinLwt.returnresponse