123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436# 1 "src/lib/eliom_route_base.shared.ml"(* Ocsigen
* http://www.ocsigen.org
* Module eliommod_services.ml
* Copyright (C) 2007 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.
*)openEliom_libopenLwtletsection=Lwt_log.Section.make"eliom:service"moduletypePARAM=sigtypesite_datatypeinfotypeparamstyperesultvalsess_info_of_info:info->Eliom_common.sess_infovalmeth_of_info:info->Eliom_common.methvalsubpath_of_info:info->stringlistvalmake_params:site_data->info->stringlistoption->Eliom_common.full_state_nameoption->paramsvalhandle_directory:info->resultLwt.tvalget_number_of_reloads:unit->intmoduleNode:sigtypetvalup:t->unitvalremove:t->unitendmoduleTable:sigtypetvalempty:unit->tvaladd:Eliom_common.page_table_key->Node.toption*(params,result)Eliom_common.servicelist->t->tvalfind:Eliom_common.page_table_key->t->Node.toption*(params,result)Eliom_common.servicelistvalremove:Eliom_common.page_table_key->t->tendmoduleContainer:sigtypetvalset_contains_timeout:t->bool->unitvaldlist_add:?sp:Eliom_common.server_params->t->(Table.tref*Eliom_common.page_table_key,Eliom_common.na_key_serv)Eliom_lib.leftright->Node.tvalget:t->(int*int*Table.tEliom_common.dircontentref)listvalset:t->(int*int*Table.tEliom_common.dircontentref)list->unitendendmoduleMake(P:PARAM)=structletfind_page_tablenosuffixversionnow(pagetableref:P.Table.tref)fullsessname(site_data:P.site_data)(info:P.info)(urlsuffix:_option)k:P.resultLwt.t=letsp=P.make_paramssite_datainfourlsuffixfullsessnameinLwt.catch(fun()->Lwt.return(P.Table.findk!pagetableref))(functionNot_found->failEliom_common.Eliom_404|e->faile)>>=fun(node,l)->letrecauxtoremove=function|[]->Lwt.return(Eliom_common.NotfoundEliom_common.Eliom_Wrong_parameter,[])|({Eliom_common.s_max_use;s_expire;s_f;_}asa)::l->(matchs_expirewith|Some(_,e)when!e<now->(* Service expired. Removing it. *)Lwt_log.ign_info~section"Service expired. Removing it";auxtoremovel>>=fun(r,toremove)->Lwt.return(r,a::toremove)|_->catch(fun()->Lwt_log.ign_info~section"Trying a service";s_fnosuffixversionsp>>=funp->(* warning: the list ll may change during funct
if funct register something on the same URL!! *)Lwt_log.ign_info~section"Page found and generated successfully";(* If this is an anonymous coservice,
we place it at the top of the dlist
(limitation of number of coservices) *)(matchnodewithNone->()|Somenode->P.Node.upnode);(* We update the expiration date *)(matchs_expirewith|Some(timeout,e)->e:=timeout+.now|None->());letnewtoremove=matchs_max_usewith|Somes_max_use->ifs_max_use=1thena::toremoveelse(a.s_max_use<-Some(s_max_use-1);toremove)|_->toremoveinLwt.return(Eliom_common.Foundp,newtoremove))(function|Eliom_common.Eliom_Wrong_parameter->auxtoremovel>>=fun(r,toremove)->Lwt.return(r,toremove)|e->Lwt.return(Eliom_common.Notfounde,toremove)))inaux[]l>>=fun(r,toremove)->(matchnode,toremovewith|_,[]->()|Somenode,_->(* it is an anonymous coservice that has expired.
We remove it form the dlist.
This will do the removal from this table
automatically.
Note that in that case, toremove has length 1
(like the initial list l).
*)P.Node.removenode|None,_->((* removing manually *)trylet_,l=P.Table.findk!pagetablerefandnewptr=P.Table.removek!pagetablerefin(* We do find once again because it may have changed! *)letnewlist=List.fold_left(funla->List.remove_first_if_any_qal)(* physical equality! *)ltoremoveinpagetableref:=matchnewlistwith|[]->newptr|newlist->P.Table.addk(None,newlist)newptrwithNot_found->()));matchrwith|Eliom_common.Foundr->Lwt.return(r:P.result)|Eliom_common.Notfounde->faileletremove_idservicesid=List.filter(fun{Eliom_common.s_id;_}->s_id<>id)servicesletfind_and_remove_idservicesid=letfound,l=letf(found,l)({Eliom_common.s_id;_}asx)=ifid=s_idthenSomex,lelsefound,x::linList.fold_leftf(None,[])servicesinmatchfoundwithSomefound->found,List.revl|None->raiseNot_foundletadd_page_tabletablesurl_acttrefkey({Eliom_common.s_id;s_expire;_}asservice)=letsp=Eliom_common.get_sp_option()in(matchs_expirewith|Some_->P.Container.set_contains_timeouttablestrue|_->());(* Duplicate registration forbidden in global table with same generation *)matchkeywith|{Eliom_common.key_state=Eliom_common.SAtt_anon_,_;key_meth=`Get}|{Eliom_common.key_state=_,Eliom_common.SAtt_anon_;key_meth=`Post|`Put|`Delete}->((* Anonymous coservice:
- only one for each key
- we add a node in the dlist to limit their number *)trylet(nodeopt,_),newt=P.Table.findkey!tref,P.Table.removekey!trefin(matchnodeoptwith|None->()(* should not occur *)|Somenode->P.Node.upnode);tref:=P.Table.addkey(nodeopt,[service])newtwithNot_found->letnode=P.Container.dlist_add?sptables(Left(tref,key))intref:=P.Table.addkey(Somenode,[service])!tref)|{Eliom_common.key_state=Eliom_common.SAtt_no,Eliom_common.SAtt_no;_}->(trylet_nodeopt,l=P.Table.findkey!trefandnewt=P.Table.removekey!trefin(* nodeopt should be None *)try(* verify that we haven't registered something similar *)let_,oldl=find_and_remove_idls_idin(* if there was an old version with the same id, we remove
it? *)ifsp=Nonethen(* but if there was already one with same generation, we
fail (if during initialisation) *)raise(Eliom_common.Eliom_duplicate_registration(Url.string_of_url_path~encode:falseurl_act))else(* We insert as last element so that services are tried
in registration order *)tref:=P.Table.addkey(None,oldl@[service])newtwithNot_found->tref:=P.Table.addkey(None,l@[service])newtwithNot_found->tref:=P.Table.addkey(None,[service])!tref)|_->(trylet_nodeopt,l=P.Table.findkey!trefandnewt=P.Table.removekey!trefinlet_,oldl=find_and_remove_idls_idin(* if there was an old version with the same id, we remove it *)tref:=P.Table.addkey(None,oldl@[service])newtwithNot_found->tref:=P.Table.addkey(None,[service])!tref)letremove_page_table__trefkeyid=(* Actually this does not remove empty directories.
But this will be done by the next service GC *)letnodeopt,l=P.Table.findkey!trefinmatchnodeoptwith|Somenode->(* In that case, l has size 1, and the id is correct,
because it is an anonymous coservice *)(*VVV the key is searched twice *)P.Node.removenode|None->(letnewt=P.Table.removekey!trefinmatchremove_idlidwith|[]->tref:=newt(* In that case, we must remove it, otherwise we get
"Wrong parameters" instead of "404 Not found" *)|newl->tref:=P.Table.addkey(None,newl)newt)letadd_dircontentdc(key,(elt:P.Table.tEliom_common.direltref))=matchdcwith|Eliom_common.Vide->Eliom_common.Table(String.Table.addkeyeltString.Table.empty)|Eliom_common.Tablet->Eliom_common.Table(String.Table.addkeyeltt)letfind_dircontentdck=matchdcwith|Eliom_common.Vide->raiseNot_found|Eliom_common.Tablet->String.Table.findktletadd_or_remove_serviceftablestableurl_actpage_table_keyva=letrecauxdircontentrefal=tryletdireltref=find_dircontent!dircontentrefainmatch!direltrefwith|Eliom_common.Dirdcr->search_page_table_refdcrl|Eliom_common.File_->raise(Eliom_common.Eliom_page_erasinga)withNot_found->letnewdcr=ref(Eliom_common.empty_dircontent())indircontentref:=add_dircontent!dircontentref(a,ref(Eliom_common.Dirnewdcr));search_page_table_refnewdcrlandsearch_page_table_refdircontentref=function|[]|[""]->search_page_table_refdircontentref[Eliom_common.defaultpagename]|[a]->(tryletdireltref=find_dircontent!dircontentrefainmatch!direltrefwith|Eliom_common.Dir_->raise(Eliom_common.Eliom_page_erasinga)|Eliom_common.Fileptr->ptrwithNot_found->letnewpagetableref=ref(P.Table.empty())indircontentref:=add_dircontent!dircontentref(a,ref(Eliom_common.Filenewpagetableref));newpagetableref)|""::l->search_page_table_refdircontentrefl|a::l->auxdircontentrefalinletpage_table_ref=search_page_table_reftableurl_actinftablesurl_actpage_table_refpage_table_keyvaletadd_serviceprioritytablesurl_actpage_table_keyva=letgeneration=P.get_number_of_reloads()inletrecfind_table=function|[]->lett=ref(Eliom_common.empty_dircontent())int,[generation,priority,t]|(g,p,t)::_aslwheng=generation&&p=priority->t,l|(g,p,_)::_aslwheng<generation||p<priority->lett=ref(Eliom_common.empty_dircontent())int,(generation,priority,t)::l|((g,p,_)asa)::lwheng=generation&&p>priority->lett,ll=find_tablelint,a::ll|_->assertfalseinlettable,new_table_services=find_table(P.Container.gettables)inP.Container.settablesnew_table_services;add_or_remove_serviceadd_page_tabletablestableurl_actpage_table_keyvaletremove_servicetablespathkunique_id=letrecaux=function|[]->()|(_,_,table)::l->(tryadd_or_remove_serviceremove_page_tabletablestablepathkunique_idwithNot_found->auxl)inaux(P.Container.gettables)exceptionExn1letfind_servicenowtablesfullsessnamesitedatainfo:P.resultLwt.t=letrecsearch_page_tabledircontent:_->P.resultLwt.t=letfindnosuffixversionpage_table_refsuffix=letsi=P.sess_info_of_infoinfoinfind_page_tablenosuffixversionnowpage_table_reffullsessnamesitedatainfosuffix{Eliom_common.key_state=(Eliom_common.att_key_serv_of_req(fstsi.Eliom_common.si_state_info),Eliom_common.att_key_serv_of_req(sndsi.Eliom_common.si_state_info));Eliom_common.key_meth=P.meth_of_infoinfo}inletauxal=letaa=matchawithNone->Eliom_common.defaultpagename|Someaa->aainLwt.catch(fun()->letdc=try!(find_dircontentdircontentaa)withNot_found->raiseExn1inmatchdcwith|Eliom_common.Dirdircontentref2->search_page_table!dircontentref2l|Eliom_common.Filepage_table_ref->(matchlwith|[]->findfalsepage_table_refNone|_->(* We have a file with suffix *)raiseEliom_common.Eliom_Wrong_parameter))(function|(Exn1|Eliom_common.Eliom_Wrong_parameter)ase->((* If no service matches, we try a suffix service *)trymatch!(tryfind_dircontentdircontentEliom_common.eliom_suffix_internal_namewithNot_found->raisee)with|Eliom_common.Dir_->Lwt.failExn1|Eliom_common.Filepage_table_ref->findfalsepage_table_ref(ifa=NonethenSome[]elseSome(aa::l))withe->Lwt.faile)|e->Lwt.faile)infunction|[]->(* It is a directory, without / at the end. We do a redirection. *)P.handle_directoryinfo|[""]->auxNone[]|[a]whena=Eliom_common.eliom_nosuffix_page->((* version without suffix of suffix service *)trymatch!(tryfind_dircontentdircontentEliom_common.eliom_suffix_internal_namewithNot_found->raiseExn1)with|Eliom_common.Dir_->Lwt.failExn1|Eliom_common.Filepage_table_ref->findtruepage_table_refNonewithe->Lwt.faile)(* | ""::l -> search_page_table dircontent l *)(* We do not remove "//" any more
because of optional suffixes *)|a::l->aux(Somea)linletsearch_by_priority_generationtablespath=(* New in 1.91: There is now one table for each pair
(generation, priority) *)List.fold_left(funprev(_prio,_gen,table)->Lwt.catch(fun()->prev)(function|Exn1|Eliom_common.Eliom_404|Eliom_common.Eliom_Wrong_parameter->search_page_table!tablepath|e->faile))(failExn1)tablesinLwt.catch(fun()->search_by_priority_generation(P.Container.gettables)(Url.change_empty_list(P.subpath_of_infoinfo)))(functionExn1->Lwt.failEliom_common.Eliom_404|e->Lwt.faile)end