123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285openCoreopenAsyncmoduleError=structtypet=|Fatal_parse_failureofstring|Invalid_rpc_messageofMsgpack.t|Response_for_unknown_requestofMsgpack.t[@@derivingsexp_of]letto_errort=Error.create_s[%sexp(t:t)]endmoduleRequest_handler=structtypet=|T:{f:Msgpack.tlist->(Msgpack.tOr_error.t*'a)Deferred.t;on_response_sent:'a->unit}->tendmoduleConnection_closed_reason=structtypet=|Reader_reached_eof|Read_failureofCore.Error.t|Write_failureofCore.Error.tendtypet={reader:Reader.tSet_once.t;writer:Writer.tSet_once.t;connection_closed:(Connection_closed_reason.t->unit)Bus.Read_write.t;pending_requests:(Msgpack.t,Msgpack.t)Result.tIvar.tInt.Table.t;request_handlers:Request_handler.tString.Table.t;notification_handlers:(Msgpack.tlist->unit)String.Table.t;create_id:unit->Int63.t;on_error:Error.t->unit;mutabledefault_notification_handler:name:string->Msgpack.tlist->unit}letreadert=matchSet_once.gett.readerwith|Somereader->reader|None->raise_s[%message"Tried to get reader for Msgpack RPC before calling [connect]"];;letwritert=matchSet_once.gett.writerwith|Somewriter->writer|None->raise_s[%message"Tried to get writer for Msgpack RPC before calling [connect]"];;letclose_connectiontreason=matchBus.is_closedt.connection_closedwith|true->()|false->Bus.writet.connection_closedreason;Bus.closet.connection_closed;letreader=Set_once.get_exnt.readerinletwriter=Set_once.get_exnt.writerin(* We can close the [Reader.t] because even though there still may be unconsumed data,
we have already responded to all the pending requests. *)don't_wait_for(let%map()=Writer.closewriterand()=Reader.closereaderin());;letevent_loopt=letwriter=writertinlethandle_messagemsg=matchmsgwith|Msgpack.Array[Int1;Intmsgid;Nil;result]->(matchHashtbl.findt.pending_requestsmsgidwith|None->t.on_error(Response_for_unknown_requestmsg)|Somebox->Ivar.fill_exnbox(Okresult))|Array[Int1;Intmsgid;err;Nil]->(matchHashtbl.findt.pending_requestsmsgidwith|None->t.on_error(Response_for_unknown_requestmsg)|Somebox->Ivar.fill_exnbox(Errorerr))|Array[Int2;Stringmethod_name;Arrayparams]->(matchHashtbl.findt.notification_handlersmethod_namewith|None->t.default_notification_handler~name:method_nameparams|Somef->fparams)|Array[Int0;Intmsgid;Stringmethod_name;Arrayparams]->letwrite_responseresult~on_response_sentx=matchWriter.is_closedwriterwith|true->return()|false->letresult:Msgpack.tlist=matchresultwith|`Unknown_method->[String(sprintf"Unknown method %s"method_name);Nil]|`Result(Okresult)->[Nil;result]|`Result(Errorerror)->[String(Core.Error.to_string_humerror);Nil]inletmessage=Msgpack.string_of_t_exn(Array(Int1::Intmsgid::result))inWriter.writewritermessage;let%map()=Writer.flushedwriterinon_response_sentxindon't_wait_for(matchHashtbl.findt.request_handlersmethod_namewith|None->write_response`Unknown_method~on_response_sent:ignore()|Some(T{f;on_response_sent})->let%bindresult,x=fparamsinwrite_response(`Resultresult)~on_response_sentx)|_->t.on_error(Invalid_rpc_messagemsg)inlet%mapparse_result=Angstrom_async.parse_manyMsgpack.Internal.Parser.msg(funmsg->(* Force synchronous message handling. Note that if a request or notification
handler raises, we don't do anything special to handle it. Clients that want to
prevent raising can use [Monitor.try_with] inside their handlers. If
[handle_message] raises due to its own logic, that's a bug. *)handle_messagemsg;return())(readert)inlet(reason:Connection_closed_reason.t)=matchparse_resultwith|Ok()->Reader_reached_eof|Errorparse_failure_details->t.on_error(Fatal_parse_failureparse_failure_details);Read_failure(Core.Error.of_string[%string"Connection closed due to fatal parse failure: %{parse_failure_details}"])inclose_connectiontreason;;letconnecttreaderwriter=Set_once.set_exnt.readerreader;Set_once.set_exnt.writerwriter;don't_wait_for(event_loopt);(* If the [write] system call fails, close the connection. *)don't_wait_for(let%mapexn=Monitor.detach_and_get_next_error(Writer.monitorwriter)inclose_connectiont(Write_failure(Core.Error.of_exn(Monitor.extract_exnexn))));;letcreate~on_error=letconnection_closed=(* Although we will only ever write a single value to [connection_closed], we
implement it as a bus rather than as an ivar to allow listeners to unsubscribe,
thereby avoiding a memory leak. *)Bus.create_exn~on_subscription_after_first_write:Raise~on_callback_raise:Core.Error.raise()inletmoduleId_factory=Unique_id.Int63()in{reader=Set_once.create();writer=Set_once.create();connection_closed;pending_requests=Int.Table.create();request_handlers=String.Table.create();notification_handlers=String.Table.create();create_id=(fun()->(Id_factory.create():>Int63.t));on_error;default_notification_handler=(fun~name:__->())};;letto_native_uint32=letmask=matchWord_size.word_sizewith|W32->Int63.of_int0x3FFFFFFF|W64->Int63.of_int64_trunc0xFFFFFFFFLinfunx->Int63.(xlandmask|>to_int_exn);;letwrite_messaget~query~on_successful_flush=letwriter=writertinmatchWriter.is_closedwriterwith|true->Deferred.Or_error.error_s[%message"Failed to send Msgpack RPC message: writer is closed"~_:(query:Msgpack.t)]|false->letconnection_closed=Ivar.create()inletsubscriber=(* We subscribe to [t.connection_closed] before calling [write] to avoid a race
where the write fails and we miss the message that the connection was closed. *)Bus.subscribe_exnt.connection_closed~f:(Ivar.fill_exnconnection_closed)inletconnection_closed=Ivar.readconnection_closedinWriter.writewriter(Msgpack.string_of_t_exnquery);let%mapresult=match%bindWriter.flushed_or_failed_with_resultwriterwith|Flushed_->on_successful_flush~connection_closed|Force_closed->Deferred.Or_error.error_s[%message"Failed to send Msgpack RPC message: writer is closed"~_:(query:Msgpack.t)]|Consumer_left->failwith"BUG: [Consumer_left] should only be possible when \
[raise_when_consumer_leaves] is set to false."|Error->(* The write side of the connection closing can race with the read side - we
return whichever error is reported first. *)(match%mapconnection_closedwith|Read_failureerror|Write_failureerror->Or_error.error_s[%message"Failed to send Msgpack RPC message"~_:(query:Msgpack.t)(error:Core.Error.t)]|Reader_reached_eof->Or_error.error_s[%message"Failed to send Msgpack RPC message: consumer left"~_:(query:Msgpack.t)])inBus.unsubscribet.connection_closedsubscriber;result;;letcallt~method_name~parameters=letmsg_id=to_native_uint32(t.create_id())inletrequest=Msgpack.Array[Int0;Intmsg_id;Stringmethod_name;Arrayparameters]inletresult=Ivar.create()inHashtbl.sett.pending_requests~key:msg_id~data:result;let%mapresult=write_messaget~query:request~on_successful_flush:(fun~connection_closed->choose[choiceconnection_closed(function|Read_failureerror->Or_error.error_s[%message"While this request was waiting for a response, a message was received \
that failed to parse"(request:Msgpack.t)(error:Core.Error.t)]|Reader_reached_eof->Or_error.error_s[%message"Consumer left without responding"(request:Msgpack.t)]|Write_failureunrelated_error->Or_error.error_s[%message"While this request was waiting for a response, an unrelated write \
failed and caused the connection to close."(request:Msgpack.t)(unrelated_error:Core.Error.t)]);choice(Ivar.readresult)(funresult->Okresult)])inHashtbl.removet.pending_requestsmsg_id;result;;letnotifyt~method_name~parameters=letquery=Msgpack.Array[Int2;Stringmethod_name;Arrayparameters]inwrite_messaget~query~on_successful_flush:(fun~connection_closed:_->Deferred.Or_error.return());;letregister_request_handlert~name~f~on_response_sent=Hashtbl.addt.request_handlers~key:name~data:(T{f;on_response_sent});;letregister_notification_handlert~name~f=Hashtbl.addt.notification_handlers~key:name~data:f;;letset_default_notification_handlert~f=t.default_notification_handler<-fmoduleExpert=structletunregister_request_handlert~name=Hashtbl.removet.request_handlersnameletunregister_notification_handlert~name=Hashtbl.removet.notification_handlersname;;letregistered_request_handlerst=Hashtbl.keyst.request_handlersletregistered_notification_handlerst=Hashtbl.keyst.notification_handlersend