| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- use async_std::sync::{Arc, Mutex, Weak};
- use std::fmt;
- use async_executor::Executor;
- use async_trait::async_trait;
- use log::{debug, info};
- use rand::seq::SliceRandom;
- use serde_json::json;
- use url::Url;
- use crate::{
- system::{StoppableTask, StoppableTaskPtr, Subscriber, SubscriberPtr, Subscription},
- util::async_util,
- Error, Result,
- };
- use super::{
- super::{ChannelPtr, Connector, P2p},
- Session, SessionBitflag, SESSION_OUTBOUND,
- };
- #[derive(Clone)]
- enum OutboundState {
- Open,
- Pending,
- Connected,
- }
- impl fmt::Display for OutboundState {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "{}",
- match self {
- Self::Open => "open",
- Self::Pending => "pending",
- Self::Connected => "connected",
- }
- )
- }
- }
- #[derive(Clone)]
- struct OutboundInfo {
- addr: Option<Url>,
- channel: Option<ChannelPtr>,
- state: OutboundState,
- }
- impl OutboundInfo {
- async fn get_info(&self) -> serde_json::Value {
- let addr = match self.addr.as_ref() {
- Some(addr) => serde_json::Value::String(addr.to_string()),
- None => serde_json::Value::Null,
- };
- let channel = match &self.channel {
- Some(channel) => channel.get_info().await,
- None => serde_json::Value::Null,
- };
- json!({
- "addr": addr,
- "state": self.state.to_string(),
- "channel": channel,
- })
- }
- }
- impl Default for OutboundInfo {
- fn default() -> Self {
- Self { addr: None, channel: None, state: OutboundState::Open }
- }
- }
- /// Defines outbound connections session.
- pub struct OutboundSession {
- p2p: Weak<P2p>,
- connect_slots: Mutex<Vec<StoppableTaskPtr>>,
- slot_info: Mutex<Vec<OutboundInfo>>,
- /// Subscriber used to signal channels processing
- channel_subscriber: SubscriberPtr<Result<ChannelPtr>>,
- /// Flag to toggle channel_subscriber notifications
- notify: Mutex<bool>,
- }
- impl OutboundSession {
- /// Create a new outbound session.
- pub fn new(p2p: Weak<P2p>) -> Arc<Self> {
- Arc::new(Self {
- p2p,
- connect_slots: Mutex::new(Vec::new()),
- slot_info: Mutex::new(Vec::new()),
- channel_subscriber: Subscriber::new(),
- notify: Mutex::new(false),
- })
- }
- /// Start the outbound session. Runs the channel connect loop.
- pub async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
- let slots_count = self.p2p().settings().outbound_connections;
- info!(target: "net", "Starting {} outbound connection slots.", slots_count);
- // Activate mutex lock on connection slots.
- let mut connect_slots = self.connect_slots.lock().await;
- self.slot_info.lock().await.resize(slots_count as usize, Default::default());
- for i in 0..slots_count {
- let task = StoppableTask::new();
- task.clone().start(
- self.clone().channel_connect_loop(i, executor.clone()),
- // Ignore stop handler
- |_| async {},
- Error::NetworkServiceStopped,
- executor.clone(),
- );
- connect_slots.push(task);
- }
- Ok(())
- }
- /// Stop the outbound session.
- pub async fn stop(&self) {
- let connect_slots = &*self.connect_slots.lock().await;
- for slot in connect_slots {
- slot.stop().await;
- }
- }
- /// Start making outbound connections. Creates a connector object, then
- /// starts a connect loop. Loads a valid address then tries to connect.
- /// Once connected, registers the channel, removes it from the list of
- /// pending channels, and starts sending messages across the channel.
- /// Otherwise returns a network error.
- pub async fn channel_connect_loop(
- self: Arc<Self>,
- slot_number: u32,
- executor: Arc<Executor<'_>>,
- ) -> Result<()> {
- let parent = Arc::downgrade(&self);
- let connector = Connector::new(self.p2p().settings(), Arc::new(parent));
- loop {
- let addr = self.load_address(slot_number).await?;
- info!(target: "net", "#{} connecting to outbound [{}]", slot_number, addr);
- {
- let info = &mut self.slot_info.lock().await[slot_number as usize];
- info.addr = Some(addr.clone());
- info.state = OutboundState::Pending;
- }
- match connector.connect(addr.clone()).await {
- Ok(channel) => {
- // Blacklist goes here
- info!(target: "net", "#{} connected to outbound [{}]", slot_number, addr);
- let stop_sub = channel.subscribe_stop().await;
- if stop_sub.is_err() {
- continue
- }
- self.clone().register_channel(channel.clone(), executor.clone()).await?;
- // Channel is now connected but not yet setup
- // Remove pending lock since register_channel will add the channel to p2p
- self.p2p().remove_pending(&addr).await;
- {
- let info = &mut self.slot_info.lock().await[slot_number as usize];
- info.channel = Some(channel.clone());
- info.state = OutboundState::Connected;
- }
- // Notify that channel processing has been finished
- if *self.notify.lock().await {
- self.channel_subscriber.notify(Ok(channel)).await;
- }
- // Wait for channel to close
- stop_sub.unwrap().receive().await;
- }
- Err(err) => {
- info!(target: "net", "Unable to connect to outbound [{}]: {}", &addr, err);
- {
- let info = &mut self.slot_info.lock().await[slot_number as usize];
- info.addr = None;
- info.channel = None;
- info.state = OutboundState::Open;
- }
- // Notify that channel processing has been finished
- if *self.notify.lock().await {
- self.channel_subscriber.notify(Err(err)).await;
- }
- }
- }
- }
- }
- /// Loops through host addresses to find a outbound address that we can
- /// connect to. Checks whether address is valid by making sure it isn't
- /// our own inbound address, then checks whether it is already connected
- /// (exists) or connecting (pending). Keeps looping until address is
- /// found that passes all checks.
- async fn load_address(&self, slot_number: u32) -> Result<Url> {
- loop {
- let p2p = self.p2p();
- let self_inbound_addr = p2p.settings().external_addr.clone();
- let mut addrs;
- {
- let hosts = p2p.hosts().load_all().await;
- addrs = hosts;
- }
- addrs.shuffle(&mut rand::thread_rng());
- for addr in addrs {
- if p2p.exists(&addr).await {
- continue
- }
- // Obtain a lock on this address to prevent duplicate connections
- if !p2p.add_pending(addr.clone()).await {
- continue
- }
- if self_inbound_addr.contains(&addr) {
- continue
- }
- return Ok(addr)
- }
- debug!(target: "net", "Hosts address pool is empty. Retrying connect slot #{}", slot_number);
- async_util::sleep(p2p.settings().outbound_retry_seconds).await;
- }
- }
- /// Subscribe to a channel.
- pub async fn subscribe_channel(&self) -> Subscription<Result<ChannelPtr>> {
- self.channel_subscriber.clone().subscribe().await
- }
- /// Enable channel_subscriber notifications.
- pub async fn enable_notify(self: Arc<Self>) {
- *self.notify.lock().await = true;
- }
- /// Disable channel_subscriber notifications.
- pub async fn disable_notify(self: Arc<Self>) {
- *self.notify.lock().await = false;
- }
- }
- #[async_trait]
- impl Session for OutboundSession {
- async fn get_info(&self) -> serde_json::Value {
- let mut slots = Vec::new();
- for info in &*self.slot_info.lock().await {
- slots.push(info.get_info().await);
- }
- json!({
- "slots": slots,
- })
- }
- fn p2p(&self) -> Arc<P2p> {
- self.p2p.upgrade().unwrap()
- }
- fn type_id(&self) -> SessionBitflag {
- SESSION_OUTBOUND
- }
- }
|