server.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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::{collections::HashSet, io::ErrorKind, sync::Arc};
  19. use async_trait::async_trait;
  20. use log::{debug, error, info};
  21. use smol::{
  22. io::{BufReader, ReadHalf, WriteHalf},
  23. lock::{Mutex, MutexGuard},
  24. };
  25. use tinyjson::JsonValue;
  26. use url::Url;
  27. use super::{
  28. common::{read_from_stream, write_to_stream, INIT_BUF_SIZE},
  29. jsonrpc::*,
  30. };
  31. use crate::{
  32. net::transport::{Listener, PtListener, PtStream},
  33. system::{StoppableTask, StoppableTaskPtr},
  34. Error, Result,
  35. };
  36. /// Asynchronous trait implementing a handler for incoming JSON-RPC requests.
  37. #[async_trait]
  38. pub trait RequestHandler: Sync + Send {
  39. async fn handle_request(&self, req: JsonRequest) -> JsonResult;
  40. async fn pong(&self, id: u16, _params: JsonValue) -> JsonResult {
  41. JsonResponse::new(JsonValue::String("pong".to_string()), id).into()
  42. }
  43. async fn connections_mut(&self) -> MutexGuard<'life0, HashSet<StoppableTaskPtr>>;
  44. async fn connections(&self) -> Vec<StoppableTaskPtr> {
  45. self.connections_mut().await.iter().cloned().collect()
  46. }
  47. async fn mark_connection(&self, task: StoppableTaskPtr) {
  48. self.connections_mut().await.insert(task);
  49. }
  50. async fn unmark_connection(&self, task: StoppableTaskPtr) {
  51. self.connections_mut().await.remove(&task);
  52. }
  53. async fn active_connections(&self) -> usize {
  54. self.connections_mut().await.len()
  55. }
  56. async fn stop_connections(&self) {
  57. info!(target: "rpc::server", "[RPC] Server stopped, closing connections");
  58. for (i, task) in self.connections().await.iter().enumerate() {
  59. debug!(target: "rpc::server", "Stopping connection #{}", i);
  60. task.stop().await;
  61. }
  62. }
  63. }
  64. /// Auxiliary function to handle a request in the background.
  65. async fn handle_request(
  66. writer: Arc<Mutex<WriteHalf<Box<dyn PtStream>>>>,
  67. addr: Url,
  68. rh: Arc<impl RequestHandler + 'static>,
  69. ex: Arc<smol::Executor<'_>>,
  70. tasks: Arc<Mutex<HashSet<Arc<StoppableTask>>>>,
  71. req: JsonRequest,
  72. ) -> Result<()> {
  73. let rep = rh.handle_request(req).await;
  74. match rep {
  75. JsonResult::Subscriber(subscriber) => {
  76. let task = StoppableTask::new();
  77. // Clone what needs to go in the background
  78. let task_ = task.clone();
  79. let addr_ = addr.clone();
  80. let tasks_ = tasks.clone();
  81. let writer_ = writer.clone();
  82. // Detach the subscriber so we can multiplex further requests
  83. task.clone().start(
  84. async move {
  85. // Subscribe to the inner method subscriber
  86. let subscription = subscriber.publisher.subscribe().await;
  87. loop {
  88. // Listen for notifications
  89. let notification = subscription.receive().await;
  90. // Push notification
  91. debug!(target: "rpc::server", "{} <-- {}", addr_, notification.stringify().unwrap());
  92. let notification = JsonResult::Notification(notification);
  93. let mut writer_lock = writer_.lock().await;
  94. if let Err(e) = write_to_stream(&mut writer_lock, &notification).await {
  95. subscription.unsubscribe().await;
  96. return Err(e.into())
  97. }
  98. drop(writer_lock);
  99. }
  100. },
  101. move |_| async move {
  102. debug!(
  103. target: "rpc::server",
  104. "Removing background task {} from map", task_.task_id,
  105. );
  106. tasks_.lock().await.remove(&task_);
  107. },
  108. Error::DetachedTaskStopped,
  109. ex.clone(),
  110. );
  111. debug!(target: "rpc::server", "Adding background task {} to map", task.task_id);
  112. tasks.lock().await.insert(task);
  113. }
  114. JsonResult::SubscriberWithReply(subscriber, reply) => {
  115. // Write the response
  116. debug!(target: "rpc::server", "{} <-- {}", addr, reply.stringify()?);
  117. let mut writer_lock = writer.lock().await;
  118. write_to_stream(&mut writer_lock, &reply.into()).await?;
  119. drop(writer_lock);
  120. let task = StoppableTask::new();
  121. // Clone what needs to go in the background
  122. let task_ = task.clone();
  123. let addr_ = addr.clone();
  124. let tasks_ = tasks.clone();
  125. let writer_ = writer.clone();
  126. // Detach the subscriber so we can multiplex further requests
  127. task.clone().start(
  128. async move {
  129. // Start the subscriber loop
  130. let subscription = subscriber.publisher.subscribe().await;
  131. loop {
  132. // Listen for notifications
  133. let notification = subscription.receive().await;
  134. // Push notification
  135. debug!(target: "rpc::server", "{} <-- {}", addr_, notification.stringify().unwrap());
  136. let notification = JsonResult::Notification(notification);
  137. let mut writer_lock = writer_.lock().await;
  138. if let Err(e) = write_to_stream(&mut writer_lock, &notification).await {
  139. subscription.unsubscribe().await;
  140. drop(writer_lock);
  141. return Err(e.into())
  142. }
  143. drop(writer_lock);
  144. }
  145. },
  146. move |_| async move {
  147. debug!(
  148. target: "rpc::server",
  149. "Removing background task {} from map", task_.task_id,
  150. );
  151. tasks_.lock().await.remove(&task_);
  152. },
  153. Error::DetachedTaskStopped,
  154. ex.clone(),
  155. );
  156. debug!(target: "rpc::server", "Adding background task {} to map", task.task_id);
  157. tasks.lock().await.insert(task);
  158. }
  159. JsonResult::Request(_) | JsonResult::Notification(_) => {
  160. unreachable!("Should never happen")
  161. }
  162. JsonResult::Response(ref v) => {
  163. debug!(target: "rpc::server", "{} <-- {}", addr, v.stringify()?);
  164. let mut writer_lock = writer.lock().await;
  165. write_to_stream(&mut writer_lock, &rep).await?;
  166. drop(writer_lock);
  167. }
  168. JsonResult::Error(ref v) => {
  169. debug!(target: "rpc::server", "{} <-- {}", addr, v.stringify()?);
  170. let mut writer_lock = writer.lock().await;
  171. write_to_stream(&mut writer_lock, &rep).await?;
  172. drop(writer_lock);
  173. }
  174. }
  175. Ok(())
  176. }
  177. /// Accept function that should run inside a loop for accepting incoming
  178. /// JSON-RPC requests and passing them to the [`RequestHandler`].
  179. #[allow(clippy::type_complexity)]
  180. pub async fn accept(
  181. reader: Arc<Mutex<BufReader<ReadHalf<Box<dyn PtStream>>>>>,
  182. writer: Arc<Mutex<WriteHalf<Box<dyn PtStream>>>>,
  183. addr: Url,
  184. rh: Arc<impl RequestHandler + 'static>,
  185. conn_limit: Option<usize>,
  186. ex: Arc<smol::Executor<'_>>,
  187. ) -> Result<()> {
  188. // If there's a connection limit set, we will refuse connections
  189. // after this point.
  190. if let Some(conn_limit) = conn_limit {
  191. if rh.clone().active_connections().await >= conn_limit {
  192. debug!(
  193. target: "rpc::server::accept()",
  194. "Connection limit reached, refusing new conn"
  195. );
  196. return Err(Error::RpcConnectionsExhausted)
  197. }
  198. }
  199. // We'll hold our background tasks here
  200. let tasks = Arc::new(Mutex::new(HashSet::new()));
  201. loop {
  202. let mut buf = Vec::with_capacity(INIT_BUF_SIZE);
  203. let mut reader_lock = reader.lock().await;
  204. let _ = read_from_stream(&mut reader_lock, &mut buf).await?;
  205. drop(reader_lock);
  206. let line = match String::from_utf8(buf) {
  207. Ok(v) => v,
  208. Err(e) => {
  209. error!(
  210. target: "rpc::server::accept()",
  211. "[RPC SERVER] Failed parsing string from read buffer: {}", e,
  212. );
  213. return Err(e.into())
  214. }
  215. };
  216. // Parse the line as JSON
  217. let val: JsonValue = match line.trim().parse() {
  218. Ok(v) => v,
  219. Err(e) => {
  220. error!(
  221. target: "rpc::server::accept()",
  222. "[RPC SERVER] Failed parsing JSON string: {}", e,
  223. );
  224. return Err(e.into())
  225. }
  226. };
  227. // Cast to JsonRequest
  228. let req = match JsonRequest::try_from(&val) {
  229. Ok(v) => v,
  230. Err(e) => {
  231. error!(
  232. target: "rpc::server::accept()",
  233. "[RPC SERVER] Failed casting JSON to a JsonRequest: {}", e,
  234. );
  235. return Err(e.into())
  236. }
  237. };
  238. debug!(target: "rpc::server", "{} --> {}", addr, val.stringify()?);
  239. // Create a new task to handle request in the background
  240. let task = StoppableTask::new();
  241. // Clone what needs to go in the background
  242. let task_ = task.clone();
  243. let tasks_ = tasks.clone();
  244. // Detach the task
  245. task.clone().start(
  246. handle_request(
  247. writer.clone(),
  248. addr.clone(),
  249. rh.clone(),
  250. ex.clone(),
  251. tasks.clone(),
  252. req,
  253. ),
  254. move |_| async move {
  255. debug!(
  256. target: "rpc::server",
  257. "Removing background task {} from map", task_.task_id,
  258. );
  259. tasks_.lock().await.remove(&task_);
  260. },
  261. Error::DetachedTaskStopped,
  262. ex.clone(),
  263. );
  264. debug!(target: "rpc::server", "Adding background task {} to map", task.task_id);
  265. tasks.lock().await.insert(task);
  266. }
  267. }
  268. /// Wrapper function around [`accept()`] to take the incoming connection and
  269. /// pass it forward.
  270. async fn run_accept_loop(
  271. listener: Box<dyn PtListener>,
  272. rh: Arc<impl RequestHandler + 'static>,
  273. conn_limit: Option<usize>,
  274. ex: Arc<smol::Executor<'_>>,
  275. ) -> Result<()> {
  276. loop {
  277. match listener.next().await {
  278. Ok((stream, url)) => {
  279. let rh_ = rh.clone();
  280. info!(target: "rpc::server", "[RPC] Server accepted conn from {}", url);
  281. let (reader, writer) = smol::io::split(stream);
  282. let reader = Arc::new(Mutex::new(BufReader::new(reader)));
  283. let writer = Arc::new(Mutex::new(writer));
  284. let task = StoppableTask::new();
  285. let task_ = task.clone();
  286. let ex_ = ex.clone();
  287. task.clone().start(
  288. accept(reader, writer, url.clone(), rh.clone(), conn_limit, ex_),
  289. |_| async move {
  290. info!(target: "rpc::server", "[RPC] Closed conn from {}", url);
  291. rh_.clone().unmark_connection(task_.clone()).await;
  292. },
  293. Error::ChannelStopped,
  294. ex.clone(),
  295. );
  296. rh.clone().mark_connection(task.clone()).await;
  297. }
  298. // As per accept(2) recommendation:
  299. Err(e) if e.raw_os_error().is_some() => match e.raw_os_error().unwrap() {
  300. libc::EAGAIN | libc::ECONNABORTED | libc::EPROTO | libc::EINTR => continue,
  301. _ => {
  302. error!(
  303. target: "rpc::server::run_accept_loop()",
  304. "[RPC] Server failed listening: {}", e,
  305. );
  306. error!(
  307. target: "rpc::server::run_accept_loop()",
  308. "[RPC] Closing accept loop"
  309. );
  310. return Err(e.into())
  311. }
  312. },
  313. // In case a TLS handshake fails, we'll get this:
  314. Err(e) if e.kind() == ErrorKind::UnexpectedEof => continue,
  315. // Errors we didn't handle above:
  316. Err(e) => {
  317. error!(
  318. target: "rpc::server::run_accept_loop()",
  319. "[RPC] Unhandled listener.next() error: {}", e,
  320. );
  321. error!(
  322. target: "rpc::server::run_accept_loop()",
  323. "[RPC] Closing acceptloop"
  324. );
  325. return Err(e.into())
  326. }
  327. }
  328. }
  329. }
  330. /// Start a JSON-RPC server bound to the given accept URL and use the
  331. /// given [`RequestHandler`] to handle incoming requests.
  332. pub async fn listen_and_serve(
  333. accept_url: Url,
  334. rh: Arc<impl RequestHandler + 'static>,
  335. conn_limit: Option<usize>,
  336. ex: Arc<smol::Executor<'_>>,
  337. ) -> Result<()> {
  338. let listener = Listener::new(accept_url, None).await?.listen().await?;
  339. run_accept_loop(listener, rh, conn_limit, ex.clone()).await
  340. }
  341. #[cfg(test)]
  342. mod tests {
  343. use super::*;
  344. use crate::{rpc::client::RpcClient, system::msleep};
  345. use smol::{net::TcpListener, Executor};
  346. struct RpcServer {
  347. rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
  348. }
  349. #[async_trait]
  350. impl RequestHandler for RpcServer {
  351. async fn handle_request(&self, req: JsonRequest) -> JsonResult {
  352. match req.method.as_str() {
  353. "ping" => return self.pong(req.id, req.params).await,
  354. _ => panic!(),
  355. }
  356. }
  357. async fn connections_mut(&self) -> MutexGuard<'life0, HashSet<StoppableTaskPtr>> {
  358. self.rpc_connections.lock().await
  359. }
  360. }
  361. #[test]
  362. fn conn_manager() -> Result<()> {
  363. let executor = Arc::new(Executor::new());
  364. // This simulates a server and a client. Through the function, there
  365. // are some calls to sleep(), which are used for the tests, because
  366. // otherwise they execute too fast. In practice, The RPC server is
  367. // a long-running task so when polled, it should handle things in a
  368. // correct manner.
  369. smol::block_on(executor.run(async {
  370. // Find an available port
  371. let listener = TcpListener::bind("127.0.0.1:0").await?;
  372. let sockaddr = listener.local_addr()?;
  373. let endpoint = Url::parse(&format!("tcp://127.0.0.1:{}", sockaddr.port()))?;
  374. drop(listener);
  375. let rpc_server = Arc::new(RpcServer { rpc_connections: Mutex::new(HashSet::new()) });
  376. let rpc_server_ = rpc_server.clone();
  377. let server_task = StoppableTask::new();
  378. server_task.clone().start(
  379. listen_and_serve(endpoint.clone(), rpc_server.clone(), None, executor.clone()),
  380. |res| async move {
  381. match res {
  382. Ok(()) | Err(Error::RpcServerStopped) => {
  383. rpc_server_.stop_connections().await
  384. }
  385. Err(e) => panic!("{}", e),
  386. }
  387. },
  388. Error::RpcServerStopped,
  389. executor.clone(),
  390. );
  391. // Let the server spawn
  392. msleep(500).await;
  393. // Connect a client
  394. let rpc_client0 = RpcClient::new(endpoint.clone(), executor.clone()).await?;
  395. msleep(500).await;
  396. assert!(rpc_server.active_connections().await == 1);
  397. // Connect another client
  398. let rpc_client1 = RpcClient::new(endpoint.clone(), executor.clone()).await?;
  399. msleep(500).await;
  400. assert!(rpc_server.active_connections().await == 2);
  401. // And another one
  402. let _rpc_client2 = RpcClient::new(endpoint.clone(), executor.clone()).await?;
  403. msleep(500).await;
  404. assert!(rpc_server.active_connections().await == 3);
  405. // Close the first client
  406. rpc_client0.stop().await;
  407. msleep(500).await;
  408. assert!(rpc_server.active_connections().await == 2);
  409. // Close the second client
  410. rpc_client1.stop().await;
  411. msleep(500).await;
  412. assert!(rpc_server.active_connections().await == 1);
  413. // The Listener should be stopped when we stop the server task.
  414. server_task.stop().await;
  415. assert!(RpcClient::new(endpoint, executor.clone()).await.is_err());
  416. // After the server is stopped, the connections tasks should also be stopped
  417. assert!(rpc_server.active_connections().await == 0);
  418. Ok(())
  419. }))
  420. }
  421. }