123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668includeSmart_git_intfmoduleVerbose=structtype'afiber='aLwt.tletsucc()=Lwt.return_unitletprint()=Lwt.return_unitendtypehandshake=uri0:Uri.t->uri1:Uri.t->Mimic.flow->unitLwt.tletgit_capabilities=Mimic.make~name:"git-capabilities"letgit_scheme=Mimic.make~name:"git-scheme"letgit_path=Mimic.make~name:"git-path"letgit_hostname=Mimic.make~name:"git-hostname"letgit_ssh_user=Mimic.make~name:"git-ssh-user"letgit_port=Mimic.make~name:"git-port"letgit_http_headers=Mimic.make~name:"git-http-headers"letgit_transmission:[`Git|`Exec|`HTTPofUri.t*handshake]Mimic.value=Mimic.make~name:"git-transmission"letgit_uri=Mimic.make~name:"git-uri"moduleEndpoint=structtypet={scheme:[`SSHofstring|`Git|`HTTPof(string*string)list|`HTTPSof(string*string)list|`Schemeofstring];port:intoption;path:string;hostname:string;}letppppfedn=matchednwith|{scheme=`SSHuser;path;hostname;_}->Fmt.pfppf"%s@%s:%s"userhostnamepath|{scheme=`Git;port;path;hostname}->Fmt.pfppf"git://%s%a/%s"hostnameFmt.(option(conststring":"++int))portpath|{scheme=`HTTP_;path;port;hostname}->Fmt.pfppf"http://%s%a%s"hostnameFmt.(option(conststring":"++int))portpath|{scheme=`HTTPS_;path;port;hostname}->Fmt.pfppf"https://%s%a%s"hostnameFmt.(option(conststring":"++int))portpath|{scheme=`Schemescheme;path;port;hostname}->Fmt.pfppf"%s://%s%a/%s"schemehostnameFmt.(option(conststring":"++int))portpathletheaders_from_uriuri=matchUri.useruri,Uri.passworduriwith|Someuser,Somepassword->letraw=Base64.encode_exn(Fmt.str"%s:%s"userpassword)in["Authorization",Fmt.str"Basic %s"raw]|_->[]letof_stringstr=letopenRresultinletparse_sshstr=letlen=String.lengthstrinEmile.of_string_raw~off:0~lenstr|>R.reword_error(R.msgf"%a"Emile.pp_error)>>=fun(consumed,m)->matchAstring.String.cut~sep:":"(String.substrconsumed(len-consumed))with|Some("",path)->letlocal=List.map(function`Atomx->x|`Stringx->Fmt.str"%S"x)m.Emile.localinletuser=String.concat"."localinlethostname=matchfstm.Emile.domainwith|`Domainvs->String.concat"."vs|`Literalv->v|`Addr(Emile.IPv4v)->Ipaddr.V4.to_stringv|`Addr(Emile.IPv6v)->Ipaddr.V6.to_stringv|`Addr(Emile.Ext(k,v))->Fmt.str"%s:%s"kvinR.ok{scheme=`SSHuser;path;port=None;hostname}|_->R.error_msg"Invalid SSH pattern"inletparse_uristr=leturi=Uri.of_stringstrinletpath=Uri.pathuriinmatchUri.schemeuri,Uri.hosturi,Uri.porturiwith|Some"git",Somehostname,port->R.ok{scheme=`Git;path;port;hostname}|Some"http",Somehostname,port->R.ok{scheme=`HTTP(headers_from_uriuri);path;port;hostname}|Some"https",Somehostname,port->R.ok{scheme=`HTTPS(headers_from_uriuri);path;port;hostname}|Somescheme,Somehostname,port->R.ok{scheme=`Schemescheme;path;port;hostname}|_->R.error_msgf"Invalid uri: %a"Uri.ppuriinmatchparse_sshstr,parse_uristrwith|Okv,_->Okv|_,Okv->Okv|Error_,Error_->R.error_msgf"Invalid endpoint: %s\n\
The format of it corresponds to:\n\
1) a SSH endpoint like: user@hostname:repository\n\
2) a Git endpoint like: git://hostname(:port)?/repository\n\
3) a HTTP endpoint like: \
http(s)?://(user:password@)?hostname(:port)?/repository\n\
4) an URI with a special scheme like: \
[scheme]://hostname(:port)?/repository\n\n\
It's not possible to specify a port if you use an SSH endpoint and \
it's not\n\
possible to specify an user and its password if you use a Git or an \
URI with\n\
a special scheme endpoint."strletwith_headers_if_httpheaders({scheme;_}asedn)=matchschemewith|`SSH_|`Git|`Scheme_->edn|`HTTP_->{ednwithscheme=`HTTPheaders}|`HTTPS_->{ednwithscheme=`HTTPSheaders}letto_ctxednctx=letscheme=matchedn.schemewith|`Git->`Git|`SSH_->`SSH|`HTTP_->`HTTP|`HTTPS_->`HTTPS|`Schemescheme->`Schemeschemeinletheaders=matchedn.schemewith|`HTTPheaders|`HTTPSheaders->Someheaders|_->Noneinletssh_user=matchedn.schemewith`SSHuser->Someuser|_->Nonein(* XXX(dinosaure): I don't like the reconstruction of the given [Uri.t] when we can miss some informations. *)leturi=matchedn.schemewith|`HTTP_->Some(Uri.of_string(Fmt.str"http://%s%a%s"edn.hostnameFmt.(option(conststring":"++int))edn.portedn.path))|`HTTPS_->Some(Uri.of_string(Fmt.str"https://%s%a%s"edn.hostnameFmt.(option(conststring":"++int))edn.portedn.path))|_->Noneinctx|>Mimic.addgit_schemescheme|>Mimic.addgit_pathedn.path|>Mimic.addgit_hostnameedn.hostname|>functx->Option.fold~none:ctx~some:(funv->Mimic.addgit_ssh_uservctx)ssh_user|>functx->Option.fold~none:ctx~some:(funv->Mimic.addgit_portvctx)edn.port|>functx->Option.fold~none:ctx~some:(funv->Mimic.addgit_urivctx)uri|>functx->Option.fold~none:ctx~some:(funv->Mimic.addgit_http_headersvctx)headersendmoduleMake(Scheduler:Sigs.SCHEDwithtype+'as='aLwt.t)(Pack:APPENDwithtype+'afiber='aLwt.t)(Index:APPENDwithtype+'afiber='aLwt.t)(Uid:UID)(Ref:Sigs.REF)=structletsrc=Logs.Src.create"git-fetch"moduleLog=(valLogs.src_logsrc:Logs.LOG)moduleThin=Carton_lwt.Thin.Make(Uid)letfs=letopenRresultinletopenLwt.InfixinThin.{create=(fun?trunctpath->Pack.create?trunc~mode:Pack.RdWrtpath>|=R.reword_error(R.msgf"%a"Pack.pp_error));append=Pack.append;map=Pack.map;close=(funtfd->Pack.closetfd>|=R.reword_error(R.msgf"%a"Pack.pp_error));}(* XXX(dinosaure): abstract it? *)letdigest:kind:[`A|`B|`C|`D]->?off:int->?len:int->Bigstringaf.t->Uid.t=fun~kind?(off=0)?lenbuf->letlen=matchlenwithSomelen->len|None->Bigstringaf.lengthbuf-offinletctx=Uid.emptyinletfeed_stringctxstr=letoff=0andlen=String.lengthstrinUid.feedctx(Bigstringaf.of_string~off~lenstr)inletctx=matchkindwith|`A->feed_stringctx(Fmt.str"commit %d\000"len)|`B->feed_stringctx(Fmt.str"tree %d\000"len)|`C->feed_stringctx(Fmt.str"blob %d\000"len)|`D->feed_stringctx(Fmt.str"tag %d\000"len)inletctx=Uid.feedctx~off~lenbufinUid.getctxlet(>>?)=Lwt_result.bindmoduleCartonSched=Carton.Make(Lwt)letfinish_itt~pack~weight~whereoffsets=letopenLwt.InfixinLog.debug(funm->m"Start to finish the canonicalized PACK file.");Pack.create~trunc:false~mode:Pack.Rdtpack>>?funfd->letzl_buffer=De.bigstring_createDe.io_buffer_sizeinletallocatebits=De.make_window~bitsinletpack=Carton.Dec.makefd~allocate~z:zl_buffer~uid_ln:Uid.length~uid_rw:Uid.of_raw_string(funuid->Hashtbl.findwhereuid)inletmapfd~poslen=letmax=Int64.subweightposinletlen=minmax(Int64.of_intlen)inletlen=Int64.to_intleninPack.maptfd~posleninletrecgoentries=function|[]->Lwt.returnentries|(offset,crc)::offsets->Lwt.catch(fun()->Log.debug(funm->m"Get object at %08Lx."offset);letweight=Carton.Dec.weight_of_offset~mappack~weight:Carton.Dec.nulloffsetinletraw=Carton.Dec.make_raw~weightinletv=Carton.Dec.of_offset~mappackraw~cursor:offsetinletkind=Carton.Dec.kindvinletraw=Carton.Dec.rawvinletlen=Carton.Dec.lenvinletuid=digest~kind~off:0~lenrawingo({Carton.Dec.Idx.offset;crc;uid}::entries)offsets)(funexn->Printexc.print_backtracestdout;Lwt.failexn)ingo[]offsets>>=funentries->Pack.closetfd>>?fun()->Lwt.return_okentriesletrun_pck?threads~light_load~heavy_loadstreamt~src~dst=letopenRresultinletopenLwt.InfixinLwt.catch(fun()->Log.debug(funm->m"Start to verify the given stream.");Thin.verify~digest?threadstsrcfsstream)(function|Failureerr->Lwt.return_error(R.msgerr)|Invalid_argumenterr->Lwt.return_error(R.msgerr)|exn->Printexc.print_backtracestdout;Lwt.return_error(`Exnexn))>>=function|Error_aserr->Lwt.returnerr|Ok(_,[],[],entries,_weight,uid)->Log.debug(funm->m"Given PACK file is not thin, move it!");Pack.movet~src~dst>|=R.reword_error(R.msgf"%a"Pack.pp_error)>>?fun()->Lwt.return_ok(uid,Array.of_listentries)|Ok(n,uids,unresolveds,entries,weight,_uid)->Log.debug(funm->m"Given PACK file is thin, canonicalize!");Thin.canonicalize~light_load~heavy_load~src~dsttfsnuidsweight>>?fun(shift,weight,uid,entries')->letwhere=Hashtbl.create0x100inletentries=letfold({Carton.Dec.Idx.offset;uid;_}asentry)=letoffset=Int64.addoffsetshiftinHashtbl.addwhereuidoffset;{entrywithCarton.Dec.Idx.offset}inList.mapfoldentriesinList.iter(fun{Carton.Dec.Idx.offset;uid;_}->Hashtbl.addwhereuidoffset)entries';letunresolveds=letfold(offset,crc)=Int64.addoffsetshift,crcinList.mapfoldunresolvedsinfinish_it~pack:dst~weight~wheretunresolveds>|=R.reword_error(R.msgf"%a"Pack.pp_error)>>?funentries''->Log.debug(funm->m"PACK canonicalized.");letentries=List.rev_appendentries'entriesinletentries=List.rev_appendentries''entriesinLwt.return_ok(uid,Array.of_listentries)moduleEnc=Carton.Dec.Idx.N(Uid)letrun_idxt~dst~packentries=letopenLwt.Infixinletencoder=Enc.encoder`Manual~packentriesinletbuf=Bigstringaf.createDe.io_buffer_sizeinEnc.dstencoderbuf0(Bigstringaf.lengthbuf);Index.create~trunc:true~mode:Index.Wrtdst>>?funfd->letrecgo=function|`Partial->letlen=Bigstringaf.lengthbuf-Enc.dst_remencoderinIndex.appendtfd(Bigstringaf.substringbuf~off:0~len)>>=fun()->Enc.dstencoderbuf0(Bigstringaf.lengthbuf);go(Enc.encodeencoder`Await)|`Ok->Lwt.return_ok()ingo(Enc.encodeencoder`Await)>>?fun()->Index.closetfdletrun?threads~light_load~heavy_loadstreamt_pckt_idx~src~dst~idx=letopenRresultinletopenLwt.Infixinrun_pck?threads~light_load~heavy_loadstreamt_pck~src~dst>>?fun(pack,entries)->run_idxt_idx~dst:idx~packentries>|=R.reword_error(R.msgf"%a"Index.pp_error)>>?fun()->Lwt.return_okpackmoduleFlow=Unixiz.Make(Mimic)moduleFetch=Nss.Fetch.Make(Scheduler)(Lwt)(Flow)(Uid)(Ref)modulePush=Nss.Push.Make(Scheduler)(Lwt)(Flow)(Uid)(Ref)letfetch_v1?(uses_git_transport=false)~push_stdout~push_stderr~capabilitiespathflow?deepen?wanthostnamestoreaccessfetch_cfgpack=letopenLwt.InfixinLwt.try_bind(fun()->Fetch.fetch_v1~uses_git_transport~push_stdout~push_stderr~capabilities?deepen?want~host:hostnamepath(Flow.makeflow)storeaccessfetch_cfg@@fun(payload,off,len)->letv=String.subpayloadoffleninpack(Some(v,0,len)))(funrefs->packNone>>=fun()->Mimic.closeflow>>=fun()->Lwt.return_okrefs)@@funexn->packNone>>=fun()->Mimic.closeflow>>=fun()->Lwt.failexnletdefault_capabilities=[`Side_band_64k;`Multi_ack_detailed;`Ofs_delta;`Thin_pack;`Report_status;]typetransmission=[`Git|`Exec]letrecget_transmission:Mimic.ednlist->[`Git|`Exec|`HTTPofUri.t*handshake]option=function|Mimic.Edn(k,v)::r->(matchMimic.equalkgit_transmissionwith|SomeMimic.Refl->Somev|None->get_transmissionr)|[]->Noneletadd_unlesslstkv=matchList.assoc_opt(String.lowercase_asciik)lstwith|Some_->lst|None->(String.lowercase_asciik,v)::lstletpp_versionppf=function|`V1->Fmt.pfppf"1"|_->Fmt.pfppf"unknown"letadd_headers_for_fetching?(version=`V1)ctx=letheaders=Option.value~default:[](Mimic.getgit_http_headersctx)inletheaders=add_unlessheaders"content-type""application/x-git-upload-pack-request"inletheaders=add_unlessheaders"accept""application/x-git-upload-pack-result"inletheaders=add_unlessheaders"git-protocol"(Fmt.str"version=%a"pp_versionversion)inMimic.replacegit_http_headersheadersctxletfetch?(push_stdout=ignore)?(push_stderr=ignore)?(bounds=10)?threads~ctx(access,light_load,heavy_load)storeedn?(version=`V1)?(capabilities=default_capabilities)?deepenwantt_pckt_idx~src~dst~idx=letopenRresultinletopenLwt.Infixinlethostname=edn.Endpoint.hostnameinletpath=edn.Endpoint.pathinletstream,emitter=Lwt_stream.create_boundedboundsinletpusher_with_logging=function|Some(str,off,len)->Log.debug(funm->m"Download %d byte(s) of the PACK file."len);emitter#push(str,off,len)|None->Log.debug(funm->m"End of pack.");emitter#close;Lwt.return_unitinletstream()=Lwt_stream.getstreaminletctx=Mimic.addgit_capabilities`Rd(Endpoint.to_ctxednctx)inletctx=add_headers_for_fetching~versionctxinLwt.catch(fun()->Mimic.unfoldctx>>?funress->Mimic.connectress>>=funflow->matchflow,get_transmissionress,versionwith|Okflow,Some(#transmissionastransmission),`V1->(letfetch_cfg=Nss.Fetch.configurationcapabilitiesinletuses_git_transport=matchtransmissionwith`Git->true|`Exec->falseinLwt.both(fetch_v1~push_stdout~push_stderr~uses_git_transport~capabilitiespathflow?deepen~wanthostnamestoreaccessfetch_cfgpusher_with_logging)(run?threads~light_load~heavy_loadstreamt_pckt_idx~src~dst~idx)>>=fun(refs,idx)->matchrefs,idxwith|Okrefs,Okuid->Lwt.return_ok(`Pack(uid,refs))|(Error_aserr),_->Lwt.returnerr|Ok[],_->Lwt.return_ok`Empty|Ok_refs,(Error_aserr)->Lwt.returnerr)|Okflow,Some(`HTTP(uri,handshake)),`V1->(letfetch_cfg=Nss.Fetch.configuration~stateless:truecapabilitiesinleturi0=Fmt.str"%a/info/refs?service=git-upload-pack"Uri.ppuri|>Uri.of_stringinleturi1=Fmt.str"%a/git-upload-pack"Uri.ppuri|>Uri.of_stringinLwt.both(handshake~uri0~uri1flow>>=fun()->fetch_v1~push_stdout~push_stderr~capabilitiespathflow?deepen~wanthostnamestoreaccessfetch_cfgpusher_with_logging)(run~light_load~heavy_loadstreamt_pckt_idx~src~dst~idx)>>=fun(refs,idx)->matchrefs,idxwith|Okrefs,Okuid->Lwt.return_ok(`Pack(uid,refs))|(Error_aserr),_->Lwt.returnerr|Ok[],_->Lwt.return_ok`Empty|Ok_refs,(Error_aserr)->Lwt.returnerr)|Okflow,Some_,_->Log.err(funm->m"The protocol version is uninmplemented.");Mimic.closeflow>>=fun()->Lwt.return_error(`Msg"Version protocol unimplemented")|Okflow,None,_->Log.err(funm->m"A flow was allocated but we can not recognize the \
transmission.");Mimic.closeflow>>=fun()->Lwt.return_error(`Msg"Unrecognized protocol")|Errorerr,_,_->Log.err(funm->m"The Git peer is not reachable.");Lwt.return_errorerr)@@function|Failureerr->Lwt.return_error(R.msgerr)|exn->Lwt.return_error(`Exnexn)moduleDelta=Carton_lwt.Enc.Delta(Uid)(Verbose)letdeltify~light_load~heavy_load?(threads=4)(uids:Uid.tlist)=letopenLwt.Infixinletfold(uid:Uid.t)=light_loaduid>|=fun(kind,length)->Carton_lwt.Enc.make_entry~kind~lengthuidinLwt_list.map_pfolduids>|=Array.of_list>>=funentries->Delta.delta~threads:(List.initthreads(fun_thread->heavy_load))~weight:10~uid_ln:Uid.lengthentries>>=funtargets->Lwt.return(entries,targets)letheader=Bigstringaf.create12letpack~(heavy_load:Uid.tCarton_lwt.Enc.load)streamtargets=letopenLwt.Infixinletoffsets=Hashtbl.create(Array.lengthtargets)inletfinduid=matchHashtbl.findoffsetsuidwith|v->Lwt.return_somev|exceptionNot_found->Lwt.return_noneinletuid={Carton.Enc.uid_ln=Uid.length;Carton.Enc.uid_rw=Uid.to_raw_string}inletb={Carton.Enc.o=Bigstringaf.createDe.io_buffer_size;Carton.Enc.i=Bigstringaf.createDe.io_buffer_size;Carton.Enc.q=De.Queue.create0x10000;Carton.Enc.w=De.Lz77.make_window~bits:15;}inletctx=refUid.emptyinletcursor=ref0inCarton.Enc.header_of_pack~length:(Array.lengthtargets)header012;stream(Some(Bigstringaf.to_stringheader));ctx:=Uid.feed!ctxheader~off:0~len:12;cursor:=!cursor+12;letencode_targetstargets=letencode_targetidx=Hashtbl.addoffsets(Carton.Enc.target_uidtargets.(idx))!cursor;Carton_lwt.Enc.encode_target~b~find~load:heavy_load~uidtargets.(idx)~cursor:!cursor>>=fun(len,encoder)->letrecgoencoder=matchCarton.Enc.N.encode~o:b.oencoderwith|`Flush(encoder,len)->letpayload=Bigstringaf.substringb.o~off:0~leninstream(Somepayload);ctx:=Uid.feed!ctxb.o~off:0~len;cursor:=!cursor+len;letencoder=Carton.Enc.N.dstencoderb.o0(Bigstringaf.lengthb.o)ingoencoder|`End->Lwt.return()inletpayload=Bigstringaf.substringb.o~off:0~leninstream(Somepayload);ctx:=Uid.feed!ctxb.o~off:0~len;cursor:=!cursor+len;letencoder=Carton.Enc.N.dstencoderb.o0(Bigstringaf.lengthb.o)ingoencoderinletrecgoidx=ifidx<Array.lengthtargetsthenencode_targetidx>>=fun()->go(succidx)elseLwt.return()ingo0inencode_targetstargets>>=fun()->letuid=Uid.get!ctx|>Uid.to_raw_stringinstream(Someuid);streamNone;Lwt.return_unitletpack~light_load~heavy_loaduids=letopenLwt.Infixinletstream,pusher=Lwt_stream.create()inletfiber()=deltify~light_load~heavy_loaduids>>=fun(_,targets)->pack~heavy_loadpushertargetsinletstream()=Lwt_stream.getstreaminLwt.asyncfiber;streamletpush_v1?uses_git_transportflow~capabilitiespathcmdshostnamestoreaccesspush_cfgpack=letopenLwt.InfixinPush.push?uses_git_transport~capabilitiescmds~host:hostnamepath(Flow.makeflow)storeaccesspush_cfgpack>>=fun()->Mimic.closeflow>>=fun()->Lwt.return_ok()letadd_headers_for_pushing?(version=`V1)ctx=letheaders=Option.value~default:[](Mimic.getgit_http_headersctx)inletheaders=add_unlessheaders"content-type""application/x-git-receive-pack-request"inletheaders=add_unlessheaders"accept""application/x-git-receive-pack-result"inletheaders=add_unlessheaders"git-protocol"(Fmt.str"version=%a"pp_versionversion)inMimic.replacegit_http_headersheadersctxletpush~ctx(access,light_load,heavy_load)storeedn?(version=`V1)?(capabilities=default_capabilities)cmds=letopenRresultinletopenLwt.Infixinlethostname=edn.Endpoint.hostnameinletpath=edn.Endpoint.pathinletctx=Mimic.addgit_capabilities`Wr(Endpoint.to_ctxednctx)inletctx=add_headers_for_pushing~versionctxinLwt.catch(fun()->Mimic.unfoldctx>>?funress->Mimic.connectress>>=funres->matchres,get_transmissionress,versionwith|Okflow,Some(#transmissionastransmission),`V1->letpush_cfg=Nss.Push.configuration()inletuses_git_transport=matchtransmissionwith`Git->true|`Exec->falseinpush_v1~uses_git_transportflow~capabilitiespathcmdshostnamestoreaccesspush_cfg(pack~light_load~heavy_load)|Okflow,Some(`HTTP(uri,handshake)),`V1->letpush_cfg=Nss.Push.configuration~stateless:true()inleturi0=Fmt.str"%a/info/refs?service=git-receive-pack"Uri.ppuri|>Uri.of_stringinleturi1=Fmt.str"%a/git-receive-pack"Uri.ppuri|>Uri.of_stringinhandshake~uri0~uri1flow>>=fun()->push_v1flow~capabilitiespathcmdshostnamestoreaccesspush_cfg(pack~light_load~heavy_load)|Okflow,Some_,_->Log.err(funm->m"The protocol version is uninmplemented.");Mimic.closeflow>>=fun()->Lwt.return_error(`Msg"Version protocol unimplemented")|Okflow,None,_->Log.err(funm->m"A flow was allocated but we can not recognize the \
transmission.");Mimic.closeflow>>=fun()->Lwt.return_error(`Msg"Unrecognized protocol")|Errorerr,_,_->Log.err(funm->m"The Git peer is not reachable.");Lwt.return_errorerr)@@function|Failureerr->Lwt.return_error(R.msgerr)|exn->Lwt.return_error(`Exnexn)end