123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996(* Ocsigen
* http://www.ocsigen.org
* Module eliommod.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.
*)(*****************************************************************************)(*****************************************************************************)(** Internal functions used by Eliom: *)(** Tables of services (global and session tables, *)(** persistent and volatile data tables) *)(** Store and load services *)(*****************************************************************************)(*****************************************************************************)openEliom_libopenOcsigen_extensions(****************************************************************************)letdefault_max_persistent_data_sessions_per_group=ref50letdefault_max_service_sessions_per_group=ref50letdefault_max_service_sessions_per_subnet=ref1000000letdefault_max_volatile_data_sessions_per_group=ref50letdefault_max_volatile_data_sessions_per_subnet=ref1000000letdefault_max_persistent_data_tab_sessions_per_group=ref50letdefault_max_service_tab_sessions_per_group=ref50letdefault_max_volatile_data_tab_sessions_per_group=ref50letdefault_secure_cookies=reffalseletdefault_application_script=ref(false,false)letdefault_cache_global_data=refNoneletdefault_html_content_type=refNoneletdefault_ignored_get_params=ref[]letdefault_ignored_post_params=ref[]letdefault_omitpersistentstorage=refNone(* Subnet defaults be large enough, because it must work behind a reverse proxy.
If 1 session takes 1000 bytes (data + tables etc),
1 million sessions take 1 GB.
If somebody opens 1000 sessions per second,
then it will take 1000 s (16 minutes) to reach 1000000.
It means that regular users will have their sessions closed
after 16 minutes of inactivity if they share their sub network with
someone doing an attack (or if the server is behind a proxy).
In any case, it is better to use session groups when possible.
For persistent session, there is a limitation per session group,
efficient only for small values.
But there is no limitation by subnet.
1 billion sessions take 1 TB.
If somebody opens 1000 sessions per second,
then it will take 1 million s (16000 minutes = 266 h = 11 days)
to reach 1TB.
*)letdefault_max_anonymous_services_per_subnet=ref500000letdefault_max_anonymous_services_per_session=ref1000letdefault_max_volatile_groups_per_site=ref1000000(*VVV value ??? *)moduleS=Hashtbl.Make(structtypet=Ocsigen_extensions.virtual_hosts*Eliom_lib.Url.pathletequal((vh1,u1):t)((vh2,u2):t)=Ocsigen_extensions.equal_virtual_hostsvh1vh2&&u1=u2lethash((vh,u):t)=Hashtbl.hash(Ocsigen_extensions.hash_virtual_hostsvh,u)end)letcreate_sitedata_auxsite_dirconfig_info=letdlist_table=Eliom_common.create_dlist_ip_table100andgroup_of_groups=Ocsigen_cache.Dlist.create!default_max_volatile_groups_per_siteinletsitedata={(* One dlist for each site? *)Eliom_common.servtimeout=None,None,[];datatimeout=None,None,[];perstimeout=None,None,[];site_value_table=Polytables.create();site_dir;(*VVV encode=false??? *)site_dir_string=Option.map(Eliom_lib.Url.string_of_url_path~encode:false)site_dir;config_info;default_links_xhr=Eliom_common.tenable_value~name:"default_links_xhr"true;global_services=Eliom_common.empty_tables!default_max_anonymous_services_per_subnetfalse;registered_scope_hierarchies=Eliom_common.Hier_set.empty;session_services=Eliommod_cookies.new_service_cookie_table();session_data=Eliommod_cookies.new_data_cookie_table();group_of_groups;remove_session_data=(fun_cookie->());not_bound_in_data_tables=(fun_cookie->true);exn_handler=Eliommod_pagegen.def_handler;unregistered_services=[];unregistered_na_services=[];max_service_sessions_per_group=!default_max_service_sessions_per_group,false;max_volatile_data_sessions_per_group=!default_max_volatile_data_sessions_per_group,false;max_persistent_data_sessions_per_group=Some!default_max_persistent_data_sessions_per_group,false;max_service_tab_sessions_per_group=!default_max_service_tab_sessions_per_group,false;max_volatile_data_tab_sessions_per_group=!default_max_volatile_data_tab_sessions_per_group,false;max_persistent_data_tab_sessions_per_group=Some!default_max_persistent_data_tab_sessions_per_group,false;max_service_sessions_per_subnet=!default_max_service_sessions_per_subnet,false;max_volatile_data_sessions_per_subnet=!default_max_volatile_data_sessions_per_subnet,false;max_anonymous_services_per_session=!default_max_anonymous_services_per_session,false;max_anonymous_services_per_subnet=!default_max_anonymous_services_per_subnet,false;secure_cookies=!default_secure_cookies;dlist_ip_table=dlist_table;ipv4mask=None,false;ipv6mask=None,false;application_script=!default_application_script;cache_global_data=!default_cache_global_data;html_content_type=!default_html_content_type;ignored_get_params=!default_ignored_get_params;ignored_post_params=!default_ignored_post_params;omitpersistentstorage=!default_omitpersistentstorage}inOcsigen_cache.Dlist.set_finaliser_after(funnode->(* Finaliser for the session groups *)(* See in eliommod_sessiongroups for the finaliser of sessions *)letfullbrowsersessgrp=Ocsigen_cache.Dlist.valuenodein(* When removing a group from the dlist, we must close it.
Actually, it must be the only way to close a group. *)(* This finaliser is almost identical to the finaliser for
other groups, defined in Eliommod_sessiongroups. *)(* First we close all browser sessions in the group, by
removing the group from its dlist: *)Eliommod_sessiongroups.Data.remove_groupfullbrowsersessgrp;(* Then we remove data from group tables: *)matchTuple3.thdfullbrowsersessgrpwith|Leftkey->(* iterate on all session data tables: *)sitedata.Eliom_common.remove_session_datakey|_->(* No group has been set. No group table.
Data associated to default (automatic) groups
is removed when closing associated sessions.
*)())group_of_groups;Eliommod_gc.service_session_gcsitedata;Eliommod_gc.data_session_gcsitedata;Eliommod_gc.persistent_session_gcsitedata;sitedata(** We associate to each service a function server_params -> page *)letcreate_sitedata,update_sitedata=(* We want to keep the old site data even if we reload the server.
To do that, we keep the site data in a table *)lett=S.create5in((funhostsite_dirconfig_info->letkey=host,site_dirintryS.findtkeywithNot_found->letsitedata=create_sitedata_aux(Somesite_dir)(Someconfig_info)inS.addtkeysitedata;sitedata),funhostsite_dirsitedata->letkey=host,site_dirinS.replacetkeysitedata)(*****************************************************************************)(* Session service table *)(****************************************************************************)(****************************************************************************)(****************************************************************************)(* The following is common to global config and site config *)letparse_eliom_option(set_volatile_timeout,set_data_timeout,set_service_timeout,set_persistent_timeout,set_max_service_sessions_per_group,set_max_service_sessions_per_subnet,set_max_data_sessions_per_group,set_max_data_sessions_per_subnet,set_max_persistent_sessions_per_group,set_max_service_tab_sessions_per_group,set_max_data_tab_sessions_per_group,set_max_persistent_tab_sessions_per_group,set_max_services_per_session,set_max_services_per_subnet,set_max_volatile_groups_per_site,set_secure_cookies,set_ipv4mask,set_ipv6mask,set_application_script,set_global_data_caching,set_html_content_type,set_ignored_get_params,set_ignored_post_params,set_omitpersistentstorage)=letparse_timeout_attrstnattrs=letrecaux((v,sn,ct)asres)=function|[]->res|("value",s)::l->aux(Somes,sn,ct)l|("hierarchyname",sn)::l->aux(v,Somesn,ct)l|("level","session")::l|("level","browser")::l->aux(v,sn,`Session)l|("level","clientprocess")::l|("level","process")::l|("level","tab")::l->aux(v,sn,`Client_process)l|("level",_)::_->raise(Error_in_config_file("Eliom: Wrong attribute value for level in "^tn^" tag"))|_->raise(Error_in_config_file("Eliom: Wrong attribute name for "^tn^" tag"))inleta,sn,ct=aux(None,None,`Session)attrsinleta=matchawith|None->raise(Error_in_config_file("Eliom: Missing value for "^tn^" tag"))|Some"infinity"->None|Somea->(trySome(float_of_stringa)withFailure_->raise(Error_in_config_file("Eliom: Wrong attribute value for "^tn^" tag")))inletparse_scope_hierarchy=function|""->Eliom_common_base.Default_ref_hier|swhenString.lowercase_asciis="default"->Eliom_common_base.Default_ref_hier|swhenString.lowercase_asciis="comet"->Eliom_common_base.Default_comet_hier|s->Eliom_common_base.User_hiersina,Eliom_lib.Option.mapparse_scope_hierarchysn,ctinletconvert_attrtagfv=tryfvwithInvalid_argument_->raise(Error_in_config_file(Printf.sprintf"Eliom: Wrong attribute value for tag %s in element cacheglobaldata"tag))inletparse_application_script_attrsattrs=letrecauxdeferasyncattrs=matchattrswith|[]->defer,async|("defer",v)::rem->aux(convert_attr"defer"bool_of_stringv)asyncrem|("async",v)::rem->auxdefer(convert_attr"async"bool_of_stringv)rem|(tag,_)::_->raise(Error_in_config_file(Printf.sprintf"Eliom: attribute %s not allowed in element applicationscript"tag))inauxfalsefalseattrsinletparse_global_data_caching_attrsattrs=letrecauxpathmax_ageattrs=matchattrswith|[]->Some(path,max_age)|("path",p)::rem->aux(Eliom_lib.Url.split_pathp)max_agerem|("cache",v)::rem->auxpath(convert_attr"cache"int_of_stringv)rem|(tag,_)::_->raise(Error_in_config_file(Printf.sprintf"Eliom: attribute %s not allowed in element cacheglobaldata"tag))inaux[]0attrsinfunction|Xml.Element("volatiletimeout",attrs,[])->lett,snoo,ct=parse_timeout_attrs"volatiletimeout"attrsinset_volatile_timeoutctsnoo(t:floatoption)|Xml.Element("datatimeout",attrs,[])->lett,snoo,ct=parse_timeout_attrs"datatimeout"attrsinset_data_timeoutctsnoot|Xml.Element("servicetimeout",attrs,[])->lett,snoo,ct=parse_timeout_attrs"servicetimeout"attrsinset_service_timeoutctsnoot|Xml.Element("persistenttimeout",attrs,[])->lett,snoo,ct=parse_timeout_attrs"persistenttimeout"attrsinset_persistent_timeoutctsnoot|Xml.Element("maxvolatilesessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_service_sessions_per_groupi;set_max_data_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxvolatilesessionspergroup tag"))|Xml.Element("maxservicesessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_service_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxservicesessionspergroup tag"))|Xml.Element("maxdatasessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_data_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxdatasessionspergroup tag"))|Xml.Element("maxvolatilesessionspersubnet",[("value",v)],[])->(tryleti=int_of_stringvinset_max_service_sessions_per_subneti;set_max_data_sessions_per_subnetiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxvolatilesessionspersubnet tag"))|Xml.Element("maxservicesessionspersubnet",[("value",v)],[])->(tryleti=int_of_stringvinset_max_service_sessions_per_subnetiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxservicesessionspersubnet tag"))|Xml.Element("maxdatasessionspersubnet",[("value",v)],[])->(tryleti=int_of_stringvinset_max_data_sessions_per_subnetiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxdatasessionspersubnet tag"))|Xml.Element("maxpersistentsessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_persistent_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxpersistentsessionspergroup tag"))|Xml.Element("maxvolatiletabsessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_service_tab_sessions_per_groupi;set_max_data_tab_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxvolatiletabsessionspergroup tag"))|Xml.Element("maxservicetabsessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_service_tab_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxservicetabsessionspergroup tag"))|Xml.Element("maxdatatabsessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_data_tab_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxdatatabsessionspergroup tag"))|Xml.Element("maxpersistenttabsessionspergroup",[("value",v)],[])->(tryleti=int_of_stringvinset_max_persistent_tab_sessions_per_groupiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxpersistenttabsessionspergroup tag"))|Xml.Element("maxanonymouscoservicespersession",[("value",v)],[])->(tryleti=int_of_stringvinset_max_services_per_sessioniwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxanonymouscoservicespersession tag"))|Xml.Element("maxanonymouscoservicespersubnet",[("value",v)],[])->(tryleti=int_of_stringvinset_max_services_per_subnetiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxanonymouscoservicespersubnet tag"))|Xml.Element("maxvolatilegroupspersite",[("value",v)],[])->(tryleti=int_of_stringvinset_max_volatile_groups_per_siteiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for maxvolatilegroupspersite tag"))|Xml.Element("securecookies",[("value",v)],[])->(tryleti=matchvwith"true"->true|"false"->false|_->failwith""inset_secure_cookiesiwithFailure_->raise(Error_in_config_file"Eliom: Wrong attribute value for securecookies tag"))|Xml.Element("ipv4subnetmask",[("value",v)],[])->(tryletmask=int_of_stringvinset_ipv4maskmaskwith_->raise(Error_in_config_file"Eliom: Wrong attribute value for ipv4subnetmask tag"))|Xml.Element("ipv6subnetmask",[("value",v)],[])->(tryletmask=int_of_stringvinset_ipv6maskmaskwith_->raise(Error_in_config_file"Eliom: Wrong attribute value for ipv6subnetmask tag"))|Xml.Element("applicationscript",attrs,[])->set_application_script(parse_application_script_attrsattrs)|Xml.Element("cacheglobaldata",attrs,[])->set_global_data_caching(parse_global_data_caching_attrsattrs)|Xml.Element("htmlcontenttype",[("value",v)],[])->set_html_content_typev|Xml.Element("ignoredgetparams",[("regexp",v)],[])->letre=Re.seq[Re.start;Re.Pcre.rev;Re.stop]|>Re.compileinset_ignored_get_params(v,re)|Xml.Element("ignoredpostparams",[("regexp",v)],[])->letre=Re.seq[Re.start;Re.Pcre.rev;Re.stop]|>Re.compileinset_ignored_post_params(v,re)|Xml.Element("omitpersistentstorage",attrs,tags)->assert(attrs=[]);letparse_rule=function|Xml.Element("header",attrs,tags)->assert(tags=[]);letattr_name,attr_value=matchattrswith[a]->a|_->assertfalseinletheader_name=Ocsigen_header.Name.of_stringattr_nameinletheader_regexp=Re.compile@@Re.Pcre.reattr_valueinEliom_common.HeaderRule(header_name,header_regexp)|_->assertfalseinletrules=List.mapparse_ruletagsinset_omitpersistentstorage(Somerules)|Xml.Element(s,_,_)->raise(Error_in_config_file("Unexpected content <"^s^"> inside eliom config"))|_->raise(Error_in_config_file"Unexpected content inside eliom config")letparse_eliom_optionsfl=letrecauxrest=function|[]->rest|e::l->(tryparse_eliom_optionfe;auxrestlwithError_in_config_file_->aux(e::rest)l)inList.rev(aux[]l)(*****************************************************************************)(** Parsing global configuration for Eliommod: *)letrecparse_global_config=function|[]->()|Xml.Element("sessiongcfrequency",[("value",s)],_)::ll->(trylett=float_of_stringsinEliommod_gc.set_servicesessiongcfrequency(Somet);Eliommod_gc.set_datasessiongcfrequency(Somet)withFailure_->ifs="infinity"then(Eliommod_gc.set_servicesessiongcfrequencyNone;Eliommod_gc.set_datasessiongcfrequencyNone)elseraise(Error_in_config_file"Eliom: Wrong value for <sessiongcfrequency>"));parse_global_configll|Xml.Element("servicesessiongcfrequency",[("value",s)],_)::ll->(tryEliommod_gc.set_servicesessiongcfrequency(Some(float_of_strings))withFailure_->ifs="infinity"thenEliommod_gc.set_servicesessiongcfrequencyNoneelseraise(Error_in_config_file"Eliom: Wrong value for <servicesessiongcfrequency>"));parse_global_configll|Xml.Element("datasessiongcfrequency",[("value",s)],_)::ll->(tryEliommod_gc.set_datasessiongcfrequency(Some(float_of_strings))withFailure_->ifs="infinity"thenEliommod_gc.set_datasessiongcfrequencyNoneelseraise(Error_in_config_file"Eliom: Wrong value for <datasessiongcfrequency>"));parse_global_configll|Xml.Element("persistentsessiongcfrequency",[("value",s)],_)::ll->(tryEliommod_gc.set_persistentsessiongcfrequency(Some(float_of_strings))withFailure_->ifs="infinity"thenEliommod_gc.set_persistentsessiongcfrequencyNoneelseraise(Error_in_config_file"Eliom: Wrong value for <persistentsessiongcfrequency>"));parse_global_configll|e::ll->parse_eliom_option((functshm->Eliommod_timeouts.set_default?scope_hierarchy:sh`Datactm;Eliommod_timeouts.set_default?scope_hierarchy:sh`Servicectm),(functsh->Eliommod_timeouts.set_default?scope_hierarchy:sh`Datact),(functsh->Eliommod_timeouts.set_default?scope_hierarchy:sh`Servicect),(functsh->Eliommod_timeouts.set_default?scope_hierarchy:sh`Persistentct),(funv->default_max_service_sessions_per_group:=v),(funv->default_max_service_sessions_per_subnet:=v),(funv->default_max_volatile_data_sessions_per_group:=v),(funv->default_max_volatile_data_sessions_per_subnet:=v),(funv->default_max_persistent_data_sessions_per_group:=v),(funv->default_max_service_tab_sessions_per_group:=v),(funv->default_max_volatile_data_tab_sessions_per_group:=v),(funv->default_max_persistent_data_tab_sessions_per_group:=v),(funv->default_max_anonymous_services_per_session:=v),(funv->default_max_anonymous_services_per_subnet:=v),(funv->default_max_volatile_groups_per_site:=v),(funv->default_secure_cookies:=v),(funv->Eliom_common.ipv4mask:=v),(funv->Eliom_common.ipv6mask:=v),(funv->default_application_script:=v),(funv->default_cache_global_data:=v),(funv->default_html_content_type:=Somev),(funregexp->default_ignored_get_params:=regexp::!default_ignored_get_params),(funregexp->default_ignored_post_params:=regexp::!default_ignored_post_params),funv->default_omitpersistentstorage:=v)e;parse_global_configll(*****************************************************************************)letexception_during_eliommodule_loading=reffalse(** Function to be called at the end of the initialisation phase *)letend_init()=if!exception_during_eliommodule_loadingthen(* An eliom module failed with an exception. We do not check
for the missing services, so that the exception can be correctly
propagated by Ocsigen_extensions *)()elsetryEliom_common.verify_all_registered(Eliom_common.get_current_sitedata());Eliom_common.end_current_sitedata()withEliom_common.Eliom_site_information_not_available_->()(*VVV The "try with" looks like a hack:
end_init is called even for user config files ... but in that case,
current_sitedata is not set ...
It would be better to avoid calling end_init for user config files. *)(** Function that will handle exceptions during the initialisation phase *)lethandle_init_exn=function|Eliom_common.Eliom_error_while_loading_sites->s|Eliom_common.Eliom_duplicate_registrations->"Eliom: Duplicate registration of service \""^s^"\". Please correct the module."|Eliom_common.Eliom_there_are_unregistered_services(s,l1,l2)->"Eliom: in site /"^Url.string_of_url_path~encode:falses^" - "^(matchl1with|[]->""|[a]->"One service or coservice has not been registered on URL /"^Url.string_of_url_path~encode:falsea^". "|a::ll->letstring_of=Url.string_of_url_path~encode:falsein"Some services or coservices have not been registered on URLs: "^List.fold_left(funbegv->beg^", /"^string_ofv)("/"^string_ofa)ll^". ")^(matchl2with|[]->""|[Eliom_common.SNa_get'_]->"One non-attached GET coservice has not been registered."|[Eliom_common.SNa_post'_]->"One non-attached POST coservice has not been registered."|[Eliom_common.SNa_get_a]->"The non-attached GET service \""^a^"\" has not been registered."|[Eliom_common.SNa_post_a]->"The non-attached POST service \""^a^"\" has not been registered."|a::ll->letstring_of=function|Eliom_common.SNa_void_keep|Eliom_common.SNa_void_dontkeep|Eliom_common.SNa_no->assertfalse|Eliom_common.SNa_get'_->"<GET coservice>"|Eliom_common.SNa_get_n->n^" (GET)"|Eliom_common.SNa_post'_->"<POST coservice>"|Eliom_common.SNa_post_n->n^" (POST)"|Eliom_common.SNa_get_csrf_safe_->" <GET CSRF-safe coservice>"|Eliom_common.SNa_post_csrf_safe_->"<POST CSRF-safe coservice>"in"Some non-attached services or coservices have not been registered: "^List.fold_left(funbegv->beg^", "^string_ofv)(string_ofa)ll^".")^"\nPlease correct your modules and make sure you have linked in all the modules..."|Eliom_common.Eliom_site_information_not_availablef->"Eliom: Bad use of function \""^f^"\". Must be used only during site initialisation phase (or, sometimes, also during request)."|Eliom_common.Eliom_page_erasings->"Eliom: You cannot create a page or directory here. "^s^" already exists. Please correct your modules."|e->raisee(*****************************************************************************)(** Module loading *)letget_sitedata=letr=refString_map.emptyinfunname->tryString_map.findname!rwithNot_found->letsitedata=create_sitedata_auxNoneNoneinr:=String_map.addnamesitedata!r;sitedataletupdate_sitedataappvhsite_dirconf_info=letsitedata=get_sitedataappinsitedata.Eliom_common.site_dir<-Somesite_dir;sitedata.Eliom_common.site_dir_string<-Some(Eliom_lib.Url.string_of_url_path~encode:falsesite_dir);sitedata.Eliom_common.config_info<-Someconf_info;update_sitedatavhsite_dirsitedata;sitedatalet_=Eliom_common.absolute_change_sitedata(get_sitedata(Eliom_common.get_app_name()))letset_app_names=Eliom_common.current_app_name:=s;Eliom_common.absolute_change_sitedata(get_sitedata(Eliom_common.get_app_name()))letsite_init_ref=ref[](** Register function for evaluation at site initialisation *)letregister_site_inite=site_init_ref:=e::!site_init_refletconfig=refNone(* None means no config file (static linking) *)letconfig_in_tag=ref""(* the parent tag of the currently handled tag *)typemodule_to_load=Filesofstringlist|Nameofstringletsite_initfirstmodule=if!firstmodulethen((* I want to be able to define global client values during that phase: *)Eliom_syntax.set_globaltrue;List.iter(funf->f())!site_init_ref;Eliom_syntax.set_globalfalse;firstmodule:=false)letload_eliom_module_sitedatacmo_or_nameparent_tagcontent=letpreload()=config:=Somecontent;config_in_tag:=parent_taginletpostload()=config:=Some[]intrymatchcmo_or_namewith|Filescmo->Ocsigen_loader.loadfilespreloadpostloadtruecmo|Namename->Ocsigen_loader.init_modulepreloadpostloadtruenamewithOcsigen_loader.Dynlink_error(n,e)->raise(Eliom_common.Eliom_error_while_loading_site(Printf.sprintf"Eliom: while loading %s: %s"n(tryhandle_init_exnewith|Dynlink.Errorerr->Dynlink.error_messageerr|e->Printexc.to_stringe)))(*****************************************************************************)(* If page has already been generated becauise there are several <eliom>
tags in the same site:
*)letgen_nothing()_=Lwt.returnOcsigen_extensions.Ext_do_nothing(*****************************************************************************)letdefault_module_action_=failwith"default_module_action"letset_timeout(f:?full_st_name:Eliom_common.full_state_name->?cookie_level:[<Eliom_common.cookie_level]->recompute_expdates:bool->bool(* override configfile *)->bool(* from config file *)->Eliom_common.sitedata->floatoption->unit)sitedatacookie_typestate_hierv=letmake_full_st_namesecurestate_hier=letscope=matchcookie_typewith|`Session->`Sessionstate_hier|`Client_process->`Client_processstate_hierinEliom_common.make_full_state_name2(Eliom_common.get_site_dir_stringsitedata)secure~scopein(*VVV We set timeout for both secure and unsecure states.
Make possible to customize this? *)f?full_st_name:(Option.map(make_full_st_namefalse)state_hier)?cookie_level:(Somecookie_type)~recompute_expdates:falsetruetruesitedatav;f?full_st_name:(Option.map(make_full_st_nametrue)state_hier)?cookie_level:(Somecookie_type)~recompute_expdates:falsetruetruesitedatav(** Parsing of config file for each site: *)letparse_config_hostpatternconf_infosite_dir=(*--- if we put the following line here: *)letsitedata=create_sitedatahostpatternsite_dirconf_infoin(*--- then there is one service tree for each <site> *)(*--- (mutatis mutandis for the following line:) *)Eliom_common.absolute_change_sitedatasitedata;letfirsteliomtag=reftrueinletfirstmodule=reftrueinleteliommodulewarningdisplayed=reffalseinletrecparse_default_links_xhrattsdefault_links_xhr=function|[]->default_links_xhr,List.revatts|("xhr-links",str_value)::suite->letdefault_links_xhr=matchstr_valuewith|"yes"->true|"no"->false|_->raise(Error_in_config_file("Invalid value for attribute xhr-links: "^str_value))inparse_default_links_xhratts(Somedefault_links_xhr)suite|att::suite->parse_default_links_xhr(att::atts)default_links_xhrsuiteinletrecparse_module_attrsfile=function|[]->file|("name",s)::suite->(matchfilewith|None->parse_module_attrs(Some(Names))suite|_->raise(Error_in_config_file"Duplicate attribute module in <eliom>"))|("module",s)::suite->(matchfilewith|None->parse_module_attrs(Some(Files[s]))suite|_->raise(Error_in_config_file"Duplicate attribute module in <eliom>"))|("findlib-package",s)::suite->(matchfilewith|None->(tryparse_module_attrs(Some(Files(Ocsigen_loader.findfiless)))suitewithOcsigen_loader.Findlib_error_ase->raise(Error_in_config_file(Printf.sprintf"Findlib error: %s"(Printexc.to_stringe))))|_->raise(Error_in_config_file"Duplicate attribute module in <eliom>"))|(s,_)::_->raise(Error_in_config_file("Wrong attribute for <eliom>: "^s))infun__parse_site->function|Xml.Element("eliommodule",atts,content)->Eliom_extension.register_eliom_extensiondefault_module_action;(matchparse_module_attrsNoneattswith|Somefile_or_name->exception_during_eliommodule_loading:=true;site_initfirstmodule;load_eliom_modulesitedatafile_or_name"eliommodule"content;exception_during_eliommodule_loading:=false|_->());ifEliom_extension.get_eliom_extension()!=default_module_actionthenEliommod_pagegen.gen(Some(Eliom_extension.get_eliom_extension()))sitedataelsegen_nothing()|Xml.Element("eliom",atts,content)->(*--- if we put the line "new_sitedata" here, then there is
one service table for each <eliom> tag ...
I think the other one is the best,
because it corresponds to the way
browsers manage cookies (one cookie for one site).
Thus we can have one site in several cmo (with one session).
*)letoldipv6mask=sitedata.Eliom_common.ipv6maskinletcontent=parse_eliom_options((functsnoov->set_timeout(Eliommod_timeouts.set_global_~kind:`Data)sitedatactsnoov;set_timeout(Eliommod_timeouts.set_global_~kind:`Service)sitedatactsnoov),set_timeout(Eliommod_timeouts.set_global_~kind:`Data)sitedata,set_timeout(Eliommod_timeouts.set_global_~kind:`Service)sitedata,set_timeout(Eliommod_timeouts.set_global_~kind:`Persistent)sitedata,(funv->sitedata.Eliom_common.max_service_sessions_per_group<-v,true),(funv->sitedata.Eliom_common.max_service_sessions_per_subnet<-v,true),(funv->sitedata.Eliom_common.max_volatile_data_sessions_per_group<-v,true),(funv->sitedata.Eliom_common.max_volatile_data_sessions_per_subnet<-v,true),(funv->sitedata.Eliom_common.max_persistent_data_sessions_per_group<-Somev,true),(funv->sitedata.Eliom_common.max_service_tab_sessions_per_group<-v,true),(funv->sitedata.Eliom_common.max_volatile_data_tab_sessions_per_group<-v,true),(funv->sitedata.Eliom_common.max_persistent_data_tab_sessions_per_group<-Somev,true),(funv->sitedata.Eliom_common.max_anonymous_services_per_session<-v,true),(funv->sitedata.Eliom_common.max_anonymous_services_per_subnet<-v,true;(* The global table has already been created, with old max
and old ipv6mask.
I update it, otherwise the setting has no effect
for this table: *)tryletdlist=Eliom_common.find_dlist_ip_tablesitedata.Eliom_common.ipv4mask(* unused *)oldipv6masksitedata.Eliom_common.dlist_ip_tableIpaddr.(V6V6.localhost)inignore(Ocsigen_cache.Dlist.set_maxsizedlistv)withNot_found->()(* should not occur *)),(funv->ignore(Ocsigen_cache.Dlist.set_maxsizesitedata.Eliom_common.group_of_groupsv)),(funv->sitedata.Eliom_common.secure_cookies<-v),(funv->sitedata.Eliom_common.ipv4mask<-Somev,true),(funv->sitedata.Eliom_common.ipv6mask<-Somev,true),(funv->sitedata.Eliom_common.application_script<-v),(funv->sitedata.Eliom_common.cache_global_data<-v),(funv->sitedata.Eliom_common.html_content_type<-Somev),(funregexp->sitedata.Eliom_common.ignored_get_params<-regexp::sitedata.Eliom_common.ignored_get_params),(funregexp->sitedata.Eliom_common.ignored_post_params<-regexp::sitedata.Eliom_common.ignored_post_params),funv->sitedata.Eliom_common.omitpersistentstorage<-v)contentinletdefault_links_xhr,atts=parse_default_links_xhr[]Noneattsin(matchdefault_links_xhrwith|Somedefault_links_xhr->sitedata.Eliom_common.default_links_xhr#set~override_tenable:truedefault_links_xhr|None->());Eliom_extension.register_eliom_extensiondefault_module_action;(matchparse_module_attrsNoneattswith|Somefile_or_name->exception_during_eliommodule_loading:=true;site_initfirstmodule;load_eliom_modulesitedatafile_or_name"eliom"content;exception_during_eliommodule_loading:=false|_->());(*VVV 2012/08
It is not possible to load an eliom extension using <eliom>. Why?
Is there a reason for this? For now I fail in that case. *)ifEliom_extension.get_eliom_extension()!=default_module_actionthenraise(Error_in_config_file"Eliom extensions cannot be loaded using <eliom>. Use <eliommodule> instead.");(* We must generate the page only if it is the first <eliom> tag
for that site: *)if!firsteliomtagthen(firsteliomtag:=false;Eliommod_pagegen.genNonesitedata)else(ifnot!eliommodulewarningdisplayedthenLwt_log.ign_warning~section:Lwt_log.eliom"Tag <eliom> used several times in the same site: will run Eliom only the first time. Prefer <eliommodule> to load a module, and <eliom/> without attribute only once at the position you want to generate your Eliom pages for this site.";eliommodulewarningdisplayed:=true;gen_nothing())|Xml.Element(t,_,_)->raise(Ocsigen_extensions.Bad_config_tag_for_extensiont)|_->raise(Error_in_config_file"(Eliommod extension)")(*****************************************************************************)(** extension registration *)let()=Ocsigen_extensions.register~name:"eliom"~fun_site:parse_config~end_init~exn_handler:handle_init_exn~init_fun:parse_global_config()