Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file staticmod.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298(* Ocsigen
* http://www.ocsigen.org
* Module staticmod.ml
* Copyright (C) 2005 Vincent Balat
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)openLwt.InfixmodulePcre=Re.Pcreletname="staticmod"letsection=Lwt_log.Section.make"ocsigen:ext:staticmod"exceptionNot_concerned(* Structures describing the static pages a each virtual server *)(* A static site is either an entire directory served unconditionally,
or a more elaborate redirection based on regexpes and http error
codes. See the web documentation of staticmod for detail *)typestatic_site_kind=|Dirofstring(* Serves an entire directory *)|Regexpofregexp_siteandregexp_site={source_regexp:Pcre.regexp;dest:Ocsigen_extensions.ud_string;http_status_filter:Pcre.regexpoption;root_checks:Ocsigen_extensions.ud_stringoption}(* Finding files *)(* Does the http status code returned for the page match the given filter ? *)lethttp_status_matchstatus_filterstatus=matchstatus_filterwith|None->true|Somer->Ocsigen_lib.Netstring_pcre.string_matchr(string_of_intCohttp.Code.(code_of_status(status:>status_code)))0<>None(* Checks that the path specified in a userconf is correct.
Currently, we check that the path does not contain ".." *)letcorrect_user_local_file=letregexp=Ocsigen_lib.Netstring_pcre.regexp"(/\\.\\./)|(/\\.\\.$)"infunpath->tryignore(Ocsigen_lib.Netstring_pcre.search_forwardregexppath0);falsewithNot_found->true(* Find the local file corresponding to [path] in the static site [dir],
with [err] as the current http status (in case [dir] is a filter).
Raises [Not_Concerned] if [dir] does not match, or returns
- a boolean indicating that [dir] is an error handler
- the local file
If the parameter [usermode] is true, we check that the path
is valid.
*)letfind_static_page~request~usermode~dir~(err:Cohttp.Code.status)~pathstring=letstatus_filter,filename,root=matchdirwith|Dird->((false,Filename.concatdpathstring,matchusermodewith|None->Somed|Some{Ocsigen_extensions.localfiles_root}->Somelocalfiles_root))|Regexp{source_regexp=source;dest;http_status_filter=status_filter;root_checks=rc}whenhttp_status_matchstatus_filtererr->letstatus_filter=status_filter<>Noneandfile=matchOcsigen_lib.Netstring_pcre.string_matchsourcepathstring0with|None->raiseNot_concerned|Some_->Ocsigen_extensions.replace_user_dirsourcedestpathstringandroot_checks=matchrc,usermodewith|None,Some{Ocsigen_extensions.localfiles_root=r}->Somer|Some_,Some_->raise(Ocsigen_extensions.Error_in_user_config_file"Staticmod: cannot specify option 'root' in user configuration files")|None,None->None|Somerc,None->Some(Ocsigen_extensions.replace_user_dirsourcercpathstring)instatus_filter,file,root_checks|_->raiseNot_concernedinifusermode=None||correct_user_local_filefilenamethen(status_filter,Ocsigen_local_files.resolve?no_check_for:root~request~filename())elseraise(Ocsigen_extensions.Error_in_user_config_file"Staticmod: cannot use '..' in user paths")letgen~usermode?cachedir=function|Ocsigen_extensions.Req_found_->Lwt.returnOcsigen_extensions.Ext_do_nothing|Ocsigen_extensions.Req_not_found(err,({Ocsigen_extensions.request_info;_}asrequest))->lettry_block()=Lwt_log.ign_info~section"Is it a static file?";letstatus_filter,page=letpathstring=Ocsigen_lib.Url.string_of_url_path~encode:false(Ocsigen_request.sub_pathrequest_info)infind_static_page~request~usermode~dir~err~pathstringinletfname=matchpagewith|Ocsigen_local_files.RFilefname->fname|Ocsigen_local_files.RDir_->failwith"FIXME: staticmod dirs not implemented"inCohttp_lwt_unix.Server.respond_file~fname()>>=funanswer->letanswer=Ocsigen_response.of_cohttpanswerinletanswer=ifnotstatus_filterthenanswerelseOcsigen_response.set_statusanswererrinletanswer=matchcachewith|None->answer|Someduration->letcache_control,expires=ifduration=0then"no-cache","0"else("max-age="^string_of_intduration,Ocsigen_lib.Date.to_string(Unix.time()+.float_of_intduration))inOcsigen_response.replace_headersanswer[Ocsigen_header.Name.cache_control,cache_control;Ocsigen_header.Name.expires,expires]inLwt.return(Ocsigen_extensions.Ext_found(fun()->Lwt.returnanswer))andcatch_block=function|Ocsigen_local_files.Failed_403->Lwt.return(Ocsigen_extensions.Ext_next`Forbidden)(* XXX We should try to leave an information about this error
for later *)|Ocsigen_local_files.NotReadableDirectory->Lwt.return(Ocsigen_extensions.Ext_nexterr)|Ocsigen_extensions.NoSuchUser|Ocsigen_extensions.Not_concerned|Ocsigen_local_files.Failed_404->Lwt.return(Ocsigen_extensions.Ext_nexterr)|e->Lwt.faileinLwt.catchtry_blockcatch_block(*****************************************************************************)(** Parsing of config file *)(* In userconf modes, paths must be relative to the root of the userconf config *)letrewrite_local_pathuserconfpath=matchuserconfwith|None->path|Some{Ocsigen_extensions.localfiles_root}->localfiles_root^"/"^pathtypeoptions={opt_dir:stringoption;opt_regexp:Pcre.regexpoption;opt_code:Pcre.regexpoption;opt_dest:Ocsigen_extensions.ud_stringoption;opt_root_checks:Ocsigen_extensions.ud_stringoption;opt_cache:intoption}letkinddirregexpcodedestroot_checks=matchdir,regexp,code,dest,root_checkswith|None,None,None,_,_->Ocsigen_extensions.badconfig"Missing attribute dir, regexp, or code for <static>"|Somed,None,None,None,None->Dir(Ocsigen_lib.Url.remove_end_slashd)|None,Somer,code,Somet,rc->Regexp{source_regexp=r;dest=t;http_status_filter=code;root_checks=rc}|None,None,(Some_ascode),Somet,None->Regexp{dest=t;http_status_filter=code;root_checks=None;source_regexp=Ocsigen_lib.Netstring_pcre.regexp"^.*$"}|_->Ocsigen_extensions.badconfig"Wrong attributes for <static>"letparse_configuserconf_:Ocsigen_extensions.parse_config_aux=fun___element->letopt=ref{opt_dir=None;opt_regexp=None;opt_code=None;opt_dest=None;opt_root_checks=None;opt_cache=None}inOcsigen_extensions.(Configuration.process_element~in_tag:"host"~other_elements:(funt__->raise(Bad_config_tag_for_extensiont))~elements:[Configuration.element~name:"static"~attributes:[Configuration.attribute~name:"dir"(funs->opt:={!optwithopt_dir=Some(rewrite_local_pathuserconfs)});Configuration.attribute~name:"regexp"(funs->lets=tryOcsigen_lib.Netstring_pcre.regexp("^"^s^"$")withRe.Pcre.Parse_error|Re.Pcre.Not_supported->badconfig"Bad regexp \"%s\" in <static regexp=\"...\" />"sinopt:={!optwithopt_regexp=Somes});Configuration.attribute~name:"code"(funs->letc=tryOcsigen_lib.Netstring_pcre.regexp("^"^s^"$")withRe.Pcre.Parse_error|Re.Pcre.Not_supported->badconfig"Bad regexp \"%s\" in <static code=\"...\" />"sinopt:={!optwithopt_code=Somec});Configuration.attribute~name:"dest"(funs->lets=Some(parse_user_dir(rewrite_local_pathuserconfs))inopt:={!optwithopt_dest=s});Configuration.attribute~name:"root"(funs->lets=Some(parse_user_dirs)inopt:={!optwithopt_root_checks=s});Configuration.attribute~name:"cache"(funs->letduration=matchswith|"no"->0|s->(tryint_of_stringswithFailure_->badconfig"Bad integer \"%s\" in <static cache=\"...\" />"s)inopt:={!optwithopt_cache=Someduration})]()]element);gen~usermode:userconf?cache:!opt.opt_cache@@kind!opt.opt_dir!opt.opt_regexp!opt.opt_code!opt.opt_dest!opt.opt_root_checkslet()=Ocsigen_extensions.register~name~fun_site:(funpath_->parse_configpath)()(* TODO: fix names and types, preprocess as we do for XML *)(* Registration for static linking: *)letpreprocesss="^"^s^"$"letrun?dir?regexp?dest?code?cache?root()=letkind=kinddir(Ocsigen_lib.Option.map(funx->Pcre.regexp(preprocessx))regexp)(Ocsigen_lib.Option.map(funx->Pcre.regexp(preprocessx))code)(Ocsigen_lib.Option.map(funx->Ocsigen_extensions.parse_user_dir(rewrite_local_pathNonex))dest)(Ocsigen_lib.Option.map(funx->Ocsigen_extensions.parse_user_dir(rewrite_local_pathNonex))root)infun___->gen~usermode:None?cachekind