123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382moduletypeS=sigtypestacktypeipaddrmoduleTCP:sigincludeMirage_flow.Svaldst:flow->ipaddr*intvalno_close:flow->unitvalto_close:flow->unitendmoduleTLS:sigtypeerror=[`Tls_alertofTls.Packet.alert_type|`Tls_failureofTls.Engine.failure|`ReadofTCP.error|`WriteofTCP.write_error]typewrite_error=[`Closed|error]includeMirage_flow.Swithtypeerror:=errorandtypewrite_error:=write_errorvalno_close:flow->unitvalto_close:flow->unitvalepoch:flow->(Tls.Core.epoch_data,unit)resultvalreneg:?authenticator:X509.Authenticator.t->?acceptable_cas:X509.Distinguished_name.tlist->?cert:Tls.Config.own_cert->?drop:bool->flow->(unit,[write_error|`Msgofstring])resultLwt.tvalkey_update:?request:bool->flow->(unit,[write_error|`Msgofstring])resultLwt.tvalserver_of_flow:Tls.Config.server->TCP.flow->(flow,write_error)resultLwt.tvalclient_of_flow:Tls.Config.client->?host:[`host]Domain_name.t->TCP.flow->(flow,write_error)resultLwt.tendvaltcp_protocol:(stack*ipaddr*int,TCP.flow)Mimic.protocolvaltcp_edn:(stack*ipaddr*int)Mimic.valuevaltls_edn:([`host]Domain_name.toption*Tls.Config.client*stack*ipaddr*int)Mimic.valuevaltls_protocol:([`host]Domain_name.toption*Tls.Config.client*stack*ipaddr*int,TLS.flow)Mimic.protocoltypettypedst=ipaddr*intvalinit:port:int->stack->tLwt.tvalaccept:t->(TCP.flow,[>`Closed])resultLwt.tvalclose:t->unitLwt.tvalhttp_service:?config:Httpaf.Config.t->error_handler:(dst->Httpaf.Server_connection.error_handler)->(TCP.flow->dst->Httpaf.Server_connection.request_handler)->tPaf.servicevalhttps_service:tls:Tls.Config.server->?config:Httpaf.Config.t->error_handler:(dst->Httpaf.Server_connection.error_handler)->(TLS.flow->dst->Httpaf.Server_connection.request_handler)->tPaf.servicevalalpn_service:tls:Tls.Config.server->?config:Httpaf.Config.t*H2.Config.t->(TLS.flow,dst)Alpn.server_handler->tPaf.servicevalserve:?stop:Lwt_switch.t->'tPaf.service->'t->[`InitializedofunitLwt.t]endmoduleMake(Stack:Tcpip.Tcp.S):Swithtypestack=Stack.tandtypeipaddr=Stack.ipaddr=structopenLwt.Infixtypeipaddr=Stack.ipaddrtypedst=ipaddr*intmoduleTCP=structletsrc=Logs.Src.create"paf-tcp"moduleLog=(valLogs.src_logsrc:Logs.LOG)includeStacktypenonrecflow={flow:flow;mutableno_close:bool}typeendpoint=Stack.t*Stack.ipaddr*inttypenonrecwrite_error=[`Writeofwrite_error|`Connectoferror|`Closed]letpp_write_errorppf=function|`Writeerr|(`Closedaserr)->pp_write_errorppferr|`Connecterr->pp_errorppferrletreadflow=readflow.flowletdstflow=dstflow.flowletwriteflowcs=writeflow.flowcs>>=function|Ok_asv->Lwt.returnv|Errorerr->Lwt.return_error(`Writeerr)letwritevflowcss=writevflow.flowcss>>=function|Ok_asv->Lwt.returnv|Errorerr->Lwt.return_error(`Writeerr)letconnect(stack,ipaddr,port)=create_connectionstack(ipaddr,port)>>=function|Okflow->Lwt.return_ok{flow;no_close=false}|Errorerr->Lwt.return_error(`Connecterr)letno_closeflow=flow.no_close<-trueletto_closeflow=flow.no_close<-falseletcloseflow=matchflow.no_closewith|true->Log.debug(funm->m"Fakely close the connection.");Lwt.return_unit|false->Log.debug(funm->m"Really close the connection.");closeflow.flowletshutdownflow=shutdownflow.flowendmoduleTLS=structletsrc=Logs.Src.create"paf-tls"moduleLog=(valLogs.src_logsrc:Logs.LOG)includeTls_mirage.Make(TCP)typeendpoint=[`host]Domain_name.toption*Tls.Config.client*Stack.t*Stack.ipaddr*inttypenonrecflow=TCP.flow*flowletconnect(host,cfg,stack,ipaddr,port)=Stack.create_connectionstack(ipaddr,port)>>=function|Errorerr->Lwt.return_error(`Readerr)|Okflow->letopenLwt_result.Infixinlettcp_flow={TCP.flow;TCP.no_close=false}inclient_of_flowcfg?hosttcp_flow>>=funtls_flow->Lwt.return_ok(tcp_flow,tls_flow)letno_close(tcp_flow,_)=TCP.no_closetcp_flowletto_close(tcp_flow,_)=TCP.to_closetcp_flowletread(_,tls_flow)=readtls_flowletwrite(_,tls_flow)=writetls_flowletwritev(_,tls_flow)=writevtls_flowletshutdown(_,tls_flow)=shutdowntls_flowletepoch(_,tls_flow)=epochtls_flowletreneg?authenticator?acceptable_cas?cert?drop(_,tls_flow)=reneg?authenticator?acceptable_cas?cert?droptls_flowletkey_update?request(_,tls_flow)=key_update?requesttls_flowletserver_of_flowconfigtcp_flow=Lwt_result.Infix.(server_of_flowconfigtcp_flow>>=funtls_flow->Lwt.return_ok(tcp_flow,tls_flow))letclient_of_flowconfig?hosttcp_flow=Lwt_result.Infix.(client_of_flowconfig?hosttcp_flow>>=funtls_flow->Lwt.return_ok(tcp_flow,tls_flow))letclose(tcp_flow,tls_flow)=matchtcp_flow.TCP.no_closewith|true->Lwt.return_unit|false->closetls_flowendletsrc=Logs.Src.create"paf-layer"moduleLog=(valLogs.src_logsrc:Logs.LOG)typestack=Stack.tlettcp_edn,tcp_protocol=Mimic.register~name:"tcp"(moduleTCP)lettls_edn,tls_protocol=Mimic.register~priority:10~name:"tls"(moduleTLS)typet={stack:Stack.t;queue:Stack.flowQueue.t;condition:unitLwt_condition.t;mutex:Lwt_mutex.t;mutableclosed:bool;}letinit~portstack=letqueue=Queue.create()inletcondition=Lwt_condition.create()inletmutex=Lwt_mutex.create()inletlistenerflow=Lwt_mutex.lockmutex>>=fun()->Queue.pushflowqueue;Lwt_condition.signalcondition();Lwt_mutex.unlockmutex;Lwt.return()inStack.listen~portstacklistener;Lwt.return{stack;queue;condition;mutex;closed=false}letrecaccept({queue;condition;mutex;_}ast)=Lwt_mutex.lockmutex>>=fun()->letrecawait()=ifQueue.is_emptyqueue&¬t.closedthenLwt_condition.waitcondition~mutex>>=awaitelseLwt.return_unitinawait()>>=fun()->matchQueue.popqueuewith|flow->Lwt_mutex.unlockmutex;Lwt.return_ok{TCP.flow;TCP.no_close=false}|exceptionQueue.Empty->ift.closedthen(Lwt_mutex.unlockmutex;Lwt.return_error`Closed)else(Lwt_mutex.unlockmutex;acceptt)letclose({condition;_}ast)=t.closed<-true;(* Stack.disconnect stack >>= fun () -> *)Lwt_condition.signalcondition();Lwt.return_unitlethttp_service?config~error_handlerrequest_handler=letmoduleR=(valMimic.reprtcp_protocol)inletconnectionflow=letdst=TCP.dstflowinleterror_handler=error_handlerdstinletrequest_handler'reqd=request_handlerflowdstreqdinletconn=Httpaf.Server_connection.create?config~error_handlerrequest_handler'inLwt.return_ok(R.Tflow,Paf.Runtime((moduleHttpaf.Server_connection),conn))inPaf.serviceconnectionLwt.return_okacceptcloselethttps_service~tls?config~error_handlerrequest_handler=letmoduleR=(valMimic.reprtls_protocol)inlethandshaketcp_flow=letdst=TCP.dsttcp_flowinTLS.server_of_flowtlstcp_flow>>=function|Okflow->Lwt.return_ok(dst,flow)|Error`Closed->(* XXX(dinosaure): be care! [`Closed] at this stage does not mean
* that the bound socket is closed but the socket with the peer is
* closed. *)Log.err(funm->m"The connection was closed by peer.");TCP.closetcp_flow>>=fun()->Lwt.return_error`Closed|Errorerr->Log.err(funm->m"Got a TLS error: %a."TLS.pp_write_errorerr);TCP.closetcp_flow>>=fun()->Lwt.return_errorerrinletconnection(dst,flow)=leterror_handler=error_handlerdstinletrequest_handler'reqd=request_handlerflowdstreqdinletconn=Httpaf.Server_connection.create?config~error_handlerrequest_handler'inLwt.return_ok(R.Tflow,Paf.Runtime((moduleHttpaf.Server_connection),conn))inPaf.serviceconnectionhandshakeacceptcloseletalpn=letmoduleR=(valMimic.reprtls_protocol)inletalpn_of_tls_connection(_edn,flow)=matchTLS.epochflowwith|Ok{Tls.Core.alpn_protocol;_}->alpn_protocol|Error_->Noneinletpeer_of_tls_connection(edn,_flow)=ednin(* XXX(dinosaure): [TLS]/[ocaml-tls] should let us to project the underlying
* [flow] and apply [TCP.dst] on it.
* Actually, we did it with the [TLS] module. *)letinjection(_edn,flow)=R.Tflowin{Alpn.alpn=alpn_of_tls_connection;Alpn.peer=peer_of_tls_connection;Alpn.injection;}letalpn_service~tls?config:(_=(Httpaf.Config.default,H2.Config.default))handler=lethandshaketcp_flow=letdst=TCP.dsttcp_flowinTLS.server_of_flowtlstcp_flow>>=function|Okflow->Lwt.return_ok(dst,flow)|Error`Closed->(* XXX(dinosaure): be care! [`Closed] at this stage does not mean
* that the bound socket is closed but the socket with the peer is
* closed. *)Log.err(funm->m"The connection was closed by peer.");Lwt.return_error(`Write`Closed)|Errorerr->Log.err(funm->m"Got a TLS error: %a."TLS.pp_write_errorerr);TCP.closetcp_flow>>=fun()->Lwt.return_error(err:>[TLS.write_error|`Msgofstring])inletmoduleR=(valMimic.reprtls_protocol)inletrequestflowednreqdprotocol=matchflowwith|R.Tflow->handler.Alpn.requestflowednreqdprotocol|_->assertfalse(* XXX(dinosaure): this case should never occur. Indeed, the [injection]
given to [Alpn.service] only create a [tls_protocol] flow. We just
destruct it and give it to [request_handler]. *)inAlpn.servicealpn{handlerwithrequest}handshakeacceptcloseletserve?stopservicet=Paf.serve?stopservicetendtypetransmission=[`Clear|`TLSofstringoption]letpaf_transmission:transmissionMimic.value=Mimic.make~name:"paf-transmission"letpaf_endpoint:(Ipaddr.t*int)Mimic.value=Mimic.make~name:"paf-endpoint"openLwt.Infixletreckind_of_flow:Mimic.ednlist->transmissionoption=function|Mimic.Edn(k,v)::r->(matchMimic.equalkpaf_transmissionwith|SomeMimic.Refl->Somev|None->kind_of_flowr)|[]->Noneletrecendpoint_of_flow:Mimic.ednlist->(Ipaddr.t*int)option=function|Mimic.Edn(k,v)::r->(matchMimic.equalkpaf_endpointwith|SomeMimic.Refl->Somev|None->endpoint_of_flowr)|[]->Nonelet(>>?)=Lwt_result.bindletrun~ctxhandlerrequest=Mimic.unfoldctx>>?funress->Mimic.connectress>>=funres->match(res,kind_of_flowress)with|(Error_aserr),_->Lwt.returnerr|Okflow,(Some`Clear|None)->letedn=endpoint_of_flowressinletalpn=matchrequestwith`V1_->"http/1.1"|`V2_->"h2c"inAlpn.run~alpnhandlerednrequestflow|Okflow,Some(`TLSalpn)->letedn=endpoint_of_flowressinAlpn.run?alpnhandlerednrequestflow