123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142openLwt.Infixletmsgffmt=Fmt.kstr(funmsg->`Msgmsg)fmtletpp_errorppf=function|#Httpaf.Status.tascode->Httpaf.Status.pp_humppfcode|`Exnexn->Fmt.pfppf"exception %s"(Printexc.to_stringexn)moduleMake(Time:Mirage_time.S)(Stack:Tcpip.Stack.V4V6)(Random:Mirage_crypto_rng_mirage.S)(Mclock:Mirage_clock.MCLOCK)(Pclock:Mirage_clock.PCLOCK)=structmodulePaf=Paf_mirage.Make(Stack.TCP)moduleLE=LE.Make(Time)(Stack)letget_certificates~yes_my_port_80_is_reachable_and_unused:stackv4v6~productionconfighttp=Paf.init~port:80(Stack.tcpstackv4v6)>>=funt->let`Initializedweb_server,stop_web_server=letrequest_handler_=LE.request_handlerinleterror_handler_dst?requesterr_=Logs.err(funm->m"error %a while processing request %a"pp_errorerrFmt.(option~none:(any"unknown")Httpaf.Request.pp_hum)request)inletstop=Lwt_switch.create()in(Paf.serve~stop(Paf.http_service~error_handlerrequest_handler)t,stop)inLogs.info(funm->m"listening on 80/HTTP (let's encrypt provisioning)");letprovision_certificate=(* XXX(dinosaure): we assume that [provision_certificate] terminates.
By this way, we are able to stop our web-server and resolve our
[Lwt.both]. *)LE.provision_certificate~productionconfighttp>>=funv->Lwt_switch.turn_offstop_web_server>>=fun()->Lwt.returnvinLwt.bothweb_serverprovision_certificate>|=sndletredirectconfigtls_portreqd=letrequest=Httpaf.Reqd.requestreqdinlethost=matchHttpaf.Headers.getrequest.Httpaf.Request.headers"host"with|Somehost->host|None->Domain_name.to_stringconfig.LE.hostnameinletresponse=letport=iftls_port=443thenNoneelseSometls_portinleturi=Fmt.str"https://%s%a%s"hostFmt.(option~none:nop(fmt":%d"))portrequest.Httpaf.Request.targetinletheaders=Httpaf.Headers.of_list[("location",uri);("connection","close")]inHttpaf.Response.create~headers`Moved_permanentlyinHttpaf.Reqd.respond_with_stringreqdresponse""letinfo=letmoduleR=(valMimic.reprPaf.tls_protocol)inletalpn_of_tls_connection(_edn,flow)=matchPaf.TLS.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;}letwith_lets_encrypt_certificates?(port=443)?(alpn_protocols=["http/1.1";"h2"])stackv4v6~productionconfigclienthandler=letcertificates=refNoneinletstop_http_server=Lwt_switch.create()inletstop_alpn_server=Lwt_switch.create()inletmutex=Lwt_mutex.create()inletrecfill_certificates()=LE.provision_certificate~productionconfigclient>>=function|Error_aserr->Lwt_switch.turn_offstop_http_server>>=fun()->Lwt_switch.turn_offstop_alpn_server>>=fun()->Lwt.returnerr|Okv->Lwt_mutex.with_lockmutex(fun()->certificates:=Somev;Lwt.return_unit)>>=fun()->(* TODO(dinosaure): should we [reneg] all previous connections? *)Time.sleep_ns(Duration.of_day80)>>=fill_certificatesinlethandshaketcp=Lwt_mutex.with_lockmutex(fun()->Lwt.return!certificates)>>=function|None->Lwt.return_error`No_certificates|Somecertificates->(matchTls.Config.server~alpn_protocols~certificates()with|Error`Msgmsg->Lwt.return_error(`Msgmsg)|Okcfg->Paf.TLS.server_of_flowcfgtcp>>=function|Okflow->Lwt.return_ok(Paf.TCP.dsttcp,flow)|Error`Closed->Lwt.return_error(`Write`Closed)|Errorerr->leterr=msgf"%a"Paf.TLS.pp_write_errorerrinPaf.TCP.closetcp>>=fun()->Lwt.return_errorerr)inletmoduleR=(valMimic.reprPaf.tls_protocol)inletrequestflowednreqdprotocol=matchflowwith|R.Tflow->handler.Alpn.requestflowednreqdprotocol|_->assertfalseinletalpn_service=Alpn.serviceinfo{handlerwithrequest}handshakePaf.acceptPaf.closeinlethttp_service=letrequest_handler_ednreqd=letrequest=Httpaf.Reqd.requestreqdinmatchString.split_on_char'/'request.Httpaf.Request.targetwith|["";_p1;_p2;_token]->LE.request_handlerednreqd|_->redirectconfigportreqdinleterror_handler_dst?requesterr_=Logs.err(funm->m"error %a while processing request %a"pp_errorerrFmt.(option~none:(any"unknown")Httpaf.Request.pp_hum)request)inPaf.http_service~error_handlerrequest_handlerinPaf.init~port:80(Stack.tcpstackv4v6)>>=funhttp->Paf.init~port(Stack.tcpstackv4v6)>>=funalpn->let(`Initializedhttp_server)=Paf.serve~stop:stop_http_serverhttp_servicehttpinlet(`Initializedalpn_server)=Paf.serve~stop:stop_alpn_serveralpn_servicealpninLwt.both(fill_certificates())(Lwt.join[http_server;alpn_server])>>=function|(Error_aserr),()->Lwt.returnerr|_->Lwt.return_ok()end