123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389# 1 "src/lib/eliom_common_base.shared.ml"(* Ocsigen
* http://www.ocsigen.org
* 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_libexceptionEliom_site_information_not_availableofstring(******************************************************************)typescope_hierarchy=|User_hierofstring|Default_ref_hier|Default_comet_hiertypeuser_scope=[`Session_groupofscope_hierarchy|`Sessionofscope_hierarchy|`Client_processofscope_hierarchy]typescope=[`Site|user_scope]typeall_scope=[scope|`Global|`Request]typeglobal_scope=[`Global]typesite_scope=[`Site]typesession_group_scope=[`Session_groupofscope_hierarchy]typesession_scope=[`Sessionofscope_hierarchy]typeclient_process_scope=[`Client_processofscope_hierarchy]typerequest_scope=[`Request](******************************************************************)typeuser_level=[`Session_group|`Session|`Client_process]typecookie_level=[`Session|`Client_process]typecookie_scope=[`Sessionofscope_hierarchy|`Client_processofscope_hierarchy]letlevel_of_user_scope:[<user_scope]->[>user_level]=function|`Session_->`Session|`Session_group_->`Session_group|`Client_process_->`Client_processletcookie_level_of_user_scope:[<user_scope]->[>cookie_level]=function|`Session_|`Session_group_->`Session|`Client_process_->`Client_processletcookie_scope_of_user_scope:[<user_scope]->[>cookie_scope]=function|`Sessionn|`Session_groupn->`Sessionn|`Client_processn->`Client_processnletscope_hierarchy_of_user_scope:[<user_scope]->scope_hierarchy=function|`Sessionn|`Session_groupn|`Client_processn->n(* The key in the table of states. For cookies scopes, it is also the
information in the cookie name, without the kind of session, and with the
scope level (that is not in the cookie name). *)typefull_state_name={user_scope:user_scope;secure:bool;site_dir_str:string}moduleFull_state_name_table=Map.Make(structtypet=full_state_nameletcompare=compareend)(******************************************************************)(* Service kinds: *)typeatt_key_serv=|SAtt_no(* regular service *)|SAtt_namedofstring(* named coservice *)|SAtt_anonofstring(* anonymous coservice *)|SAtt_csrf_safeof(int*user_scope*booloption)(* CSRF safe anonymous coservice *)(* CSRF safe service registration delayed until form/link creation *)(* the int is an unique id,
the user_scope is used for delayed registration
(if the service is registered in the global table),
the bool option is the ?secure parameter for delayed registration
(if the service is registered in the global table) *)(* The following three are for non-attached coservices
that have been attached on a service afterwards *)|SAtt_na_namedofstring|SAtt_na_anonofstring|SAtt_na_csrf_safeof(int*user_scope*booloption)typena_key_serv=|SNa_no(* no na information *)|SNa_void_keep(* void coservice that keeps GET na parameters *)|SNa_void_dontkeep(* void coservice that does not keep GET na parameters *)|SNa_get_ofstring(* named *)|SNa_post_ofstring(* named *)|SNa_get'ofstring(* anonymous *)|SNa_post'ofstring(* anonymous *)|SNa_get_csrf_safeof(int*user_scope*booloption)(* CSRF safe anonymous coservice *)|SNa_post_csrf_safeof(int*user_scope*booloption)(* CSRF safe anonymous coservice *)(* the same, for incoming requests: *)typeatt_key_req=|RAtt_no(* no coservice information *)|RAtt_namedofstring(* named coservice *)|RAtt_anonofstring(* anonymous coservice *)typena_key_req=|RNa_no(* no na information *)|RNa_get_ofstring(* named *)|RNa_post_ofstring(* named *)|RNa_get'ofstring(* anonymous *)|RNa_post'ofstring(* anonymous *)letatt_key_serv_of_req=function|RAtt_no->SAtt_no|RAtt_nameds->SAtt_nameds|RAtt_anons->SAtt_anonsletna_key_serv_of_req=function|RNa_no->SNa_no|RNa_post's->SNa_post's|RNa_get's->SNa_get's|RNa_post_s->SNa_post_s|RNa_get_s->SNa_get_s(*****************************************************************************)letdefaultpagename="./"(* should be "" but this does not work with firefox.
"index" works but one page may have two different URLs *)leteliom_suffix_name="__eliom_suffix"leteliom_suffix_internal_name="__(suffix service)__"leteliom_nosuffix_page="__eliom_suffix__"letnaservice_num="__eliom_na__num"letnaservice_name="__eliom_na__name"letget_state_param_name="__eliom__"letpost_state_param_name="__eliom_p__"letget_numstate_param_name="__eliom_n__"letpost_numstate_param_name="__eliom_np__"letco_param_prefix="__co_eliom_"letna_co_param_prefix="__na_eliom_"letnl_param_prefix="__nl_"letpnl_param_prefix=nl_param_prefix^"p_"letnpnl_param_prefix=nl_param_prefix^"n_"leteliom_internal_nlp_prefix="__eliom"lettab_cookies_param_name="__eliom_P_tab_cookies"letto_be_considered_as_get_param_name="__eliom_P_was_GET"letappl_name_cookie_name="__eliom_appl_name"letappl_name_header_name="X-Eliom-Application"letfull_xhr_redir_header="X-Eliom-Location-Full"lethalf_xhr_redir_header="X-Eliom-Location-Half"letresponse_url_header="X-Eliom-Url"letset_tab_cookies_header_name="X-Eliom-Set-Process-Cookies"lettab_cookies_header_name="X-Eliom-Process-Cookies"(* Cookie substitutes for iOS WKWebView *)letcookie_substitutes_header_name="X-Eliom-Cookie-Substitutes"letset_cookie_substitutes_header_name="X-Eliom-Set-Cookie-Substitutes"lettab_cpi_header_name="X-Eliom-Process-Info"letexpecting_process_page_name="X-Eliom-Expecting-Process-Page"letbase_elt_id="eliom_base_elt"letnl_is_persistentn=n.[0]='p'(*****************************************************************************)typeclient_process_info={cpi_ssl:bool;cpi_hostname:string;cpi_server_port:int;cpi_original_full_path:Url.path}typesess_info={si_other_get_params:(string*string)list;si_all_get_params:(string*string)list;si_all_post_params:(string*string)listoption;si_all_file_params:(string*file_info)listoption;si_service_session_cookies:stringFull_state_name_table.t;(* the session service cookies sent by the request *)(* the key is the cookie name (or site dir) *)si_data_session_cookies:stringFull_state_name_table.t;(* the session data cookies sent by the request *)(* the key is the cookie name (or site dir) *)si_persistent_session_cookies:stringFull_state_name_table.t;(* the persistent session cookies sent by the request *)(* the key is the cookie name (or site dir) *)si_secure_cookie_info:stringFull_state_name_table.t*stringFull_state_name_table.t*stringFull_state_name_table.t(* the same, but for secure cookies *);(* now for tab cookies: *)si_service_session_cookies_tab:stringFull_state_name_table.t;si_data_session_cookies_tab:stringFull_state_name_table.t;si_persistent_session_cookies_tab:stringFull_state_name_table.t;si_secure_cookie_info_tab:stringFull_state_name_table.t*stringFull_state_name_table.t*stringFull_state_name_table.t;si_tab_cookies:stringOcsigen_cookie_map.Map_inner.t;si_nonatt_info:na_key_req;si_state_info:att_key_req*att_key_req;si_previous_extension_error:int(* HTTP error code sent by previous extension (default: 404) *);si_na_get_params:(string*string)listLazy.t;si_nl_get_params:(string*string)listString.Table.t;si_nl_post_params:(string*string)listString.Table.t;si_nl_file_params:(string*file_info)listString.Table.t;si_persistent_nl_get_params:(string*string)listString.Table.tLazy.t;si_all_get_but_na_nl:(string*string)listLazy.t;si_all_get_but_nl:(string*string)list;si_ignored_get_params:(string*string)list;si_ignored_post_params:(string*string)list;si_client_process_info:client_process_infooption;si_expect_process_data:boolLazy.t(*204FORMS* si_internal_form: bool; *)}typeeliom_js_page_data={ejs_global_data:(Eliom_runtime.global_data*Eliom_wrap.unwrapper)option;ejs_request_data:Eliom_runtime.request_data;(* Event handlers *)ejs_event_handler_table:Eliom_runtime.RawXML.event_handler_table;(* Client Attributes *)ejs_client_attrib_table:Eliom_runtime.RawXML.client_attrib_table;(* Session info *)ejs_sess_info:sess_info}(************ unwrapping identifiers *********************)lettyxml_unwrap_id_int=Eliom_runtime.tyxml_unwrap_id_intlet()=assert(tyxml_unwrap_id_int=1)letcomet_channel_unwrap_id_int=2letreact_up_unwrap_id_int=3letreact_down_unwrap_id_int=4letsignal_down_unwrap_id_int=5letbus_unwrap_id_int=6letclient_value_unwrap_id_int=Eliom_runtime.client_value_unwrap_id_intlet()=assert(client_value_unwrap_id_int=7)letglobal_data_unwrap_id_int=Eliom_runtime.global_data_unwrap_id_intlet()=assert(global_data_unwrap_id_int=8)letserver_function_unwrap_id_int=9typenode_ref=string(****** *)(* CCC take care: this must remain of the same syntax as non localised
non persistent get parameter name *)letnl_get_appl_parameter="__nl_n_eliom-process.p"(* make a path by going up when there is a '..' *)letmake_actual_pathpath=letrecauxaccupath=matchaccu,pathwith|[],".."::path'->auxaccupath'|_::accu',".."::path'->auxaccu'path'|_,a::path'->aux(a::accu)path'|_,[]->accuinmatchpathwith|""::path->""::List.rev(aux[]path)|_->List.rev(aux[]path)letis_client_app=reffalse(* Special version for non localized parameters *)letprefixlength=String.lengthnl_param_prefixletprefixlengthminusone=prefixlength-1letsplit_nl_prefix_paraml=letrecauxothermap=function|[]->map,other|((n,_)asa)::l->ifString.first_diffnnl_param_prefix0prefixlengthminusone=prefixlengththentryletlast=String.index_fromnprefixlength'.'inletnl_param_name=String.subnprefixlength(last-prefixlength)inletprevious=tryString.Table.findnl_param_namemapwithNot_found->[]inauxother(String.Table.addnl_param_name(a::previous)map)lwithInvalid_argument_|Not_found->aux(a::other)maplelseaux(a::other)maplinaux[]String.Table.emptyl(* Split parameter list, removing those whose name starts with pref *)letsplit_prefix_paramprefl=letlen=String.lengthprefinList.partition(fun(n,_)->tryString.subn0len=prefwithInvalid_argument_->false)l(* Remove all parameters whose name starts with pref *)letremove_prefixed_paramprefl=letlen=String.lengthprefinletrecaux=function|[]->[]|((n,_)asa)::l->(tryifString.subn0len=prefthenauxlelsea::auxlwithInvalid_argument_->a::auxl)inauxlletremove_na_prefix_paramsl=remove_prefixed_paramna_co_param_prefixl|>List.remove_assocnaservice_name|>List.remove_assocnaservice_numletfilter_na_get_params=List.filter@@fun(s,(_:string))->s=naservice_name||s=naservice_num||String.subs0(String.lengthna_co_param_prefix)=na_co_param_prefixexceptionEliom_404type('a,'b)foundornot=Foundof'a|Notfoundof'b(** Service called with wrong parameter names *)exceptionEliom_Wrong_parameterexceptionEliom_duplicate_registrationofstringexceptionEliom_page_erasingofstringtype'adircontent=Vide|Tableof'adireltrefString.Table.tand'adirelt=Dirof'adircontentref|Fileof'arefletempty_dircontent()=Videtypemeth=[`Get|`Post|`Put|`Delete|`Other]typepage_table_key={key_state:att_key_serv*att_key_serv;key_meth:meth}typeanon_params_type=intexceptionEliom_Typing_Errorof(string*exn)listtype('params,'result)service={(* unique_id, computed from parameters type. must be the same even
if the actual service reference is different (after reloading the
site) so that it replaces the former one *)s_id:anon_params_type*anon_params_type;mutables_max_use:intoption;s_expire:(float*floatref)option;s_f:bool->'params->'resultLwt.t}type'ato_and_of={of_string:string->'a;to_string:'a->string}(* gets backtrace up until the first slot in the backtrace which mentions
Lwt, which is usually where the backtrace is no longer informative *)letbacktrace_lwt=letlwt_slot_re=Re.Str.regexp"Called from Lwt."infunskip->letstack=Printexc.get_callstack16inletstack_length=Printexc.raw_backtrace_lengthstackinletrecloopacci=ifi>=stack_lengththenaccelsematchletopenPrintexcinSlot.formati@@convert_raw_backtrace_slot@@get_raw_backtrace_slotstackiwith|Somes->ifRe.Str.string_matchlwt_slot_res0thenaccelseloop(s::acc)(i+1)|None->loopacc(i+1)inList.rev@@loop[]skip