|  | @@ -1,188 +0,0 @@
 | 
	
		
			
				|  |  | -use crate::{pool::Event, Error};
 | 
	
		
			
				|  |  | -use futures::Future;
 | 
	
		
			
				|  |  | -use futures_util::{SinkExt, StreamExt};
 | 
	
		
			
				|  |  | -use nostr_rs_types::{Request, Response};
 | 
	
		
			
				|  |  | -use std::{
 | 
	
		
			
				|  |  | -    pin::Pin,
 | 
	
		
			
				|  |  | -    sync::{
 | 
	
		
			
				|  |  | -        atomic::{AtomicBool, Ordering::Relaxed},
 | 
	
		
			
				|  |  | -        Arc,
 | 
	
		
			
				|  |  | -    },
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -use tokio::{
 | 
	
		
			
				|  |  | -    sync::mpsc,
 | 
	
		
			
				|  |  | -    task::JoinHandle,
 | 
	
		
			
				|  |  | -    time::{sleep, timeout, Duration},
 | 
	
		
			
				|  |  | -};
 | 
	
		
			
				|  |  | -use tokio_tungstenite::{connect_async, tungstenite::Message};
 | 
	
		
			
				|  |  | -use url::Url;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/// Relayer object
 | 
	
		
			
				|  |  | -#[derive(Debug)]
 | 
	
		
			
				|  |  | -pub struct Relayer {
 | 
	
		
			
				|  |  | -    /// URL of the relayer
 | 
	
		
			
				|  |  | -    pub url: Url,
 | 
	
		
			
				|  |  | -    /// Sender to the relayer. This can be used to send a Requests to this
 | 
	
		
			
				|  |  | -    /// relayer
 | 
	
		
			
				|  |  | -    pub send_to_socket: mpsc::Sender<Request>,
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    worker: JoinHandle<()>,
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    is_connected: Arc<AtomicBool>,
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const NO_ACTIVITY_TIMEOUT_SECS: u64 = 120;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -impl Drop for Relayer {
 | 
	
		
			
				|  |  | -    fn drop(&mut self) {
 | 
	
		
			
				|  |  | -        self.worker.abort()
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -impl Relayer {
 | 
	
		
			
				|  |  | -    /// Creates a new relayer
 | 
	
		
			
				|  |  | -    pub fn new<F>(
 | 
	
		
			
				|  |  | -        broadcast_to_listeners: mpsc::Sender<(Event, Url)>,
 | 
	
		
			
				|  |  | -        url: Url,
 | 
	
		
			
				|  |  | -        on_connection: Option<F>,
 | 
	
		
			
				|  |  | -    ) -> Self
 | 
	
		
			
				|  |  | -    where
 | 
	
		
			
				|  |  | -        F: (Fn(&Url, mpsc::Sender<Request>) -> Pin<Box<dyn Future<Output = ()> + Send>>)
 | 
	
		
			
				|  |  | -            + Send
 | 
	
		
			
				|  |  | -            + Sync
 | 
	
		
			
				|  |  | -            + 'static,
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        let (sender_to_socket, send_to_socket) = mpsc::channel(100_000);
 | 
	
		
			
				|  |  | -        let is_connected = Arc::new(AtomicBool::new(false));
 | 
	
		
			
				|  |  | -        let worker = Self::spawn_background_client(
 | 
	
		
			
				|  |  | -            broadcast_to_listeners,
 | 
	
		
			
				|  |  | -            sender_to_socket.clone(),
 | 
	
		
			
				|  |  | -            send_to_socket,
 | 
	
		
			
				|  |  | -            url.clone(),
 | 
	
		
			
				|  |  | -            is_connected.clone(),
 | 
	
		
			
				|  |  | -            on_connection,
 | 
	
		
			
				|  |  | -        );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        Self {
 | 
	
		
			
				|  |  | -            url,
 | 
	
		
			
				|  |  | -            is_connected,
 | 
	
		
			
				|  |  | -            send_to_socket: sender_to_socket,
 | 
	
		
			
				|  |  | -            worker,
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    fn spawn_background_client<F>(
 | 
	
		
			
				|  |  | -        broadcast_to_listeners: mpsc::Sender<(Event, Url)>,
 | 
	
		
			
				|  |  | -        sender_to_socket: mpsc::Sender<Request>,
 | 
	
		
			
				|  |  | -        mut send_to_socket: mpsc::Receiver<Request>,
 | 
	
		
			
				|  |  | -        url: Url,
 | 
	
		
			
				|  |  | -        is_connected: Arc<AtomicBool>,
 | 
	
		
			
				|  |  | -        on_connection: Option<F>,
 | 
	
		
			
				|  |  | -    ) -> JoinHandle<()>
 | 
	
		
			
				|  |  | -    where
 | 
	
		
			
				|  |  | -        F: (Fn(&Url, mpsc::Sender<Request>) -> Pin<Box<dyn Future<Output = ()> + Send>>)
 | 
	
		
			
				|  |  | -            + Send
 | 
	
		
			
				|  |  | -            + Sync
 | 
	
		
			
				|  |  | -            + 'static,
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        is_connected.store(false, Relaxed);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        tokio::spawn(async move {
 | 
	
		
			
				|  |  | -            let mut connection_attempts = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            loop {
 | 
	
		
			
				|  |  | -                log::warn!("{}: Connect attempt {}", url, connection_attempts);
 | 
	
		
			
				|  |  | -                connection_attempts += 1;
 | 
	
		
			
				|  |  | -                let mut socket = match connect_async(url.clone()).await {
 | 
	
		
			
				|  |  | -                    Ok(x) => x.0,
 | 
	
		
			
				|  |  | -                    Err(err) => {
 | 
	
		
			
				|  |  | -                        log::warn!("{}: Failed to connect: {}", url, err);
 | 
	
		
			
				|  |  | -                        sleep(Duration::from_secs(5)).await;
 | 
	
		
			
				|  |  | -                        continue;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                log::info!("Connected to {}", url);
 | 
	
		
			
				|  |  | -                connection_attempts = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if let Some(on_connection) = &on_connection {
 | 
	
		
			
				|  |  | -                    on_connection(&url, sender_to_socket.clone()).await;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                loop {
 | 
	
		
			
				|  |  | -                    tokio::select! {
 | 
	
		
			
				|  |  | -                        Some(msg) = send_to_socket.recv() => {
 | 
	
		
			
				|  |  | -                            if let Ok(json) = serde_json::to_string(&msg) {
 | 
	
		
			
				|  |  | -                                log::info!("{}: Sending {}", url, json);
 | 
	
		
			
				|  |  | -                                if let Err(x) = socket.send(Message::Text(json)).await {
 | 
	
		
			
				|  |  | -                                    log::error!("{} : Reconnecting due {}", url, x);
 | 
	
		
			
				|  |  | -                                    break;
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | -                            }
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                        msg = timeout(Duration::from_secs(NO_ACTIVITY_TIMEOUT_SECS), socket.next()) => {
 | 
	
		
			
				|  |  | -                            let msg = if let Ok(Some(Ok(msg))) = msg {
 | 
	
		
			
				|  |  | -                                is_connected.store(true, Relaxed);
 | 
	
		
			
				|  |  | -                                    match msg {
 | 
	
		
			
				|  |  | -                                        Message::Text(text) => text,
 | 
	
		
			
				|  |  | -                                        Message::Ping(msg) => {
 | 
	
		
			
				|  |  | -                                            if let Err(x) = socket.send(Message::Pong(msg)).await {
 | 
	
		
			
				|  |  | -                                                log::error!("{} : Reconnecting due error at sending Pong: {:?}", url, x);
 | 
	
		
			
				|  |  | -                                                break;
 | 
	
		
			
				|  |  | -                                            }
 | 
	
		
			
				|  |  | -                                            continue;
 | 
	
		
			
				|  |  | -                                        },
 | 
	
		
			
				|  |  | -                                        msg => {
 | 
	
		
			
				|  |  | -                                            log::error!("Unexpected {:?}", msg);
 | 
	
		
			
				|  |  | -                                            continue;
 | 
	
		
			
				|  |  | -                                        }
 | 
	
		
			
				|  |  | -                                    }
 | 
	
		
			
				|  |  | -                                } else {
 | 
	
		
			
				|  |  | -                                    log::error!("{} Reconnecting client due of empty recv: {:?}", url, msg);
 | 
	
		
			
				|  |  | -                                    break;
 | 
	
		
			
				|  |  | -                                };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                            if msg.is_empty() {
 | 
	
		
			
				|  |  | -                                continue;
 | 
	
		
			
				|  |  | -                            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                            log::info!("New message: {}", msg);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                            let msg: Result<Response, _> = serde_json::from_str(&msg);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                            if let Ok(msg) = msg {
 | 
	
		
			
				|  |  | -                                if let Err(error) = broadcast_to_listeners.try_send((Event::Response(msg.into()), url.clone())) {
 | 
	
		
			
				|  |  | -                                    log::error!("{}: Reconnecting client because of {}", url, error);
 | 
	
		
			
				|  |  | -                                    break;
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | -                            }
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                        else => {
 | 
	
		
			
				|  |  | -                            log::warn!("{}: else", url);
 | 
	
		
			
				|  |  | -                            break;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                is_connected.store(false, Relaxed);
 | 
	
		
			
				|  |  | -                // Throttle down to not spam the server with reconnections
 | 
	
		
			
				|  |  | -                sleep(Duration::from_millis(500)).await;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        })
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Checks if the relayer is connected. It is guaranteed that the relayer is
 | 
	
		
			
				|  |  | -    /// connected if this method returns true.
 | 
	
		
			
				|  |  | -    pub fn is_connected(&self) -> bool {
 | 
	
		
			
				|  |  | -        self.is_connected.load(Relaxed)
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Sends a requests to this relayer
 | 
	
		
			
				|  |  | -    pub async fn send(&self, request: Request) -> Result<(), Error> {
 | 
	
		
			
				|  |  | -        self.send_to_socket
 | 
	
		
			
				|  |  | -            .send(request)
 | 
	
		
			
				|  |  | -            .await
 | 
	
		
			
				|  |  | -            .map_err(|e| Error::Sync(Box::new(e)))
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -}
 |