123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391(* Ocsigen
* http://www.ocsigen.org
* Module eliommod_pagegen.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.
*)openLwt.Infixletheaders_with_content_typeheaders=Cohttp.Header.add_optheadersOcsigen_header.Name.(to_stringcontent_type)(Printf.sprintf"%s; charset=utf-8"Eliom_content_core.Html.D.Info.content_type)letout=letencodex=fst(Xml_print.Utf8.normalize_htmlx)inEliom_content_core.Html.Printer.pp~encode()letmake_response?headers~statusbody=letbody=Cohttp_lwt.Body.of_string(Format.asprintf"%a"outbody)andresponse=letheaders=headers_with_content_typeheadersinCohttp.Response.make~status~headers()inLwt.return(Ocsigen_response.make~bodyresponse)(* module Html_content = Ocsigen_senders.Make_XML_Content(Xml)(Html.F) *)(* Exception handler for the site *)letdef_handlere=Lwt.faile(* Update cookie tables *)letupdate_cookie_table?nowsitedata(ci,sci)=letnow=matchnowwithSomen->n|None->Unix.gettimeofday()inletupdate_exp(service_cookies_info,data_cookies_info,pers_cookies_info)=(* Update service expiration date and value *)Eliom_common.Full_state_name_table.iter(funname(_oldvalue,newr)->(* catch fun () -> *)match!newrwith|Eliom_common.SCData_session_expired|Eliom_common.SCNo_data->()(* The cookie has been removed *)|Eliom_common.SCnewc->(newc.Eliom_common.sc_exp:=match!(newc.Eliom_common.sc_timeout)with|Eliom_common.TGlobal->(letglobaltimeout=Eliommod_timeouts.find_global`Servicenamesitedatainmatchglobaltimeoutwith|None->None|Somet->Some(t+.now))|Eliom_common.TNone->None|Eliom_common.TSomet->Some(t+.now)))!service_cookies_info;(* Update "in memory data" expiration date and value *)Eliom_common.Full_state_name_table.iter(funnamev->(* 2018-07-17 We do this for all volatile sessions,
even if it has not been used,
otherwise, sessions could have different duration.
(Before: we were doing this only if (Lazy.is_val v))
Keeping same duration is important for example for comet
(which is using both service and volatile data sessions).
*)let_oldvalue,newr=Lazy.forcevinmatch!newrwith|Eliom_common.SCData_session_expired|Eliom_common.SCNo_data->()(* The cookie has been removed *)|Eliom_common.SCnewc->(newc.Eliom_common.dc_exp:=match!(newc.Eliom_common.dc_timeout)with|Eliom_common.TGlobal->(letglobaltimeout=Eliommod_timeouts.find_global`Datanamesitedatainmatchglobaltimeoutwith|None->None|Somet->Some(t+.now))|Eliom_common.TNone->None|Eliom_common.TSomet->Some(t+.now)))!data_cookies_info;letmoduleExpiry_tolerance=struct(* Avoid cookie updates that only change the cookie
expiry date by a negligible amount of time. *)lettimeout_tolerance_factor=0.01letwithin_tolerancexy=letdiff=Float.abs(x-.y)indiff<timeout_tolerance_factor*.Float.abs(x-.now)letwithin_tolerance_optxy=matchx,ywithSomex,Somey->within_tolerancexy|_->x=yendin(* Update persistent expiration date, user timeout and value *)(* 2018-07-17 We do this for all persistent sessions
only if one persistent session has been used:
- all persistent sessions will have same duration
- will not do too many database requests
*)ifEliom_common.Full_state_name_table.exists(fun_v->Lazy.is_valv)!pers_cookies_infothenEliom_common.Full_state_name_table.fold(funnamevthr->letthr2=Lazy.forcev>>=fun(oldvalue,newr)->match!newrwith|Eliom_common.SCData_session_expired|Eliom_common.SCNo_data->(* The cookie has been removed *)Lwt.return()|Eliom_common.SCnewc->(letnewexp=match!(newc.Eliom_common.pc_timeout)with|Eliom_common.TGlobal->(letglobaltimeout=Eliommod_timeouts.find_global`Persistentnamesitedatainmatchglobaltimeoutwith|None->None|Somet->Some(t+.now))|Eliom_common.TNone->None|Eliom_common.TSomet->Some(t+.now)inmatcholdvaluewith|Some(_,oldti,oldexp,oldgrp)whenExpiry_tolerance.within_tolerance_optoldexpnewexp&&oldti=!(newc.Eliom_common.pc_timeout)&&oldgrp=!(newc.Eliom_common.pc_session_group)&&newc.Eliom_common.pc_set_value=None->Lwt.return()(* nothing to do *)|Some(_,_oldti,oldexp,_oldgrp)whennewc.Eliom_common.pc_set_value=None->Lwt.catch(fun()->letcookieid=Eliom_common.(Hashed_cookies.to_stringnewc.pc_hvalue)inEliommod_cookies.Persistent_cookies.replace_if_existscookieid{Eliommod_cookies.full_state_name=name;expiry=newexp;timeout=!(newc.Eliom_common.pc_timeout);session_group=!(newc.Eliom_common.pc_session_group)}>>=fun()->Eliommod_cookies.Persistent_cookies.Expiry_dates.remove_cookieoldexpcookieid)(function|Not_found->Lwt.return()(* someone else closed the session *)|e->Lwt.faile)|_->Eliommod_cookies.Persistent_cookies.addEliom_common.(Hashed_cookies.to_stringnewc.pc_hvalue){Eliommod_cookies.full_state_name=name;expiry=newexp;timeout=!(newc.Eliom_common.pc_timeout);session_group=!(newc.Eliom_common.pc_session_group)})(*VVV Do not forget to change persistent_cookie_table_version
if you change the type of persistent table data,
otherwise the server will crash!!!
*)inthr>>=fun()->thr2)!pers_cookies_infoLwt.return_unitelseLwt.return_unitinupdate_expci>>=fun()->(* the same, for secure cookies: *)update_expsci(*****************************************************************************)(* Generation of the page or naservice
+ update the cookie tables (value, expiration date and timeout) *)letexecutenowgenerate_page({Eliom_common.all_cookie_info;tab_cookie_info;_}asinfo)sitedata=let%lwtresult=Lwt.catch(fun()->generate_pagenowinfositedata)(fune->sitedata.Eliom_common.exn_handlere)inlet%lwt()=update_cookie_table~nowsitedataall_cookie_infoinlet%lwt()=update_cookie_table~nowsitedatatab_cookie_infoinLwt.returnresult(** Set expired sessions in request data *)letset_expired_sessionsriclosedservsessions=ifclosedservsessions=([],[])then()elsePolytables.set~table:(Ocsigen_request.request_cacheri.Ocsigen_extensions.request_info)~key:Eliom_common.eliom_service_session_expired~value:closedservsessionsopenOcsigen_extensionslethandled_method=function|`GET|`HEAD|`POST|`PUT|`DELETE->true|_->falseletdo_redirectionheader_idstatusuri=Ocsigen_extensions.Ext_found(fun()->letresponse=letheaders=Cohttp.Header.init_withOcsigen_header.Name.(to_stringheader_id)uriinCohttp.Response.make~status~headers()inLwt.return(Ocsigen_response.makeresponse))letgen_req_not_found~is_eliom_extension~sitedata~previous_extension_err~req=letreq=Eliom_common.patch_request_inforeqinletnow=Unix.gettimeofday()inlet%lwtri,si,previous_tab_cookies_info=Eliom_common.get_session_info~sitedata~req404inletall_cookie_info,closedsessions=Eliommod_cookies.get_cookie_infonowsitedatasi.Eliom_common.si_service_session_cookiessi.Eliom_common.si_data_session_cookiessi.Eliom_common.si_persistent_session_cookiessi.Eliom_common.si_secure_cookie_infoinlet(tab_cookie_info,closedsessions_tab),user_tab_cookies=(* If tab cookie info exists in rc (because an action put them here),
we get it from here.
Otherwise we get it from tab cookies in parameters.
*)matchprevious_tab_cookies_infowith|Some(atci,utc)->(atci,[]),utc|None->(Eliommod_cookies.get_cookie_infonowsitedatasi.Eliom_common.si_service_session_cookies_tabsi.Eliom_common.si_data_session_cookies_tabsi.Eliom_common.si_persistent_session_cookies_tabsi.Eliom_common.si_secure_cookie_info_tab,Ocsigen_cookie_map.empty)inset_expired_sessionsri(closedsessions,closedsessions_tab);letrecgen_aux({Eliom_common.request=ri;session_info=si;all_cookie_info;_}asinfo)=letsp=Eliom_common.make_server_paramssitedatainfoNoneNonein(* The last two arguments are not yet available, so for now we use None.
This value will later be overwritten once this information is available. *)Lwt.with_valueEliom_common.sp_key(Somesp)@@fun()->letgenfun=matchsi.Eliom_common.si_nonatt_infowith|Eliom_common.RNa_no->(* page generation *)Eliom_route.get_page|_->(* anonymous service *)Eliom_route.make_naserviceinLwt.catch(fun()->let%lwtres=executenowgenfuninfositedatainletresponse,_=Ocsigen_response.to_cohttpresandall_user_cookies=Ocsigen_response.cookiesresinlet%lwtcookies=Eliommod_cookies.compute_cookies_to_sendsitedataall_cookie_infoall_user_cookiesinletres=matchOcsigen_request.headerri.Ocsigen_extensions.request_info(Ocsigen_header.Name.of_stringEliom_common_base.cookie_substitutes_header_name)with|Some_->letresponse=letheaders=Cohttp.Header.add(Cohttp.Response.headersresponse)Eliom_common_base.set_cookie_substitutes_header_name(Eliommod_cookies.cookieset_to_jsoncookies)in{responsewithCohttp.Response.headers}inOcsigen_response.update~response~cookiesres|None->Ocsigen_response.update~cookiesresintryPolytables.get~table:(Ocsigen_request.request_cacheri.Ocsigen_extensions.request_info)~key:Eliom_common.found_stop_key;(* if we find this information in request cache,
the request has already been completed.
(used after an action).
Do not try the following extensions.
*)Lwt.return(Ocsigen_extensions.Ext_found_stop(fun()->Lwt.returnres))withNot_found->Lwt.return(Ocsigen_extensions.Ext_found(fun()->Lwt.returnres)))(function(* FIXME COHTTP transition ; restore all that *)|Eliom_common.Eliom_Typing_Errorl->Lwt.return(Ocsigen_extensions.Ext_found(fun()->make_response~status:`Bad_request(Eliom_error_pages.page_error_param_typel)))|Eliom_common.Eliom_Wrong_parameter->let%lwtripp=matchOcsigen_request.post_paramsreq.request_infori.request_config.Ocsigen_extensions.uploaddirri.request_config.Ocsigen_extensions.maxuploadfilesizewith|None->Lwt.return[]|Somel->linletresponse=Eliom_error_pages.page_bad_param(tryignore@@Polytables.get~table:(Ocsigen_request.request_cacheri.request_info)~key:Eliom_common.eliom_params_after_action;truewithNot_found->false)(Ocsigen_request.get_params_flatri.request_info)(List.mapfstripp)inLwt.return@@Ocsigen_extensions.Ext_found(fun()->make_response~status:`Bad_requestresponse)|Eliom_common.Eliom_404->Lwt.return(Ocsigen_extensions.Ext_nextprevious_extension_err)|Eliom_common.Eliom_retry_witha->gen_auxa|Eliom_common.Eliom_do_redirectionuri->Lwt.return@@do_redirectionOcsigen_header.Name.location`Temporary_redirecturi|Eliom_common.Eliom_do_half_xhr_redirectionuri->Lwt.return@@do_redirection(Ocsigen_header.Name.of_stringEliom_common.half_xhr_redir_header)`No_contenturi|e->Lwt.faile)inletinfo={Eliom_common.request=ri;session_info=si;all_cookie_info;tab_cookie_info;user_tab_cookies}inmatchis_eliom_extensionwith|Someext->Eliom_extension.run_eliom_extensionextnowinfositedata|None->gen_auxinfoletgenis_eliom_extensionsitedata=letopenOcsigen_extensionsinfunction|Req_found_->Lwt.returnExt_do_nothing|Req_not_found((`Not_foundasprevious_extension_err),req)whenhandled_method(Ocsigen_request.methreq.request_info)->gen_req_not_found~is_eliom_extension~sitedata~previous_extension_err~req|Req_not_found(_,_ri)->Lwt.returnExt_do_nothing