123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263# 1 "src/lib/eliom_registration.server.ml"(* Ocsigen
* http://www.ocsigen.org
* Module Eliom_registration
* 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.
*)openLwt.Infixletheaders_with_content_type?charset?content_typeheaders=matchcontent_typewith|Somecontent_type->letcharset=ifcharset<>NonethencharsetelseifString.lengthcontent_type>=5&&(String.subcontent_type05="text/"||letsuffix=String.subcontent_type(String.lengthcontent_type-4)4insuffix="/xml"||suffix="=xml")thenSome(Eliom_config.get_config_default_charset())elseNoneinCohttp.Header.replaceheadersOcsigen_header.Name.(to_stringcontent_type)(matchcharsetwith|Somecharset->Printf.sprintf"%s; charset=%s"content_typecharset|None->content_type)|None->headersletresult_of_content?charset?content_type?headers?statusbody=letheaders=matchcontent_typewith|Somecontent_type->letheaders=Ocsigen_header.of_optionheadersinSome(headers_with_content_type?charset~content_typeheaders)|None->headersinletheaders=Cohttp.Header.add_unless_exists(Ocsigen_header.of_optionheaders)"cache-control""no-cache"inletresponse=Cohttp.Response.make?status~headers()inLwt.return(Ocsigen_response.make~bodyresponse)moduleResult_types:sigtype'akindvalcast_result:Ocsigen_response.t->'akindvalcast_kind:'akind->Ocsigen_response.tvalcast_kind_lwt:'akindLwt.t->Ocsigen_response.tLwt.tvalcast_result_lwt:Ocsigen_response.tLwt.t->'akindLwt.tvalcast_function_http:('c->'akindLwt.t)->'c->Ocsigen_response.tLwt.tend=structtype'akind=Ocsigen_response.tletcast_resultx=xletcast_kindx=xletcast_kind_lwtx=xletcast_result_lwtx=xletcast_function_httpx=xendtype'akind='aResult_types.kindtype'aapplication_content=[`Applof'a]typeblock_contenttypebrowser_content=[`Browser]type'aocaml_contenttypeunknown_contentletcast_unknown_content_kind(x:unknown_contentkind):'akind=Result_types.cast_result(Result_types.cast_kindx)letcast_http_result=Result_types.cast_resultletcontent_type_htmlcontent_type=letsp=Eliom_common.get_sp()inmatchcontent_type,sp.Eliom_common.sp_sitedata.Eliom_common.html_content_typewith|None,Somecontent_type->content_type|Somecontent_type,_->content_type|None,None->letaccept=Ocsigen_request.header_multi(Eliom_request_info.get_ri())Ocsigen_header.Name.acceptinletaccept=Ocsigen_header.Accept.parseacceptinOcsigen_header.Content_type.chooseacceptEliom_content.Html.D.Info.content_typeEliom_content.Html.D.Info.alternative_content_typesmoduleHtml_base=structtypepage=Html_types.htmlEliom_content.Html.elttypeoptions=unittyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletout=letencodex=fst(Xml_print.Utf8.normalize_htmlx)inEliom_content.Html.Printer.pp~encode()letsend?options:_?charset?code?content_type?headersc=letstatus=Eliom_lib.Option.mapCohttp.Code.status_of_codecodeandcontent_type=content_type_htmlcontent_typeandbody=Cohttp_lwt.Body.of_string(Format.asprintf"%a"outc)inresult_of_content?charset?headers?status~content_typebodyendmoduleHtml=Eliom_mkreg.Make(Html_base)moduleFlow5_base=structtypeoptions=unittyperesult=block_contentkindtypepage=Html_types.flow5Eliom_content.Html.eltlistletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletout=letencodex=fst(Xml_print.Utf8.normalize_htmlx)inEliom_content.Html.Printer.pp_elt~encode()letbodyl=Lwt_stream.of_listl|>Lwt_stream.map(Format.asprintf"%a"out)|>Cohttp_lwt.Body.of_streamletsend?options:_?charset?code?content_type?headersc=letstatus=Eliom_lib.Option.mapCohttp.Code.status_of_codecodeandcontent_type=content_type_htmlcontent_typeandbody=bodycinresult_of_content?charset?headers?status~content_typebodyendmoduleFlow5=Eliom_mkreg.Make(Flow5_base)letadd_cache_headercacheheaders=let(<-<)h(n,v)=Cohttp.Header.replaceh(Ocsigen_header.Name.to_stringn)vinmatchcachewith|None->headers|Some0->headers<-<(Ocsigen_header.Name.cache_control,"no-cache")|Someduration->headers<-<(Ocsigen_header.Name.cache_control,"max-age: "^string_of_intduration)moduleString_base=structtypepage=string*stringtypeoptions=inttyperesult=unknown_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletsend?options?charset?code?content_type:_?headers(c,content_type)=letstatus=Eliom_lib.Option.mapCohttp.Code.status_of_codecodeandbody=Cohttp_lwt.Body.of_stringcandheaders=add_cache_headeroptions(Ocsigen_header.of_optionheaders)inresult_of_content?charset?status~content_type~headersbodyendmoduleString=Eliom_mkreg.Make(String_base)moduleCssText_base=structtypepage=stringtypeoptions=inttyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletsend?options?charset?code?content_type?headerscontent=String_base.send?options?charset?code?content_type?headers(content,"text/css")endmoduleCssText=Eliom_mkreg.Make(CssText_base)moduleHtml_text_base=structtypepage=stringtypeoptions=unittyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletsend?options:_?charset?code?content_type?headerscontent=String_base.send?charset?code?content_type?headers(content,"text/html")endmoduleHtml_text=Eliom_mkreg.Make(Html_text_base)(** Actions are like services, but do not generate any page. The current
page is reloaded (but if you give the optional parameter
[~options:`NoReload] to the registration function).
*)moduleAction_base=structtypepage=unittypeoptions=[`Reload|`NoReload]typeresult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XAlways(* The post action service will decide later *)letsend_directlyrires=(* send bypassing the following directives in the configuration
file (they have already been taken into account) *)Polytables.set~table:(Ocsigen_request.request_cacheri)~key:Eliom_common.found_stop_key~value:();resletupdate_requestrisicookies_override=Ocsigen_request.updateri~post_data:None~meth:`GET~cookies_override~get_params_flat:si.Eliom_common.si_other_get_paramsletsend?(options=`Reload)?charset?(code=204)?content_type?headers()=letuser_cookies=Eliom_request_info.get_user_cookies()inmatchoptionswith|`NoReload->letheaders=Ocsigen_header.of_optionheadersinletheaders=matchEliom_request_info.get_sp_client_appl_name()with|Someanr->Cohttp.Header.replaceheadersEliom_common_base.appl_name_header_nameanr|_->headersandstatus=Cohttp.Code.status_of_codecodeinresult_of_content?charset?content_type~headers~statusCohttp_lwt.Body.empty|`Reload->((* It is an action, we reload the page. To do that, we retry
without POST params.
If no post param at all, we retry without GET non_att info.
If no GET non_att info, we retry without GET state.
If no GET state, we do not reload, otherwise it will
loop.
Be very careful while re-reading this. *)letsp=Eliom_common.get_sp()inletsitedata=Eliom_request_info.get_sitedata_sp~spinletsi=Eliom_request_info.get_sispinletri=Eliom_request_info.get_request_spspinletopenOcsigen_extensionsinmatch(si.Eliom_common.si_nonatt_info,si.Eliom_common.si_state_info,Ocsigen_request.methri.request_info)with|(Eliom_common.RNa_no,(Eliom_common.RAtt_no,Eliom_common.RAtt_no),`GET)->Lwt.return(Ocsigen_response.make(Cohttp.Response.make()))|_->letall_cookie_info=sp.Eliom_common.sp_cookie_infoinlet%lwtric=Eliommod_cookies.compute_new_ri_cookies(Unix.time())(Ocsigen_request.sub_pathri.request_info)(Ocsigen_request.cookiesri.request_info)all_cookie_infouser_cookiesinlet%lwtall_new_cookies=Eliommod_cookies.compute_cookies_to_sendsitedataall_cookie_infouser_cookiesin(* Now tab cookies:
As tab cookies are sent only by Eliom_app services,
we just need to keep them in rc.
If the fallback service is not Eliom_app, they will be
lost. *)letrc=Eliom_request_info.get_request_cache_spspinPolytables.set~table:rc~key:Eliom_common.tab_cookie_action_info_key~value:(sp.Eliom_common.sp_tab_cookie_info,sp.Eliom_common.sp_user_tab_cookies,si.Eliom_common.si_tab_cookies);(* Remove some parameters to choose the following service *)Polytables.set~table:(Ocsigen_request.request_cacheri.Ocsigen_extensions.request_info)~key:Eliom_common.eliom_params_after_action~value:(si.Eliom_common.si_all_get_params,si.Eliom_common.si_all_post_params,(* is Some [] *)si.Eliom_common.si_all_file_params,(* is Some [] *)si.Eliom_common.si_nl_get_params,si.Eliom_common.si_nl_post_params,si.Eliom_common.si_nl_file_params,si.Eliom_common.si_all_get_but_nl,si.Eliom_common.si_ignored_get_params,si.Eliom_common.si_ignored_post_params);(*VVV Also put all_cookie_info in this, to avoid
update_cookie_table and get_cookie_info (?) *)letri=update_requestri.request_infosiricinlet%lwt()=Eliommod_pagegen.update_cookie_tablesitedataall_cookie_infoinsend_directlyri(Ocsigen_extensions.compute_result~previous_cookies:all_new_cookiesri))endmoduleAction=Eliom_mkreg.Make(Action_base)(** Unit services are like services, do not generate any page, and do not
reload the page. To be used carefully. Probably not useful at all.
(Same as {!Action} with [`NoReload] option).
*)moduleUnit_base=structtypepage=unittypeoptions=unittyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XAlwaysletsend?options:_?charset?(code=204)?content_type?headers_content=letstatus=Cohttp.Code.status_of_codecodeinresult_of_content?charset?content_type?headers~statusCohttp_lwt.Body.emptyendmoduleUnit=Eliom_mkreg.Make(Unit_base)(* Any is a module allowing to register services that decide
themselves what they want to send. *)moduleAny_base=structtype'apage='akindtypeoptions=unittype'areturn=Eliom_service.non_ocaml(* let send_appl_content = Eliom_service.XNever *)letsend_appl_content=Eliom_service.XAlwaysletsend?options:_?charset?code:_?content_type?headers:_(result:'akind)=letresult=Result_types.cast_kindresultinletcohttp_response=fst(Ocsigen_response.to_cohttpresult)inletheaders=headers_with_content_type?charset?content_type(Cohttp.Response.headerscohttp_response)inletresponse={cohttp_responsewithCohttp.Response.headers}inLwt.return(Ocsigen_response.update~responseresult)endmoduleAny=structincludeEliom_mkreg.Make_poly(Any_base)type'aresult='akindletsend?options?charset?code?content_type?headerscontent=Result_types.cast_result_lwt(Any_base.send?options?charset?code?content_type?headerscontent)endtype'aapplication_name=stringletappl_self_redirectsendpage=ifEliom_request_info.expecting_process_page()thenletresponse=letheaders=Cohttp.Header.(add(init()))Eliom_common.half_xhr_redir_header(Eliom_request_info.get_full_url())inCohttp.Response.make~headers()inOcsigen_response.makeresponse|>Result_types.cast_result|>Lwt.returnelselet%lwtr=(Result_types.cast_function_httpsend)pageinLwt.return(Result_types.cast_resultr)(* File is a module allowing to register services that send files *)moduleFile_base=structtypepage=stringtypeoptions=inttyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletsend?options?charset?code:_?content_type?headersfilename=letsp=Eliom_common.get_sp()inletrequest=Eliom_request_info.get_request_spspinmatchtryOcsigen_local_files.resolve~request~filename()with|Ocsigen_local_files.Failed_403(* XXXBY : maybe we should signal a true 403 ? *)|Ocsigen_local_files.Failed_404|Ocsigen_local_files.NotReadableDirectory->raiseEliom_common.Eliom_404with|Ocsigen_local_files.RFilefname->let%lwtresponse,body=letheaders=Ocsigen_header.of_optionheaders|>add_cache_headeroptions|>headers_with_content_type?charset?content_typeinCohttp_lwt_unix.Server.respond_file~headers~fname()inLwt.return(Ocsigen_response.make~bodyresponse)|Ocsigen_local_files.RDir_->(* FIXME COHTTP TRANSITION: implement directories *)raiseOcsigen_local_files.Failed_404endmoduleFile=structincludeEliom_mkreg.Make(File_base)letcheck_filefilename=letsp=Eliom_common.get_sp()inletrequest=Eliom_request_info.get_request_spspintryignore(Ocsigen_local_files.resolve~request~filename());truewith|Ocsigen_local_files.Failed_403|Ocsigen_local_files.Failed_404|Ocsigen_local_files.NotReadableDirectory->falseendmoduleFile_ct_base=structtypepage=string*stringtypeoptions=inttyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XNeverletsend?options?charset?code?content_type?headers(filename,content_type')=letcontent_type=matchcontent_typewith|Somecontent_type->content_type|None->content_type'inFile_base.send?options?charset?code?headers~content_typefilenameendmoduleFile_ct=structincludeEliom_mkreg.Make(File_ct_base)letcheck_file=File.check_fileend(* FIXME COHTTP TRANSITION: Streamlist (temporarily?) removed *)moduleCustomize(R:Eliom_registration_sigs.S_with_create)(T:sigtypepagevaltranslate:page->R.pageLwt.tend)=structtypepage=T.pagetypereturn=R.returntypeoptions=R.optionstyperesult=R.resultletmake_eh=function|None->None|Someeh->Some(funl->ehl>>=T.translate)letmake_service_handlerfgp=fgp>>=T.translateletsend?options?charset?code?content_type?headerscontent=T.translatecontent>>=func->R.send?options?charset?code?content_type?headerscletregister?app?scope?options?charset?code?content_type?headers?secure_session~service?error_handler(f:'get->'post->'returnLwt.t)=R.register?app?scope?options?charset?code?content_type?headers?secure_session~service?error_handler:(make_eherror_handler)(make_service_handlerf)letcreate?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~meth~path?error_handlerf=R.create?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~meth~path?error_handler:(make_eherror_handler)(make_service_handlerf)letcreate_attached_get?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback~get_params?error_handlerf=R.create_attached_get?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback~get_params?error_handler:(make_eherror_handler)(make_service_handlerf)letcreate_attached_post?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback~post_params?error_handlerf=R.create_attached_post?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback~post_params?error_handler:(make_eherror_handler)(make_service_handlerf)endmoduleOcaml_base=structtypepage=stringtypeoptions=unittyperesult=Ocsigen_response.tletresult_of_http_resultx=xletsend_appl_content=Eliom_service.XNeverletsend?options:_?charset?code?content_type?headerscontent=Result_types.cast_kind_lwt(String.send?charset?code?content_type?headers(content,Eliom_service.eliom_appl_answer_content_type))endmoduleOcaml=structtype'apage='atypeoptions=unittype'areturn='aEliom_service.ocamltype'aresult='aocaml_contentkindmoduleM=Eliom_mkreg.Make(Ocaml_base)letprepare_datadata=letecs_request_data=letdata=Eliom_syntax.get_request_data()inifnot(Ocsigen_config.get_debugmode())thenArray.iter(fund->Eliom_runtime.Client_value_server_repr.clear_locd.Eliom_runtime.value)data;datain(* debug_client_value_data (debug "%s") client_value_data; *)letr={Eliom_runtime.ecs_request_data;ecs_data=data}inLwt.return(Eliom_types.encode_eliom_datar)letmake_eh=function|None->None|Someeh->Some(funl->ehl>>=prepare_data)letstring_regexp=Str.regexp"\"\\([^\\\"]\\|\\\\.\\)*\""letmake_service_handler~namefgp=let%lwtdata=try%lwtlet%lwtres=fgpinLwt.return(`Successres)withexn->letcode=Printf.sprintf"%06x"(Random.int0x1000000)inletargument=letsp=Eliom_common.get_sp()inletsi=Eliom_request_info.get_sispinletpost_params=matchsi.Eliom_common.si_all_post_paramswith|None->[]|Somel->lintryPrintf.sprintf" (%s)"(List.assoc"argument"post_params)withNot_found->""in(matchnamewith|Somename->Lwt_log_core.ign_error_f~exn"Uncaught exception in service %s [%s]%s"namecode(Str.global_replacestring_regexp"\"xxx\""argument)|None->Lwt_log_core.ign_error_f~exn"Uncaught exception [%s]%s"codeargument);Lwt.return(`Failurecode)inprepare_datadataletsend?options?charset?code?content_type?headerscontent=let%lwtcontent=prepare_datacontentinResult_types.cast_result_lwt(M.send?options?charset?code?content_type?headerscontent)letregister?app?scope?options?charset?code?content_type?headers?secure_session~(service:('get,'post,_,_,_,Eliom_service.non_ext,Eliom_service.reg,_,_,_,'returnEliom_service.ocaml)Eliom_service.t)?(error_handler:((string*exn)list->'returnLwt.t)option)(f:'get->'post->'returnLwt.t)=M.register?app?scope?options?charset?code?content_type?headers?secure_session~service:(Eliom_service.untypeservice)?error_handler:(make_eherror_handler)(make_service_handler~name:Nonef)letcreate?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~meth~path?error_handlerf=Eliom_service.untype@@M.create?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~meth~path?error_handler:(make_eherror_handler)(make_service_handler~namef)letcreate_attached_get?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback~get_params?error_handlerf=Eliom_service.untype@@M.create_attached_get?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback:(Eliom_service.untypefallback)~get_params?error_handler:(make_eherror_handler)(make_service_handler~namef)letcreate_attached_post?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback~post_params?error_handlerf=Eliom_service.untype@@M.create_attached_post?app?scope?options?charset?code?content_type?headers?secure_session?https?name?csrf_safe?csrf_scope?csrf_secure?max_use?timeout~fallback:(Eliom_service.untypefallback)~post_params?error_handler:(make_eherror_handler)(make_service_handler~namef)endtypeappl_service_options={do_not_launch:bool}(** [{do_not_launch = true}]: do not launch the client side program if
it is not already launched. Default: [false]. *)letdefault_appl_service_options={do_not_launch=false}letrequest_template=Eliom_reference.eref~scope:Eliom_common.request_scopeNoneletglobal_data_unwrapper=Eliom_wrap.create_unwrapper(Eliom_wrap.id_of_intEliom_runtime.global_data_unwrap_id_int)letget_global_data~keep_debug=letdata=Eliom_syntax.get_global_data()inletdata=ifkeep_debugthendataelseEliom_lib.String_map.map(fun{Eliom_runtime.server_sections_data;client_sections_data}->Array.iter(Array.iter(fund->Eliom_runtime.Client_value_server_repr.clear_locd.Eliom_runtime.value))server_sections_data;{Eliom_runtime.server_sections_data;client_sections_data=Array.map(Array.map(funx->{xwithEliom_runtime.injection_dbg=None}))client_sections_data})dataindata,global_data_unwrapperlettransform_global_app_uri=ref(funx->x)moduletypeAPP=sigvalapplication_script:?defer:bool->?async:bool->unit->[>`Script]Eliom_content.Html.eltvalapplication_name:stringvalis_initial_request:unit->booltypeapp_idtypepage=Html_types.htmlEliom_content.Html.elttypeoptions=appl_service_optionstypereturn=Eliom_service.non_ocamltyperesult=app_idapplication_contentkindincludeEliom_registration_sigs.S_with_createwithtypepage:=pageandtypeoptions:=optionsandtypereturn:=returnandtyperesult:=resultvaltyped_name:app_idapplication_nameendmoduleApp_base(App_param:Eliom_registration_sigs.APP_PARAM)=structtypeapp_idtypepage=Html_types.htmlEliom_content.Html.elttypeoptions=appl_service_optionstyperesult=app_idapplication_contentkindletresult_of_http_result=Result_types.cast_resultletis_initial_request()=Eliom_common.((get_sp()).sp_client_appl_name)<>SomeApp_param.application_nameletglobal_data_cache_options()=(Eliom_request_info.get_sitedata()).Eliom_common.cache_global_dataleteliom_appl_script_id:[`Script]Eliom_content.Html.Id.id=Eliom_content.Html.Id.new_elt_id~global:true()letapplication_script?defer?async()=letdefer',async'=(Eliom_request_info.get_sitedata()).Eliom_common.application_scriptinletdefer=matchdeferwithSomeb->b|None->defer'inletasync=matchasyncwithSomeb->b|None->async'inleta=(ifdeferthen[Eliom_content.Html.D.a_defer()]else[])@ifasyncthen[Eliom_content.Html.D.a_async()]else[]inEliom_content.Html.Id.create_named_elt~id:eliom_appl_script_id(Eliom_content.Html.D.js_script~a~uri:(Eliom_content.Html.D.make_uri~service:(Eliom_service.static_dir())[App_param.application_name^".js"])())letapplication_script=(application_script:?defer:_->?async:_->_->[`Script]Eliom_content.Html.elt:>?defer:_->?async:_->_->[>`Script]Eliom_content.Html.elt)letis_eliom_appl_scriptelt=Eliom_content.Html.Id.have_ideliom_appl_script_ideltleteliom_appl_data_script_id=Eliom_content.Html.Id.new_elt_id~global:true()letmake_eliom_appl_data_script~sp=letscript=Printf.sprintf"var __eliom_appl_sitedata = \'%s\';\nvar __eliom_appl_process_info = \'%s\'\nvar __eliom_request_data;\nvar __eliom_request_cookies;\nvar __eliom_request_template;\n"(Eliom_lib.jsmarshal(Eliommod_cli.client_sitedatasp))(Eliom_lib.jsmarshalsp.Eliom_common.sp_client_process_info)inLwt.returnEliom_content.Html.(Id.create_named_elt~id:eliom_appl_data_script_id(F.script(F.cdata_scriptscript)))letmake_eliom_data_script?(keep_debug=false)~sppage=letejs_global_data=ifis_initial_request()&&global_data_cache_options()=NonethenSome(get_global_data~keep_debug)elseNoneinletejs_request_data=letdata=Eliom_syntax.get_request_data()inifnotkeep_debugthenArray.iter(fund->Eliom_runtime.Client_value_server_repr.clear_locd.Eliom_runtime.value)data;datain(* wrapping of values could create eliom references that may
create cookies that needs to be sent along the page. Hence,
cookies should be calculated after wrapping. *)leteliom_data=Eliom_content.Xml.wrap(Eliom_content.Html.D.toeltpage){Eliom_common.ejs_global_data;ejs_request_data;ejs_event_handler_table=Eliom_content.Xml.make_event_handler_table(Eliom_content.Html.D.toeltpage);ejs_client_attrib_table=Eliom_content.Xml.make_client_attrib_table(Eliom_content.Html.D.toeltpage);ejs_sess_info=Eliommod_cli.client_sisp.Eliom_common.sp_si}inlet%lwttab_cookies=Eliommod_cookies.compute_cookies_to_sendsp.Eliom_common.sp_sitedatasp.Eliom_common.sp_tab_cookie_infosp.Eliom_common.sp_user_tab_cookiesinlet%lwttemplate=Eliom_reference.getrequest_templateinletscript=Printf.sprintf"__eliom_request_data = \'%s\';\n__eliom_request_cookies = \'%s\';\n__eliom_request_template = \'%s\';"(Eliom_lib.jsmarshaleliom_data)(Eliom_lib.jsmarshaltab_cookies)(Eliom_lib.jsmarshal(template:stringoption))inLwt.returnEliom_content.Html.(F.script(F.cdata_scriptscript))letglobal_data_service=lazy(letpath,max_age=matchglobal_data_cache_options()with|Somev->v|None->assertfalseinletglobal_data=get_global_data~keep_debug:(Ocsigen_config.get_debugmode())|>Eliom_wrap.wrap|>Eliom_lib.jsmarshalinletscript=Printf.sprintf"__eliom_global_data = \'%s\';"global_datainletname=Digest.to_hex(Digest.stringglobal_data)^".js"inString.create~options:max_age~path:(Eliom_service.Path(path@[name]))~meth:(GetEliom_parameter.unit)(fun__->Lwt.return(script,"application/x-javascript")))letadd_eliom_global_data_scriptrem=ifglobal_data_cache_options()<>Nonethen(* Using the async flag does not make sense here as we need to
be sure that this is executed before the application script. *)letdefer,_=(Eliom_request_info.get_sitedata()).Eliom_common.application_scriptinleturi=Eliom_content.Html.F.make_uri~absolute:false~service:(Lazy.forceglobal_data_service)()inleta=(ifdeferthen[Eliom_content.Html.F.a_defer()]else[])@[Eliom_content.Html.F.a_src@@!transform_global_app_uriuri]inEliom_content.Html.F.script~a(Eliom_content.Html.F.txt"")::remelseremletsplit_pagepage:Html_types.html_attribEliom_content.Html.attriblist*(Html_types.head_attribEliom_content.Html.attriblist*Html_types.titleEliom_content.Html.elt*Html_types.head_content_funEliom_content.Html.eltlist)*Html_types.bodyEliom_content.Html.elt=matchEliom_content.Xml.contentpagewith|Eliom_content.Xml.Node(_,html_attribs,[head;body])->(matchEliom_content.Xml.contentheadwith|Eliom_content.Xml.Node(_,head_attribs,head_elts)->(List.mapEliom_content.Html.D.to_attribhtml_attribs,(List.mapEliom_content.Html.D.to_attribhead_attribs,Eliom_content.Html.D.tot(List.hdhead_elts),Eliom_content.Html.D.totl(List.tlhead_elts)),Eliom_content.Html.D.totbody)|_->assertfalse)|_->assertfalseletadd_eliom_scripts~sppage=let%lwtappl_data_script=make_eliom_appl_data_script~spin(* First we build a fake page to build the ref_tree... *)lethtml_attribs,(head_attribs,title,head_elts),body=split_page(Eliom_content.Html.D.toeltpage)inletencode_slashs=List.map(Eliom_lib.Url.encode~plus:false)inletbase_url=Eliom_uri.make_proto_prefix(Eliom_config.default_protocol_is_https()||Eliom_request_info.get_csp_ssl_spsp)^Eliom_lib.String.concat"/"(encode_slashs(Eliom_request_info.get_csp_original_full_path()))inlethead_elts=ifList.existsis_eliom_appl_scripthead_eltsthenhead_eltselsehead_elts@[application_script()]inlethead_elts=appl_data_script(* <base> elt is added only for xhr done by client process,
because in that case, URLs are relative to the URL of
the first page, not the current URL.
We don't want to put base for non-xhr,
to make it possible to have truly relative URLs in HTML pages.
*)::(ifEliom_request_info.expecting_process_page()thenEliom_content.Html.(F.base~a:[F.a_idEliom_common_base.base_elt_id;F.a_href(Eliom_content.Xml.uri_of_stringbase_url)]())::head_eltselsehead_elts)inletfake_page=Eliom_content.Html.F.html~a:html_attribs(Eliom_content.Html.F.head~a:head_attribstitlehead_elts)bodyinlet%lwtdata_script=make_eliom_data_script~keep_debug:(Ocsigen_config.get_debugmode())~spfake_pagein(* Then we replace the faked data_script *)lethead_elts=(* Eliom_client_core.load_data_script expects data_script to be
second in this list *)List.hdhead_elts::data_script::add_eliom_global_data_script(List.tlhead_elts)inLwt.return(Eliom_content.Html.F.html~a:html_attribs(Eliom_content.Html.F.head~a:head_attribstitlehead_elts)body)letremove_eliom_scriptspage=lethtml_attribs,(head_attribs,title,head_elts),body=split_page(Eliom_content.Html.D.toeltpage)inlethead_elts=List.filter(funx->not(is_eliom_appl_scriptx))head_eltsinLwt.return(Eliom_content.Html.F.html~a:html_attribs(Eliom_content.Html.F.head~a:head_attribstitlehead_elts)body)letsend_appl_content=Eliom_service.XSame_appl(App_param.application_name,None)letout=letencodex=fst(Xml_print.Utf8.normalize_htmlx)inEliom_content.Html.Printer.pp~encode()letsend?(options=default_appl_service_options)?charset?code?content_type?headerscontent=letsp=Eliom_common.get_sp()in(* GRGR FIXME et si le nom de l'application diffère ?? Il faut
renvoyer un full_redirect... TODO *)ifsp.Eliom_common.sp_client_appl_name<>SomeApp_param.application_namethenEliom_state.set_cookie~cookie_level:`Client_process~name:Eliom_common.appl_name_cookie_name~value:App_param.application_name();let%lwtbody=(matchsp.Eliom_common.sp_client_appl_name,options.do_not_launchwith|None,true->remove_eliom_scriptscontent|_->add_eliom_scripts~spcontent)>|=funbody->Cohttp_lwt.Body.of_string(Format.asprintf"%a"outbody)inletheaders=leth=Ocsigen_header.of_optionheadersinleth=Cohttp.Header.replacehEliom_common_base.appl_name_header_nameApp_param.application_nameintry(* If it is a suffix service with redirection, we may have to
normalize the uri *)lettable=Eliom_request_info.get_request_cache()inCohttp.Header.replacehEliom_common_base.response_url_header(Polytables.get~table~key:Eliom_mkreg.suffix_redir_uri_key)withNot_found->handstatus=Eliom_lib.Option.mapCohttp.Code.status_of_codecodeandcontent_type=content_type_htmlcontent_typeinresult_of_content?charset?status~content_type~headersbodyendmoduleApp(App_param:Eliom_registration_sigs.APP_PARAM)=structmoduleBase=App_base(App_param)typeapp_id=Base.app_idletis_initial_request=Base.is_initial_requestletapplication_script=Base.application_scriptincludeEliom_mkreg.Make(Base)letapplication_name=App_param.application_namelettyped_name=App_param.application_nameletdata_service_handler()()=Lwt.return(get_global_data~keep_debug:(Ocsigen_config.get_debugmode()))let_=matchApp_param.global_data_pathwith|Someglobal_data_path->ignore@@Ocaml.create~path:(Eliom_service.Pathglobal_data_path)~meth:(GetEliom_parameter.unit)~https:truedata_service_handler|None->()endmoduletypeTMPL_PARAMS=sigtypetvalname:stringvalmake_page:t->Html_types.htmlEliom_content.Html.eltLwt.tvalupdate:t->unitEliom_client_value.tendmoduleEliom_tmpl_reg_make_param(Appl:APP)(Tmpl_param:TMPL_PARAMS)=structtypepage=Tmpl_param.ttypeoptions=appl_service_optionstyperesult=Appl.app_idapplication_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XSame_appl(Appl.application_name,SomeTmpl_param.name)letnl_template=Eliom_parameter.make_non_localized_parameters~prefix:"eliom"~name:"template"(Eliom_parameter.string"name")letsend?(options=default_appl_service_options)?charset?code?content_type?headerscontent=matchEliom_parameter.get_non_localized_get_parametersnl_templatewith|None->let%lwt()=Eliom_reference.setrequest_template(SomeTmpl_param.name)inlet%lwtcontent=Tmpl_param.make_pagecontentinResult_types.cast_kind_lwt(Appl.send~options?charset?code?content_type?headerscontent)|Some_->ignore(Tmpl_param.updatecontent);Result_types.cast_kind_lwt(Ocaml.send?charset?code?content_type?headers())endmoduleEliom_tmpl(App:APP)(Tmpl_param:TMPL_PARAMS)=Eliom_mkreg.Make(Eliom_tmpl_reg_make_param(App)(Tmpl_param))typeredirection_options=[`MovedPermanently|`Found|`SeeOther|`NotNodifed|`UseProxy|`TemporaryRedirect]letstatus_of_redirection_optionsoptionscode=matchcodewith|Somecode->Cohttp.Code.status_of_codecode|None->(match(options:redirection_options)with|`MovedPermanently->`Moved_permanently|`Found->`Found|`SeeOther->`See_other|`NotNodifed->`Not_modified|`UseProxy->`Use_proxy|`TemporaryRedirect->`Temporary_redirect)(* Redirection services are like services, but send a redirection
instead of a page.
The HTTP/1.1 RFC says: If the 301 status code is received in
response to a request other than GET or HEAD, the user agent MUST
NOT automatically redirect the request unless it can be confirmed
by the user, since this might change the conditions under which the
request was issued.
Here redirections are done towards services without parameters.
(possibly preapplied). *)moduleString_redirection_base=structtypepage=Eliom_lib.Url.uritypeoptions=redirection_optionstyperesult=browser_contentkindletresult_of_http_result=Result_types.cast_resultletsend_appl_content=Eliom_service.XAlways(* actually, the service will decide itself *)letsend?(options=`Found)?charset?code?content_type?headersuri=letheaders=Ocsigen_header.of_optionheadersandheader_id,status=(* We decide the kind of redirection we do. If the request is an
XHR done by a client side Eliom program expecting a process
page, we do not send an HTTP redirection. In that case, we
send a half XHR redirection. *)ifnot(Eliom_request_info.expecting_process_page())then(* the browser did not ask application eliom data, we send a
regular redirection *)(Ocsigen_header.Name.(to_stringlocation),status_of_redirection_optionsoptionscode)elseEliom_common.half_xhr_redir_header,`OKinletheaders=Cohttp.Header.replaceheadersheader_iduriinresult_of_content?charset?content_type~status~headers(Cohttp.Body.empty:>Cohttp_lwt.Body.t)endmoduleString_redirection=Eliom_mkreg.Make(String_redirection_base)type_redirection=|Redirection:(unit,unit,Eliom_service.get,_,_,_,_,[`WithoutSuffix],unit,unit,'a)Eliom_service.t->'aredirectionmoduleRedirection_base=structtype'apage='aredirectiontypeoptions=redirection_optionstype'areturn='aletsend_appl_content=Eliom_service.XAlways(* actually, the service will decide itself *)letsend?(options=`Found)?charset?code?content_type?headers(Redirectionservice)=leturi=Eliom_uri.make_string_uri~service()andheaders=Ocsigen_header.of_optionheadersin(* Now we decide the kind of redirection we do.
If the request is an xhr done by a client side Eliom program
expecting a process page, we do not send an HTTP redirection.
In that case, we send:
- a full xhr redirection if the application to which belongs
the destination service is the same (thus it will send back
tab cookies) (simulate a redirection without stopping the
client process)
- a half xhr redirection otherwise (i.e. ask the browser to do
an actual redirection). *)match(Eliom_request_info.expecting_process_page(),Eliom_request_info.get_sp_client_appl_name())with|true,None(* should not happen *)|false,_->(* the browser did not ask for process data,we
send a regular redirection *)letstatus=status_of_redirection_optionsoptionscodeandheaders=Cohttp.Header.replaceheadersOcsigen_header.Name.(to_stringlocation)uriinresult_of_content?charset?content_type~status~headers(Cohttp.Body.empty:>Cohttp_lwt.Body.t)|true,Someanr->letheaders=Cohttp.Header.replaceheadersEliom_common_base.appl_name_header_nameanrinletheaders=Cohttp.Header.replaceheaders(matchEliom_service.send_appl_contentservicewith(* the appl name of the destination service *)|Eliom_service.XSame_appl(an,_)whenan=anr->(* Same appl, we do a full XHR redirection (not an HTTP
redirection, because we want to send back tab cookies) *)Eliom_common.full_xhr_redir_header|Eliom_service.XAlways->(* It is probably an action, or a void coservice. Full XHR
again *)Eliom_common.full_xhr_redir_header|_->(* No application, or another application. We ask the
browser to do an HTTP redirection. *)Eliom_common.half_xhr_redir_header)uriinresult_of_content?charset?content_type~status:`No_content~headers(Cohttp.Body.empty:>Cohttp_lwt.Body.t)endmoduleRedirection=structincludeEliom_mkreg.Make_poly(Redirection_base)letsend?options?charset?code?content_type?headerscontent=Result_types.cast_result_lwt(Redirection_base.send?options?charset?code?content_type?headerscontent)endletset_exn_handlerh=letsitedata=Eliom_request_info.find_sitedata"set_exn_handler"inEliom_request_info.set_site_handlersitedata(Result_types.cast_function_httph)