client.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. //! JSON-RPC client-side implementation.
  19. use std::time::Duration;
  20. use async_std::io::timeout;
  21. use futures::{select, AsyncReadExt, AsyncWriteExt, FutureExt};
  22. use log::{debug, error};
  23. use serde_json::{json, Value};
  24. use url::Url;
  25. use super::jsonrpc::{ErrorCode, JsonError, JsonRequest, JsonResult};
  26. use crate::{
  27. net::transport::{
  28. TcpTransport, TorTransport, Transport, TransportName, TransportStream, UnixTransport,
  29. },
  30. system::SubscriberPtr,
  31. Error, Result,
  32. };
  33. /// JSON-RPC client implementation using asynchronous channels.
  34. pub struct RpcClient {
  35. send: smol::channel::Sender<(Value, bool)>,
  36. recv: smol::channel::Receiver<JsonResult>,
  37. stop_signal: smol::channel::Sender<()>,
  38. url: Url,
  39. }
  40. impl RpcClient {
  41. /// Instantiate a new JSON-RPC client that will connect to the given URL.
  42. pub async fn new(url: Url) -> Result<Self> {
  43. let (send, recv, stop_signal) = Self::open_channels(&url).await?;
  44. Ok(Self { send, recv, stop_signal, url })
  45. }
  46. /// Close the channels of an instantiated [`RpcClient`].
  47. pub async fn close(&self) -> Result<()> {
  48. self.stop_signal.send(()).await?;
  49. Ok(())
  50. }
  51. /// Listen instantiated client for notifications.
  52. /// NOTE: Subscriber listeners must perform response handling.
  53. pub async fn subscribe(
  54. &self,
  55. req: JsonRequest,
  56. subscriber: SubscriberPtr<JsonResult>,
  57. ) -> Result<()> {
  58. // Perform initial request.
  59. debug!(target: "rpc::client", "--> {}", serde_json::to_string(&req)?);
  60. // If the connection is closed, the sender will get an error for sending to a closed channel.
  61. if let Err(e) = self.send.send((json!(req), false)).await {
  62. error!(target: "rpc::client", "JSON-RPC client unable to send to {} (channels closed): {}", self.url, e);
  63. return Err(Error::NetworkOperationFailed)
  64. }
  65. loop {
  66. // If the connection is closed, the receiver will get an error for waiting on a closed channel.
  67. let notification = self.recv.recv().await;
  68. if notification.is_err() {
  69. error!(target: "rpc::client", "JSON-RPC client unable to recv from {} (channels closed)", self.url);
  70. break
  71. }
  72. // Notify subscribed channels
  73. let notification = notification?;
  74. debug!(target: "rpc::client", "<-- {}", serde_json::to_string(&notification)?);
  75. subscriber.notify(notification.clone()).await;
  76. // Stop listenning on error
  77. match notification {
  78. JsonResult::Notification(_) => {}
  79. _ => break,
  80. }
  81. // Triggering next consume
  82. if let Err(e) = self.send.send((json!(req), false)).await {
  83. error!(target: "rpc::client", "JSON-RPC client unable to send to {} (channels closed): {}", self.url, e);
  84. break
  85. }
  86. }
  87. subscriber.notify(JsonError::new(ErrorCode::InternalError, None, req.id).into()).await;
  88. Err(Error::NetworkOperationFailed)
  89. }
  90. /// Send a given JSON-RPC request over the instantiated client.
  91. pub async fn request(&self, value: JsonRequest) -> Result<Value> {
  92. let req_id = value.id.clone().as_u64().unwrap();
  93. debug!(target: "rpc::client", "--> {}", serde_json::to_string(&value)?);
  94. // If the connection is closed, the sender will get an error for
  95. // sending to a closed channel.
  96. if let Err(e) = self.send.send((json!(value), true)).await {
  97. error!(target: "rpc::client", "JSON-RPC client unable to send to {} (channels closed): {}", self.url, e);
  98. return Err(Error::NetworkOperationFailed)
  99. }
  100. // If the connection is closed, the receiver will get an error for
  101. // waiting on a closed channel.
  102. let reply = self.recv.recv().await;
  103. if reply.is_err() {
  104. error!(target: "rpc::client", "JSON-RPC client unable to recv from {} (channels closed)", self.url);
  105. return Err(Error::NetworkOperationFailed)
  106. }
  107. match reply? {
  108. JsonResult::Response(r) => {
  109. // Check if the IDs match
  110. let resp_id = r.id.as_u64();
  111. if resp_id.is_none() {
  112. let e = JsonError::new(ErrorCode::InvalidId, None, r.id);
  113. return Err(Error::JsonRpcError(e.error.message.to_string()))
  114. }
  115. if resp_id.unwrap() != req_id {
  116. let e = JsonError::new(ErrorCode::InvalidId, None, r.id);
  117. return Err(Error::JsonRpcError(e.error.message.to_string()))
  118. }
  119. debug!(target: "rpc::client", "<-- {}", serde_json::to_string(&r)?);
  120. Ok(r.result)
  121. }
  122. JsonResult::Error(e) => {
  123. debug!(target: "rpc::client", "<-- {}", serde_json::to_string(&e)?);
  124. Err(Error::JsonRpcError(e.error.message.to_string()))
  125. }
  126. JsonResult::Notification(n) => {
  127. debug!(target: "rpc::client", "<-- {}", serde_json::to_string(&n)?);
  128. Err(Error::JsonRpcError("Unexpected reply".to_string()))
  129. }
  130. JsonResult::Subscriber(_) => Err(Error::JsonRpcError("Unexpected reply".to_string())),
  131. }
  132. }
  133. /// Oneshot send a given JSON-RPC request over the instantiated client
  134. /// and close the channels on reply.
  135. pub async fn oneshot_request(&self, value: JsonRequest) -> Result<Value> {
  136. let rep = self.request(value).await?;
  137. self.stop_signal.send(()).await?;
  138. Ok(rep)
  139. }
  140. /// Instantiate channels for a new [`RpcClient`].
  141. async fn open_channels(
  142. uri: &Url,
  143. ) -> Result<(
  144. smol::channel::Sender<(Value, bool)>,
  145. smol::channel::Receiver<JsonResult>,
  146. smol::channel::Sender<()>,
  147. )> {
  148. let (data_send, data_recv) = smol::channel::unbounded();
  149. let (result_send, result_recv) = smol::channel::unbounded();
  150. let (stop_send, stop_recv) = smol::channel::unbounded();
  151. let transport_name = TransportName::try_from(uri.clone())?;
  152. macro_rules! reqrep {
  153. ($stream:expr, $transport:expr, $upgrade:expr) => {{
  154. if let Err(err) = $stream {
  155. error!(target: "rpc::client", "JSON-RPC client setup for {} failed: {}", uri, err);
  156. return Err(Error::ConnectFailed)
  157. }
  158. let stream = $stream?.await;
  159. if let Err(err) = stream {
  160. error!(target: "rpc::client", "JSON-RPC client connection to {} failed: {}", uri, err);
  161. return Err(Error::ConnectFailed)
  162. }
  163. let stream = stream?;
  164. match $upgrade {
  165. None => {
  166. smol::spawn(Self::reqrep_loop(stream, result_send, data_recv, stop_recv))
  167. .detach();
  168. }
  169. Some(u) if u == "tls" => {
  170. let stream = $transport.upgrade_dialer(stream)?.await?;
  171. smol::spawn(Self::reqrep_loop(stream, result_send, data_recv, stop_recv))
  172. .detach();
  173. }
  174. Some(u) => return Err(Error::UnsupportedTransportUpgrade(u)),
  175. }
  176. }};
  177. }
  178. match transport_name {
  179. TransportName::Tcp(upgrade) => {
  180. let transport = TcpTransport::new(None, 1024);
  181. let stream = transport.dial(uri.clone(), None);
  182. reqrep!(stream, transport, upgrade);
  183. }
  184. TransportName::Tor(upgrade) => {
  185. let socks5_url = TorTransport::get_dialer_env()?;
  186. let transport = TorTransport::new(socks5_url, None)?;
  187. let stream = transport.clone().dial(uri.clone(), None);
  188. reqrep!(stream, transport, upgrade);
  189. }
  190. TransportName::Unix => {
  191. let transport = UnixTransport::new();
  192. let stream = transport.dial(uri.clone(), None);
  193. reqrep!(stream, transport, None);
  194. }
  195. _ => unimplemented!(),
  196. }
  197. Ok((data_send, result_recv, stop_send))
  198. }
  199. /// Internal function that loops on a given stream and multiplexes the data.
  200. async fn reqrep_loop<T: TransportStream>(
  201. mut stream: T,
  202. result_send: smol::channel::Sender<JsonResult>,
  203. data_recv: smol::channel::Receiver<(Value, bool)>,
  204. stop_recv: smol::channel::Receiver<()>,
  205. ) -> Result<()> {
  206. // If timeout is enabled and we don't get a reply within 30 seconds, we'll fail.
  207. let read_timeout = Duration::from_secs(30);
  208. loop {
  209. // FIXME: Nasty size. 8M
  210. let mut buf = vec![0; 1024 * 8192];
  211. select! {
  212. tuple = data_recv.recv().fuse() => {
  213. let (data, with_timeout) = tuple?;
  214. let data_bytes = serde_json::to_vec(&data)?;
  215. stream.write_all(&data_bytes).await?;
  216. // Since we are using async read and write,
  217. // the other side might not have finished writing
  218. // to the stream. To mitigate this, we perform a read
  219. // and check if data can be converted to a JsonResult.
  220. // If data is incomplete, this will fail, therefore,
  221. // we re-execute read and write after previous read in the buffer,
  222. // and repeat until the data in buffer can be converted.
  223. let mut n = 0;
  224. loop {
  225. n += if with_timeout {
  226. timeout(read_timeout, async { stream.read(&mut buf[n..]).await }).await?
  227. } else {
  228. stream.read(&mut buf[n..]).await?
  229. };
  230. match serde_json::from_slice(&buf[0..n]) {
  231. Ok(reply) => {
  232. result_send.send(reply).await?;
  233. break
  234. },
  235. Err(e) => debug!(target: "rpc::client", "JSON-RPC client retrying failed convertion with error: {}", e),
  236. }
  237. }
  238. }
  239. _ = stop_recv.recv().fuse() => break
  240. }
  241. }
  242. Ok(())
  243. }
  244. }