123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316openLwt.Infixletsection=Lwt_log.Section.make"ocsigen:cohttp"exceptionOcsigen_http_errorofOcsigen_cookie_map.t*Cohttp.Code.statusexceptionExt_http_errorofCohttp.Code.status*stringoption*Cohttp.Header.toption(** print_request Print request for debug
@param out_ch output for debug
@param request Cohttp request *)let_print_requestfmtrequest=letprint_listprint_dataout_chlst=letrecaux=function|[]->()|[x]->print_dataout_chx|x::r->print_dataout_chx;auxrinauxlstinFormat.fprintffmt"%s [%s/%s]:\n"(Uri.to_string(Cohttp.Request.urirequest))Cohttp.(Code.string_of_version(Request.versionrequest))Cohttp.(Code.string_of_method(Request.methrequest));Cohttp.Header.iter(funkeyvalues->(print_list(funfmtvalue->Format.fprintffmt"\t%s = %s\n"keyvalue)fmtvalues))(Cohttp.Request.headersrequest)letconnections=Hashtbl.create256letget_number_of_connected,incr_connected,decr_connected,_wait_fewer_connected=letconnected=ref0inletmaxr=ref(-1000)inletmvar=Lwt_mvar.create_empty()in((fun()->!connected),(funn->connected:=!connected+n),(fun()->letc=!connectedinconnected:=c-1;if!connected<0thenexit1;ifc=!maxrthenbeginLwt_log.ign_warning~section"Number of connections now ok";maxr:=-1000;Lwt_mvar.putmvar()endelseLwt.return()),(funmax->maxr:=max;Lwt_mvar.takemvar))exceptionOcsigen_is_dirof(Ocsigen_request.t->Uri.t)moduleCookie=structletserialize_cookie_rawpathexpnamecsecure=Format.sprintf"%s=%s; path=/%s%s%s"namec(Ocsigen_lib.Url.string_of_url_path~encode:truepath)(ifsecurethen"; secure"else"")(matchexpwith|Somes->"; expires="^(Ocsigen_lib.Date.to_strings)|None->"")letserialize_cookiespath=Ocsigen_cookie_map.Map_inner.fold@@funnamech->letopenOcsigen_cookie_mapinletexp,v,secure=matchcwith|OUnset->(Some0.,"",false)|OSet(t,v,secure)->(t,v,secure)inCohttp.Header.addhOcsigen_header.Name.(to_stringset_cookie)(serialize_cookie_rawpathexpnamevsecure)letserializecookiesheaders=Ocsigen_cookie_map.Map_path.foldserialize_cookiescookiesheadersend(* FIXME: secure *)letmake_cookies_headerpathexpnamec_secure=Format.sprintf"%s=%s%s%s"namec(*VVV encode = true? *)("; path=/"^Ocsigen_lib.Url.string_of_url_path~encode:truepath)(* (if secure && slot.sl_ssl then "; secure" else "")^ *)""^(matchexpwith|Somes->"; expires="^Ocsigen_lib.Date.to_strings|None->"")letmake_cookies_headerspaththds=Ocsigen_cookie_map.Map_inner.fold(funnamech->letopenOcsigen_cookie_mapinletexp,v,secure=matchcwith|OUnset->Some0.,"",false|OSet(t,v,secure)->t,v,secureinCohttp.Header.addhOcsigen_header.Name.(to_stringset_cookie)(make_cookies_headerpathexpnamevsecure))thdslethandler~ssl~address~port~connector(flow,conn)requestbody=letfilenames=ref[]inletedn=Conduit_lwt_unix.endp_of_flowflowinletrecgetsockname=function|`TCP(ip,port)->Unix.ADDR_INET(Ipaddr_unix.to_inet_addrip,port)|`Unix_domain_socketpath->Unix.ADDR_UNIXpath|`TLS(_,edn)->getsocknameedn|`Unknownerr->raise(Failure("resolution failed: "^err))|`Vchan_direct_->raise(Failure"VChan not supported")|`Vchan_domain_socket_->raise(Failure"VChan not supported")inletsockaddr=getsocknameedninletconnection_closed=tryfst(Hashtbl.findconnectionsconn)withNot_found->let((connection_closed,_)asp)=Lwt.wait()inHashtbl.addconnectionsconnp;incr_connected1;connection_closedinlethandle_errorexn=Lwt_log.ign_debug~section~exn"Got exception while handling request.";letheaders,ret_code=matchexnwith|Ocsigen_http_error(cookies_to_set,code)->letheaders=Cookie.serializecookies_to_set(Cohttp.Header.init())inSomeheaders,code|Ocsigen_stream.InterruptedOcsigen_stream.Already_read->None,`Internal_server_error|Unix.Unix_error(Unix.EACCES,_,_)->None,`Forbidden|Ext_http_error(code,_,headers)->headers,code|Ocsigen_lib.Ocsigen_Bad_Request->None,`Bad_request|Ocsigen_lib.Ocsigen_Request_too_long->None,`Request_entity_too_large|exn->Lwt_log.ign_error~section~exn"Error while handling request.";None,`Internal_server_errorinletbody=matchret_codewith|`Not_found->"Not Found"|_->Printexc.to_stringexninCohttp_lwt_unix.Server.respond_error?headers~status:(ret_code:>Cohttp.Code.status_code)~body()in(* TODO: equivalent of Ocsigen_range *)letrequest=Ocsigen_request.make~address~port~ssl~filenames~sockaddr~body~connection_closedrequestinLwt.finalize(fun()->Ocsigen_messages.accesslog(Format.sprintf"connection for %s from %s (%s)%s: %s"(matchOcsigen_request.hostrequestwith|None->"<host not specified in the request>"|Someh->h)(Ocsigen_request.remote_iprequest)(Option.value~default:""(Ocsigen_request.headerrequestOcsigen_header.Name.user_agent))(Option.fold~none:""~some:(funs->" X-Forwarded-For: "^s)(Ocsigen_request.headerrequestOcsigen_header.Name.x_forwarded_for))(Uri.path(Ocsigen_request.urirequest)));Lwt.catch(fun()->connectorrequest>>=funresponse->letresponse,body=Ocsigen_response.to_cohttpresponseandcookies=Ocsigen_response.cookiesresponseinletresponse=letheaders=Cohttp.Header.add_unless_exists(Cohttp.Header.add_unless_exists(Ocsigen_cookie_map.Map_path.foldmake_cookies_headerscookies(Cohttp.Response.headersresponse))"server"Ocsigen_config.server_name)"date"(Ocsigen_lib.Date.to_string(Unix.time()))in{responsewithCohttp.Response.headers}inLwt.return(response,body))(function|Ocsigen_is_dirfun_request->letheaders=fun_requestrequest|>Uri.to_string|>Cohttp.Header.init_with"location"andstatus=`Moved_permanentlyinCohttp_lwt_unix.Server.respond~headers~status~body:`Empty()|exn->handle_errorexn))(fun()->if!filenames<>[]thenList.iter(funa->tryUnix.unlinkawithUnix.Unix_error_asexn->Lwt_log.ign_warning_f~section~exn"Error while removing file %s"a)!filenames;Lwt.return_unit)letconn_closed(_flow,conn)=tryLwt_log.ign_debug_f~section"Connection closed:\n%s"(Cohttp.Connection.to_stringconn);Lwt.wakeup(snd(Hashtbl.findconnectionsconn))();Hashtbl.removeconnectionsconn;Lwt.asyncdecr_connectedwithNot_found->()letstop,stop_wakener=Lwt.wait()letshutdowntimeout=letprocess=matchtimeoutwith|Somef->(fun()->Lwt_unix.sleepf)|None->(fun()->Lwt.return())inignore(Lwt.pick[process();stop]>>=fun()->exit0)letservice?ssl~address~port~connector()=lettls_own_key=matchsslwith|Some(crt,key,Somepassword)->`TLS(`Crt_file_pathcrt,`Key_file_pathkey,`Passwordpassword)|Some(crt,key,None)->`TLS(`Crt_file_pathcrt,`Key_file_pathkey,`No_password)|None->`Nonein(* We create a specific context for Conduit and Cohttp. *)Conduit_lwt_unix.init~src:(Ocsigen_config.Socket_type.to_stringaddress)~tls_own_key()>>=funconduit_ctx->Lwt.return(Cohttp_lwt_unix.Net.init~ctx:conduit_ctx())>>=functx->(* We catch the INET_ADDR of the server *)letcallback=letaddress=Ocsigen_config.Socket_type.to_inet_addraddressandssl=matchsslwithSome_->true|None->falseinhandler~ssl~address~port~connectorinletconfig=Cohttp_lwt_unix.Server.make~conn_closed~callback()inletmode=matchtls_own_keywith|`None->`TCP(`Portport)|`TLS(crt,key,pass)->`OpenSSL(crt,key,pass,`Portport)inCohttp_lwt_unix.Server.create~stop~ctx~modeconfig>>=fun()->Lwt.return(Lwt.wakeupstop_wakener())