/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2023 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use async_std::sync::{Arc, Mutex, Weak}; use async_trait::async_trait; use log::{info, warn}; use serde_json::json; use smol::Executor; use url::Url; use crate::{ net::transport::TransportName, system::{StoppableTask, StoppableTaskPtr, Subscriber, SubscriberPtr, Subscription}, util::async_util::sleep, Error, Result, }; use super::{ super::{ChannelPtr, Connector, P2p}, Session, SessionBitflag, SESSION_MANUAL, }; pub struct ManualSession { p2p: Weak, connect_slots: Mutex>, /// Subscriber used to signal channels processing channel_subscriber: SubscriberPtr>, /// Flag to toggle channel_subscriber notifications notify: Mutex, } impl ManualSession { /// Create a new inbound session. pub fn new(p2p: Weak) -> Arc { Arc::new(Self { p2p, connect_slots: Mutex::new(Vec::new()), channel_subscriber: Subscriber::new(), notify: Mutex::new(false), }) } /// 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; } } pub async fn connect(self: Arc, addr: &Url, executor: Arc>) { let task = StoppableTask::new(); task.clone().start( self.clone().channel_connect_loop(addr.clone(), executor.clone()), // Ignore stop handler |_| async {}, Error::NetworkServiceStopped, executor.clone(), ); self.connect_slots.lock().await.push(task); } pub async fn channel_connect_loop( self: Arc, addr: Url, executor: Arc>, ) -> Result<()> { let parent = Arc::downgrade(&self); let settings = self.p2p().settings(); let connector = Connector::new(settings.clone(), Arc::new(parent)); let attempts = settings.manual_attempt_limit; let mut remaining = attempts; // Retrieve preferent outbound transports let outbound_transports = &settings.outbound_transports; // Check that addr transport is in configured outbound transport let addr_transport = TransportName::try_from(addr.clone())?; let transports = if outbound_transports.contains(&addr_transport) { vec![addr_transport] } else { warn!(target: "net::manual_session", "Manual outbound address {} transport is not in accepted outbound transports, will try with: {:?}", addr, outbound_transports); outbound_transports.clone() }; loop { // Loop forever if attempts is 0 // Otherwise loop attempts number of times remaining = if attempts == 0 { 1 } else { remaining - 1 }; if remaining == 0 { break } self.p2p().add_pending(addr.clone()).await; for transport in &transports { // Replace addr transport let mut transport_addr = addr.clone(); transport_addr.set_scheme(&transport.to_scheme())?; info!(target: "net::manual_session", "Connecting to manual outbound [{}]", transport_addr); match connector.connect(transport_addr.clone()).await { Ok(channel) => { // Blacklist goes here info!(target: "net::manual_session", "Connected to manual outbound [{}]", transport_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; //self.clone().attach_protocols(channel, executor.clone()).await?; // 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::manual_session", "Unable to connect to manual outbound [{}]: {}", addr, err); } } } // Notify that channel processing has been finished (failed) if *self.notify.lock().await { self.channel_subscriber.notify(Err(Error::ConnectFailed)).await; } sleep(settings.connect_timeout_seconds.into()).await; } warn!( target: "net::manual_session", "Suspending manual connection to [{}] after {} failed attempts.", &addr, attempts ); Ok(()) } /// Subscribe to a channel. pub async fn subscribe_channel(&self) -> Subscription> { self.channel_subscriber.clone().subscribe().await } /// Enable channel_subscriber notifications. pub async fn enable_notify(self: Arc) { *self.notify.lock().await = true; } /// Disable channel_subscriber notifications. pub async fn disable_notify(self: Arc) { *self.notify.lock().await = false; } // Starts sending keep-alive and address messages across the channels. /*async fn attach_protocols( self: Arc, channel: ChannelPtr, executor: Arc>, ) -> Result<()> { let hosts = self.p2p().hosts(); let protocol_ping = ProtocolPing::new(channel.clone(), self.p2p()); let protocol_addr = ProtocolAddress::new(channel, hosts).await; protocol_ping.start(executor.clone()).await; protocol_addr.start(executor).await; Ok(()) }*/ } #[async_trait] impl Session for ManualSession { async fn get_info(&self) -> serde_json::Value { json!({ "key": 110 }) } fn p2p(&self) -> Arc { self.p2p.upgrade().unwrap() } fn type_id(&self) -> SessionBitflag { SESSION_MANUAL } }