manual_session.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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. //! Manual connections session. Manages the creation of manual sessions.
  19. //! Used to create a manual session and to stop and start the session.
  20. //!
  21. //! A manual session is a type of outbound session in which we attempt
  22. //! connection to a predefined set of peers. Manual sessions loop forever
  23. //! continually trying to connect to a given peer, and sleep
  24. //! `outbound_connect_timeout` times between each attempt.
  25. //!
  26. //! Class consists of a weak pointer to the p2p interface and a vector of
  27. //! outbound connection slots. Using a weak pointer to p2p allows us to
  28. //! avoid circular dependencies. The vector of slots is wrapped in a mutex
  29. //! lock. This is switched on every time we instantiate a connection slot
  30. //! and insures that no other part of the program uses the slots at the
  31. //! same time.
  32. use std::sync::{Arc, Weak};
  33. use async_trait::async_trait;
  34. use futures::stream::{FuturesUnordered, StreamExt};
  35. use log::{debug, error, info, warn};
  36. use smol::lock::Mutex;
  37. use url::Url;
  38. use super::{
  39. super::{
  40. connector::Connector,
  41. p2p::{P2p, P2pPtr},
  42. },
  43. Session, SessionBitFlag, SESSION_MANUAL,
  44. };
  45. use crate::{
  46. net::{hosts::HostState, settings::SettingsPtr},
  47. system::{sleep, LazyWeak, StoppableTask, StoppableTaskPtr},
  48. Error, Result,
  49. };
  50. pub type ManualSessionPtr = Arc<ManualSession>;
  51. /// Defines manual connections session.
  52. pub struct ManualSession {
  53. pub(in crate::net) p2p: LazyWeak<P2p>,
  54. slots: Mutex<Vec<Arc<Slot>>>,
  55. }
  56. impl ManualSession {
  57. /// Create a new manual session.
  58. pub fn new() -> ManualSessionPtr {
  59. Arc::new(Self { p2p: LazyWeak::new(), slots: Mutex::new(Vec::new()) })
  60. }
  61. pub(crate) async fn start(self: Arc<Self>) {
  62. // Activate mutex lock on connection slots.
  63. let mut slots = self.slots.lock().await;
  64. let mut futures = FuturesUnordered::new();
  65. let self_ = Arc::downgrade(&self);
  66. // Initialize a slot for each configured peer.
  67. // Connections will be started by not yet activated.
  68. for peer in &self.p2p().settings().peers {
  69. let slot = Slot::new(self_.clone(), peer.clone(), self.p2p().settings());
  70. futures.push(slot.clone().start());
  71. slots.push(slot);
  72. }
  73. while (futures.next().await).is_some() {}
  74. }
  75. /// Stops the manual session.
  76. pub async fn stop(&self) {
  77. let slots = &*self.slots.lock().await;
  78. let mut futures = FuturesUnordered::new();
  79. for slot in slots {
  80. futures.push(slot.stop());
  81. }
  82. while (futures.next().await).is_some() {}
  83. }
  84. }
  85. #[async_trait]
  86. impl Session for ManualSession {
  87. fn p2p(&self) -> P2pPtr {
  88. self.p2p.upgrade()
  89. }
  90. fn type_id(&self) -> SessionBitFlag {
  91. SESSION_MANUAL
  92. }
  93. }
  94. struct Slot {
  95. addr: Url,
  96. process: StoppableTaskPtr,
  97. session: Weak<ManualSession>,
  98. connector: Connector,
  99. }
  100. impl Slot {
  101. fn new(session: Weak<ManualSession>, addr: Url, settings: SettingsPtr) -> Arc<Self> {
  102. Arc::new(Self {
  103. addr,
  104. process: StoppableTask::new(),
  105. session: session.clone(),
  106. connector: Connector::new(settings, session),
  107. })
  108. }
  109. async fn start(self: Arc<Self>) {
  110. let ex = self.p2p().executor();
  111. self.process.clone().start(
  112. self.run(),
  113. |res| async {
  114. match res {
  115. Ok(()) | Err(Error::NetworkServiceStopped) => {}
  116. Err(e) => error!("net::manual_session {}", e),
  117. }
  118. },
  119. Error::NetworkServiceStopped,
  120. ex,
  121. );
  122. }
  123. /// Attempts a connection on the associated Connector object.
  124. async fn run(self: Arc<Self>) -> Result<()> {
  125. let ex = self.p2p().executor();
  126. let mut attempts = 0;
  127. loop {
  128. attempts += 1;
  129. info!(
  130. target: "net::manual_session",
  131. "[P2P] Connecting to manual outbound [{}] (attempt #{})",
  132. self.addr, attempts
  133. );
  134. // Do not establish a connection to a host that is also configured as a seed.
  135. // This indicates a user misconfiguration.
  136. if self.p2p().settings().seeds.contains(&self.addr) {
  137. error!(target: "net::manual_session",
  138. "[P2P] Suspending manual connection to seed [{}]", self.addr.clone());
  139. return Ok(())
  140. }
  141. match self.p2p().hosts().try_register(self.addr.clone(), HostState::Connect) {
  142. Ok(_) => {
  143. match self.connector.connect(&self.addr).await {
  144. Ok((url, channel)) => {
  145. info!(
  146. target: "net::manual_session",
  147. "[P2P] Manual outbound connected [{}]", url,
  148. );
  149. let stop_sub = channel.subscribe_stop().await?;
  150. // Channel is now connected but not yet setup
  151. // Register the new channel
  152. self.session().register_channel(channel.clone(), ex.clone()).await?;
  153. // Wait for channel to close
  154. stop_sub.receive().await;
  155. info!(
  156. target: "net::manual_session",
  157. "[P2P] Manual outbound disconnected [{}]", url,
  158. );
  159. }
  160. Err(e) => {
  161. warn!(
  162. target: "net::manual_session",
  163. "[P2P] Unable to connect to manual outbound [{}]: {}",
  164. self.addr, e,
  165. );
  166. // Stop tracking this peer, to avoid it getting stuck in the Connect
  167. // state. This is safe since we have failed to connect at this point.
  168. self.p2p().hosts().unregister(&self.addr);
  169. }
  170. }
  171. }
  172. // This address is currently unavailable.
  173. Err(e) => {
  174. debug!(target: "net::manual_session", "[P2P] Unable to connect to manual
  175. outbound [{}]: {}", self.addr.clone(), e);
  176. }
  177. }
  178. info!(
  179. target: "net::manual_session",
  180. "[P2P] Waiting {} seconds until next manual outbound connection attempt [{}]",
  181. self.p2p().settings().outbound_connect_timeout, self.addr,
  182. );
  183. sleep(self.p2p().settings().outbound_connect_timeout).await;
  184. }
  185. }
  186. fn session(&self) -> ManualSessionPtr {
  187. self.session.upgrade().unwrap()
  188. }
  189. fn p2p(&self) -> P2pPtr {
  190. self.session().p2p()
  191. }
  192. async fn stop(&self) {
  193. self.connector.stop();
  194. self.process.stop().await;
  195. }
  196. }