client.rs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. use std::sync::Arc;
  19. use log::{debug, error};
  20. use smol::{channel, io::BufReader, Executor};
  21. use tinyjson::JsonValue;
  22. use url::Url;
  23. use super::{
  24. common::{read_from_stream, write_to_stream, INIT_BUF_SIZE, READ_TIMEOUT},
  25. jsonrpc::*,
  26. };
  27. use crate::{
  28. net::transport::{Dialer, PtStream},
  29. system::{io_timeout, StoppableTask, StoppableTaskPtr, SubscriberPtr},
  30. Error, Result,
  31. };
  32. /// JSON-RPC client implementation using asynchronous channels.
  33. pub struct RpcClient {
  34. /// The channel used to send JSON-RPC request objects.
  35. /// The `bool` marks if we should have a reply read timeout.
  36. req_send: channel::Sender<(JsonRequest, bool)>,
  37. /// The channel used to read the JSON-RPC response object.
  38. rep_recv: channel::Receiver<JsonResult>,
  39. /// The stoppable task pointer, used on [`RpcClient::stop()`]
  40. task: StoppableTaskPtr,
  41. }
  42. impl RpcClient {
  43. /// Instantiate a new JSON-RPC client that connects to the given endpoint.
  44. /// The function takes an `Executor` object, which is needed to start the
  45. /// `StoppableTask` which represents the client-server connection.
  46. pub async fn new(endpoint: Url, ex: Arc<Executor<'_>>) -> Result<Self> {
  47. // Instantiate communication channels
  48. let (req_send, req_recv) = channel::unbounded();
  49. let (rep_send, rep_recv) = channel::unbounded();
  50. // Instantiate Dialer and dial the server
  51. // TODO: Could add a timeout here
  52. let dialer = Dialer::new(endpoint).await?;
  53. let stream = dialer.dial(None).await?;
  54. // Create the StoppableTask running the request-reply loop.
  55. // This represents the actual connection, which can be stopped
  56. // using `RpcClient::stop()`.
  57. let task = StoppableTask::new();
  58. task.clone().start(
  59. Self::reqrep_loop(stream, rep_send, req_recv),
  60. |res| async move {
  61. match res {
  62. Ok(()) | Err(Error::RpcClientStopped) => {}
  63. Err(e) => error!(target: "rpc::client", "[RPC] Client error: {}", e),
  64. }
  65. },
  66. Error::RpcClientStopped,
  67. ex.clone(),
  68. );
  69. Ok(Self { req_send, rep_recv, task })
  70. }
  71. /// Stop the JSON-RPC client. This will trigger `stop()` on the inner
  72. /// `StoppableTaskPtr` resulting in stopping the internal reqrep loop
  73. /// and therefore closing the connection.
  74. pub async fn stop(&self) {
  75. self.task.stop().await;
  76. }
  77. /// Internal function that loops on a given stream and multiplexes the data
  78. async fn reqrep_loop(
  79. stream: Box<dyn PtStream>,
  80. rep_send: channel::Sender<JsonResult>,
  81. req_recv: channel::Receiver<(JsonRequest, bool)>,
  82. ) -> Result<()> {
  83. debug!(target: "rpc::client::reqrep_loop()", "Starting reqrep loop");
  84. let (reader, mut writer) = smol::io::split(stream);
  85. let mut reader = BufReader::new(reader);
  86. loop {
  87. let mut buf = Vec::with_capacity(INIT_BUF_SIZE);
  88. let (request, with_timeout) = req_recv.recv().await?;
  89. let request = JsonResult::Request(request);
  90. write_to_stream(&mut writer, &request).await?;
  91. if with_timeout {
  92. let _ = io_timeout(READ_TIMEOUT, read_from_stream(&mut reader, &mut buf)).await?;
  93. } else {
  94. let _ = read_from_stream(&mut reader, &mut buf).await?;
  95. }
  96. let val: JsonValue = String::from_utf8(buf)?.parse()?;
  97. let rep = JsonResult::try_from_value(&val)?;
  98. rep_send.send(rep).await?;
  99. }
  100. }
  101. /// Send a given JSON-RPC request over the instantiated client and
  102. /// return a possible result. If the response is an error, returns
  103. /// a `JsonRpcError`.
  104. pub async fn request(&self, req: JsonRequest) -> Result<JsonValue> {
  105. let req_id = req.id;
  106. debug!(target: "rpc::client", "--> {}", req.stringify()?);
  107. // If the connection is closed, the sender will get an error
  108. // for sending to a closed channel.
  109. self.req_send.send((req, true)).await?;
  110. // If the connection is closed, the receiver will get an error
  111. // for waiting on a closed channel.
  112. let reply = self.rep_recv.recv().await?;
  113. // Handle the response
  114. match reply {
  115. JsonResult::Response(rep) | JsonResult::SubscriberWithReply(_, rep) => {
  116. debug!(target: "rpc::client", "<-- {}", rep.stringify()?);
  117. // Check if the IDs match
  118. if req_id != rep.id {
  119. let e = JsonError::new(ErrorCode::IdMismatch, None, rep.id);
  120. return Err(Error::JsonRpcError((e.error.code, e.error.message)))
  121. }
  122. Ok(rep.result)
  123. }
  124. JsonResult::Error(e) => {
  125. debug!(target: "rpc::client", "<-- {}", e.stringify()?);
  126. Err(Error::JsonRpcError((e.error.code, e.error.message)))
  127. }
  128. JsonResult::Notification(n) => {
  129. debug!(target: "rpc::client", "<-- {}", n.stringify()?);
  130. let e = JsonError::new(ErrorCode::InvalidReply, None, req_id);
  131. Err(Error::JsonRpcError((e.error.code, e.error.message)))
  132. }
  133. JsonResult::Request(r) => {
  134. debug!(target: "rpc::client", "<-- {}", r.stringify()?);
  135. let e = JsonError::new(ErrorCode::InvalidReply, None, req_id);
  136. Err(Error::JsonRpcError((e.error.code, e.error.message)))
  137. }
  138. JsonResult::Subscriber(_) => {
  139. // When?
  140. let e = JsonError::new(ErrorCode::InvalidReply, None, req_id);
  141. Err(Error::JsonRpcError((e.error.code, e.error.message)))
  142. }
  143. }
  144. }
  145. /// Oneshot send a given JSON-RPC request over the instantiated client
  146. /// and immediately close the channels upon receiving a reply.
  147. pub async fn oneshot_request(&self, req: JsonRequest) -> Result<JsonValue> {
  148. let rep = match self.request(req).await {
  149. Ok(v) => v,
  150. Err(e) => {
  151. self.stop().await;
  152. return Err(e)
  153. }
  154. };
  155. self.stop().await;
  156. Ok(rep)
  157. }
  158. /// Listen instantiated client for notifications.
  159. /// NOTE: Subscriber listeners must perform response handling.
  160. pub async fn subscribe(&self, req: JsonRequest, sub: SubscriberPtr<JsonResult>) -> Result<()> {
  161. // Perform initial request
  162. debug!(target: "rpc::client", "--> {}", req.stringify()?);
  163. let req_id = req.id;
  164. // If the connection is closed, the sender will get an error for
  165. // sending to a closed channel.
  166. self.req_send.send((req, false)).await?;
  167. // Now loop and listen to notifications
  168. loop {
  169. // If the connection is closed, the receiver will get an error
  170. // for waiting on a closed channel.
  171. let notification = self.rep_recv.recv().await?;
  172. // Handle the response
  173. match notification {
  174. JsonResult::Notification(ref n) => {
  175. debug!(target: "rpc::client", "<-- {}", n.stringify()?);
  176. sub.notify(notification.clone()).await;
  177. continue
  178. }
  179. JsonResult::Error(e) => {
  180. debug!(target: "rpc::client", "<-- {}", e.stringify()?);
  181. return Err(Error::JsonRpcError((e.error.code, e.error.message)))
  182. }
  183. JsonResult::Response(r) | JsonResult::SubscriberWithReply(_, r) => {
  184. debug!(target: "rpc::client", "<-- {}", r.stringify()?);
  185. let e = JsonError::new(ErrorCode::InvalidReply, None, req_id);
  186. return Err(Error::JsonRpcError((e.error.code, e.error.message)))
  187. }
  188. JsonResult::Request(r) => {
  189. debug!(target: "rpc::client", "<-- {}", r.stringify()?);
  190. let e = JsonError::new(ErrorCode::InvalidReply, None, req_id);
  191. return Err(Error::JsonRpcError((e.error.code, e.error.message)))
  192. }
  193. JsonResult::Subscriber(_) => {
  194. // When?
  195. let e = JsonError::new(ErrorCode::InvalidReply, None, req_id);
  196. return Err(Error::JsonRpcError((e.error.code, e.error.message)))
  197. }
  198. }
  199. }
  200. }
  201. }