1use std::{
6 sync::{atomic::Ordering, Arc},
7 time::Duration,
8};
9
10use async_channel::Receiver;
11use log::{debug, error, info};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13use serde_json::json;
14
15use karyon_core::{
16 async_util::{select, timeout, Either, TaskResult},
17 util::random_32,
18};
19
20use karyon_net::{framed, Endpoint, FramedConn, MessageRx, MessageTx};
21
22#[cfg(feature = "ws")]
23use karyon_net::{layers::ws::WsConn, ClientLayer};
24
25use crate::{
26 client::{
27 message_dispatcher::MessageDispatcher,
28 subscriptions::{NotificationDecoder, Subscriptions},
29 Client, ClientBackend, ClientConfig, RequestID, WsCodec,
30 },
31 codec::JsonRpcCodec,
32 error::{Error, Result},
33 message,
34};
35
36#[cfg(feature = "ws")]
37use crate::codec::JsonRpcWsCodec;
38
39const OUTBOUND_BUFFER_SIZE: usize = 10;
43
44#[derive(Serialize, Deserialize)]
45#[serde(untagged)]
46enum NewMsg {
47 Notification(message::Notification),
48 Response(message::Response),
49}
50
51pub(super) async fn build_byte_backend<B>(
54 config: &ClientConfig,
55 codec: B,
56) -> Result<(ClientBackend, FramedConn<B>)>
57where
58 B: JsonRpcCodec,
59{
60 let conn = connect_byte(config, codec).await?;
61 let peer = conn.peer_endpoint();
62 info!("Successfully connected to the RPC server: {peer:?}");
63
64 let backend = build_backend_state(config);
65 Ok((backend, conn))
66}
67
68#[cfg(feature = "ws")]
70pub(super) async fn build_ws_backend<W>(
71 config: &ClientConfig,
72 codec: W,
73) -> Result<(ClientBackend, WsConn<W>)>
74where
75 W: JsonRpcWsCodec,
76{
77 let conn = connect_ws(config, codec).await?;
78 let peer = conn.peer_endpoint();
79 info!("Successfully connected to the RPC server: {peer:?}");
80
81 let backend = build_backend_state(config);
82 Ok((backend, conn))
83}
84
85fn build_backend_state(config: &ClientConfig) -> ClientBackend {
86 ClientBackend::Multiplexed {
87 message_dispatcher: MessageDispatcher::new(),
88 subscriptions: Subscriptions::new(config.subscription_buffer_size),
89 send_chan: async_channel::bounded(OUTBOUND_BUFFER_SIZE),
90 }
91}
92
93pub(super) fn start_io_loop<B, W, R, Wr>(client: &Arc<Client<B, W>>, reader: R, writer: Wr)
95where
96 B: JsonRpcCodec,
97 W: WsCodec,
98 R: MessageRx<Message = serde_json::Value> + Send + 'static,
99 Wr: MessageTx<Message = serde_json::Value> + Send + 'static,
100{
101 let this = client.clone();
102 client.task_group.spawn_then(
103 background_loop(client.clone(), reader, writer),
104 move |result: TaskResult<Result<()>>| async move {
105 if let TaskResult::Completed(Err(err)) = result {
106 error!("Client stopped: {err}");
107 }
108 this.disconnect.store(true, Ordering::Relaxed);
109 if let ClientBackend::Multiplexed {
110 subscriptions,
111 message_dispatcher,
112 ..
113 } = &this.backend
114 {
115 subscriptions.clear().await;
116 message_dispatcher.clear().await;
117 }
118 },
119 );
120}
121
122async fn background_loop<B, W, R, Wr>(
123 client: Arc<Client<B, W>>,
124 reader: R,
125 writer: Wr,
126) -> Result<()>
127where
128 B: JsonRpcCodec,
129 W: WsCodec,
130 R: MessageRx<Message = serde_json::Value> + Send,
131 Wr: MessageTx<Message = serde_json::Value> + Send,
132{
133 let ClientBackend::Multiplexed {
134 message_dispatcher,
135 subscriptions,
136 send_chan,
137 } = &client.backend
138 else {
139 return Err(Error::InvalidState("not in multiplexed mode".into()));
140 };
141
142 run_io_loop(
143 reader,
144 writer,
145 send_chan.1.clone(),
146 message_dispatcher,
147 subscriptions,
148 client.config.notification_decoder,
149 )
150 .await
151}
152
153async fn run_io_loop<R, Wr>(
154 mut reader: R,
155 mut writer: Wr,
156 outbound: Receiver<serde_json::Value>,
157 message_dispatcher: &MessageDispatcher,
158 subscriptions: &Subscriptions,
159 decoder: NotificationDecoder,
160) -> Result<()>
161where
162 R: MessageRx<Message = serde_json::Value> + Send,
163 Wr: MessageTx<Message = serde_json::Value> + Send,
164{
165 loop {
166 match select(outbound.recv(), reader.recv_msg()).await {
167 Either::Left(req) => {
168 writer.send_msg(req?).await?;
169 }
170 Either::Right(msg) => {
171 match handle_mux_msg(message_dispatcher, subscriptions, decoder, msg?).await {
172 Err(Error::SubscriptionBufferFull) => {
173 return Err(Error::SubscriptionBufferFull);
174 }
175 Err(err) => {
176 let ep = reader.peer_endpoint();
177 error!("Handle msg from {ep:?}: {err}");
178 }
179 Ok(_) => {}
180 }
181 }
182 }
183 }
184}
185
186async fn handle_mux_msg(
187 message_dispatcher: &MessageDispatcher,
188 subscriptions: &Subscriptions,
189 decoder: NotificationDecoder,
190 msg: serde_json::Value,
191) -> Result<()> {
192 match serde_json::from_value::<NewMsg>(msg.clone()) {
193 Ok(NewMsg::Response(res)) => {
194 debug!("<-- {res}");
195 message_dispatcher.dispatch(res).await
196 }
197 Ok(NewMsg::Notification(nt)) => {
198 debug!("<-- {nt}");
199 subscriptions.notify(decoder(nt)?).await
200 }
201 Err(err) => {
202 error!("Receive unexpected msg {msg}: {err}");
203 Err(Error::InvalidMsg("Unexpected msg".to_string()))
204 }
205 }
206}
207
208async fn connect_byte<B>(config: &ClientConfig, codec: B) -> Result<FramedConn<B>>
209where
210 B: JsonRpcCodec,
211{
212 let endpoint = config.endpoint.clone();
213
214 match &endpoint {
215 #[cfg(feature = "tcp")]
216 Endpoint::Tcp(..) => {
217 let stream = karyon_net::tcp::connect(&endpoint, config.tcp_config.clone()).await?;
218 Ok(framed(stream, codec))
219 }
220 #[cfg(feature = "tls")]
221 Endpoint::Tls(..) => {
222 let stream = karyon_net::tcp::connect(&endpoint, config.tcp_config.clone()).await?;
223 let tls_config = config.tls_config.as_ref().ok_or(Error::TLSConfigRequired)?;
224 let tls_layer = karyon_net::tls::TlsLayer::client(tls_config.clone());
225 let tls_stream = karyon_net::ClientLayer::handshake(&tls_layer, stream).await?;
226 Ok(framed(tls_stream, codec))
227 }
228 #[cfg(all(feature = "unix", target_family = "unix"))]
229 Endpoint::Unix(..) => {
230 let stream = karyon_net::unix::connect(&endpoint).await?;
231 Ok(framed(stream, codec))
232 }
233 _ => Err(Error::UnsupportedProtocol(endpoint.to_string())),
234 }
235}
236
237#[cfg(feature = "ws")]
238async fn connect_ws<W>(config: &ClientConfig, codec: W) -> Result<WsConn<W>>
239where
240 W: JsonRpcWsCodec,
241{
242 let endpoint = config.endpoint.clone();
243 let url = endpoint.to_string();
244
245 match &endpoint {
246 Endpoint::Ws(..) => {
247 let stream = karyon_net::tcp::connect(&endpoint, config.tcp_config.clone()).await?;
248 let layer = karyon_net::layers::ws::WsLayer::client(&url, codec);
249 Ok(ClientLayer::handshake(&layer, stream).await?)
250 }
251 #[cfg(feature = "tls")]
252 Endpoint::Wss(..) => {
253 let stream = karyon_net::tcp::connect(&endpoint, config.tcp_config.clone()).await?;
254 let tls_config = config.tls_config.as_ref().ok_or(Error::TLSConfigRequired)?;
255 let tls_layer = karyon_net::tls::TlsLayer::client(tls_config.clone());
256 let tls_stream = karyon_net::ClientLayer::handshake(&tls_layer, stream).await?;
257 let layer = karyon_net::layers::ws::WsLayer::client(&url, codec);
258 Ok(ClientLayer::handshake(&layer, tls_stream).await?)
259 }
260 _ => Err(Error::UnsupportedProtocol(endpoint.to_string())),
261 }
262}
263
264pub(super) async fn send_request<B, W, T>(
265 client: &Client<B, W>,
266 method: &str,
267 params: T,
268) -> Result<message::Response>
269where
270 B: JsonRpcCodec,
271 W: WsCodec,
272 T: Serialize + DeserializeOwned,
273{
274 let ClientBackend::Multiplexed {
275 message_dispatcher,
276 send_chan,
277 ..
278 } = &client.backend
279 else {
280 return Err(Error::InvalidState("not in multiplexed mode".into()));
281 };
282
283 let id: RequestID = random_32();
284 let request = message::Request {
285 jsonrpc: message::JSONRPC_VERSION.to_string(),
286 id: json!(id),
287 method: method.to_string(),
288 params: Some(json!(params)),
289 };
290
291 if client.disconnect.load(Ordering::Relaxed) {
292 return Err(Error::ClientDisconnected);
293 }
294 let req = serde_json::to_value(request)?;
295
296 let rx = message_dispatcher.register(id).await;
297 if let Err(err) = send_chan.0.send(req).await {
298 message_dispatcher.unregister(&id).await;
299 return Err(err.into());
300 }
301
302 let result = match client.config.timeout {
303 Some(t) => match timeout(Duration::from_millis(t), rx.recv()).await {
304 Ok(res) => res.map_err(Error::from),
305 Err(err) => Err(Error::from(err)),
306 },
307 None => rx.recv().await.map_err(Error::from),
308 };
309
310 let response = match result {
313 Ok(r) => r,
314 Err(err) => {
315 message_dispatcher.unregister(&id).await;
316 return Err(err);
317 }
318 };
319
320 let resp_id = response
324 .id
325 .as_ref()
326 .ok_or_else(|| Error::InvalidMsg("Missing response id".to_string()))?;
327 if *resp_id != id {
328 return Err(Error::InvalidMsg("Invalid response id".to_string()));
329 }
330
331 Ok(response)
332}