123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151openLwt.InfixmoduleHeader=Cohttp.HeadermoduleMake(IO:S.IO)=structmoduleIO=IOmoduleRequest=Make.Request(IO)moduleResponse=Make.Response(IO)letsrc=Logs.Src.create"cohttp.lwt.server"~doc:"Cohttp Lwt server module"moduleLog=(valLogs.src_logsrc:Logs.LOG)typeconn=IO.conn*Cohttp.Connection.ttyperesponse_action=[`ExpertofCohttp.Response.t*(IO.ic->IO.oc->unitLwt.t)|`ResponseofCohttp.Response.t*Body.t]typet={callback:conn->Cohttp.Request.t->Body.t->response_actionLwt.t;conn_closed:conn->unit;}letmake_response_action?(conn_closed=ignore)~callback()={conn_closed;callback}letmake?conn_closed~callback()=letcallbackconnreqbody=callbackconnreqbody>|=funrsp->`Responserspinmake_response_action?conn_closed~callback()letmake_expert?conn_closed~callback()=letcallbackconnreqbody=callbackconnreqbody>|=funrsp->`Expertrspinmake_response_action?conn_closed~callback()moduleTransfer_IO=Cohttp__Transfer_io.Make(IO)(* Deprecated *)letresolve_local_file~docroot~uri=Cohttp.Path.resolve_local_file~docroot~uriletrespond?headers?(flush=true)~status~body()=letencoding=matchheaderswith|None->Body.transfer_encodingbody|Someheaders->(matchHeader.get_transfer_encodingheaderswith|Cohttp.Transfer.Unknown->Body.transfer_encodingbody|t->t)inletres=Response.make~status~flush~encoding?headers()inLwt.return(res,body)letrespond_string?(flush=true)?headers~status~body()=letres=Response.make~status~flush~encoding:(Cohttp.Transfer.Fixed(Int64.of_int(String.lengthbody)))?headers()inletbody=Body.of_stringbodyinLwt.return(res,body)letrespond_error?headers?(status=`Internal_server_error)~body()=respond_string?headers~status~body:("Error: "^body)()letrespond_redirect?headers~uri()=letheaders=matchheaderswith|None->Header.init_with"location"(Uri.to_stringuri)|Someh->Header.add_unless_existsh"location"(Uri.to_stringuri)inrespond~headers~status:`Found~body:`Empty()letrespond_need_auth?headers~auth()=letheaders=matchheaderswithNone->Header.init()|Someh->hinletheaders=Header.add_authorization_reqheadersauthinrespond~headers~status:`Unauthorized~body:`Empty()letrespond_not_found?uri()=letbody=matchuriwith|None->"Not found"|Someuri->"Not found: "^Uri.to_stringuriinrespond_string~status:`Not_found~body()letread_bodyicreq=matchRequest.has_bodyreqwith|`Yes->letreader=Request.make_body_readerreqicinletbody_stream=Body.create_streamRequest.read_body_chunkreaderinBody.of_streambody_stream|`No|`Unknown->`Emptylethandle_requestcallbackconnreqbody=Log.debug(funm->m"Handle request: %a."Request.pp_humreq);Lwt.finalize(fun()->Lwt.catch(fun()->callbackconnreqbody)(function|Out_of_memory->Lwt.failOut_of_memory|exn->Log.err(funf->f"Error handling %a: %s"Request.pp_humreq(Printexc.to_stringexn));respond_error~body:"Internal Server Error"()>|=funrsp->`Responsersp))(fun()->Body.drain_bodybody)letrechandle_clienticocconncallback=Request.readic>>=function|`Eof->Lwt.return_unit|`Invaliddata->Log.err(funm->m"invalid input %s while handling client"data);Lwt.return_unit|`Okreq->(letbody=read_bodyicreqinhandle_requestcallbackconnreqbody>>=function|`Response(res,body)->letflush=Response.flushresinResponse.write~flush(funwriter->Body.write_body(Response.write_bodywriter)body)resoc>>=fun()->ifRequest.is_keep_alivereqthenhandle_clienticocconncallbackelseLwt.return_unit|`Expert(res,io_handler)->Response.write_headerresoc>>=fun()->io_handlericoc>>=fun()->handle_clienticocconncallback)letcallbackspecio_idicoc=letconn_id=Cohttp.Connection.create()inletconn_closed()=spec.conn_closed(io_id,conn_id)inLwt.finalize(fun()->IO.catch(fun()->handle_clienticoc(io_id,conn_id)spec.callback)>>=function|Ok()->Lwt.return_unit|Errore->Log.info(funm->m"IO error while handling client: %a"IO.pp_errore);Lwt.return_unit)(fun()->(* Clean up resources when the response stream terminates and call
* the user callback *)conn_closed()|>Lwt.return)end