ee5b55fab6
* Feature/ephemera compile (#3437) * Include ephemera node code in repo * Upgrade deps * Bump minor version of cosmwasm-std * Include ephemera in nym-api dep and downgrade rusqlite * Fix clippy and ephemera docs code * More clippy on ephemera --------- Co-authored-by: Andrus Salumets <andrus@nymtech.net> * Start ephemera components in nym-api (#3475) * Start ephemera components in nym-api * Pass nyxd client and use common metric structures * Swap url endpoint with contract for sending rewarding messages * Fix build after rebase * Perform ephemera rewards computation before normal nym-api ones * Remove contract mock from ephemera * Take raw rewards from network monitor * Remove ephemera old reward version * Use nym shutdown procedure in ephemera * Temporary fix for some warnings * Umock contract membership of ephemera (#3574) * Pass nyxd client to members provider * Basic ephemera contract * Add register peer tx * Add query all peers * Nyxd ephemera client * Add registration of ephemera peer * Replace epoch http api with actual contract * Merge ephemera config into nym-api config * Load cluster from contract * Guard nym-outfox out of cosmwasm builds (#3650) * Feature/fixes while testing (#3668) * Commit local peer before querying contract * Default to anyonline * Remove string from template * Fix avg computing * Use updated qa env * Fix clippy * Add unit tests for ephemera contract * Upload ephemera contract in CI * Add group check for peer signup * Peer registration unit test * Start ephemera only on monitoring * Remove old MixnodeToReward struct * Move all ephemera config to its file * Skip with serde ephemera config * Fix default value in args * Feature/add ephemera flag (#3727) * Replace unwrap with error handling * Add ephemera enable flag * Fix template * Add json schema to ephemera contract (#3735) * Update lock files * Update changelog --------- Co-authored-by: Andrus Salumets <andrus@nymtech.net>
139 lines
4.4 KiB
Rust
139 lines
4.4 KiB
Rust
use std::net::SocketAddr;
|
|
|
|
use anyhow::Result;
|
|
use futures_util::SinkExt;
|
|
use log::{debug, error, info};
|
|
use tokio::sync::broadcast::error::RecvError;
|
|
use tokio::{
|
|
net::{TcpListener, TcpStream},
|
|
sync::broadcast,
|
|
};
|
|
use tokio_tungstenite::{tungstenite::Message, WebSocketStream};
|
|
|
|
use crate::api::types::ApiBlock;
|
|
use crate::block::types::block::Block;
|
|
|
|
pub struct WsConnection {
|
|
socket: WebSocketStream<TcpStream>,
|
|
pending_messages_rx: broadcast::Receiver<Message>,
|
|
address: SocketAddr,
|
|
}
|
|
|
|
impl WsConnection {
|
|
pub fn new(
|
|
socket: WebSocketStream<TcpStream>,
|
|
pending_messages_rx: broadcast::Receiver<Message>,
|
|
address: SocketAddr,
|
|
) -> WsConnection {
|
|
WsConnection {
|
|
socket,
|
|
pending_messages_rx,
|
|
address,
|
|
}
|
|
}
|
|
|
|
pub async fn accept_messages(mut self) {
|
|
loop {
|
|
match self.pending_messages_rx.recv().await {
|
|
Ok(msg) => {
|
|
debug!("Sending message to {}", self.address);
|
|
if let Err(err) = self.socket.send(msg).await {
|
|
error!("Error sending message to websocket client: {:?}", err);
|
|
}
|
|
}
|
|
Err(e) => {
|
|
if RecvError::Closed == e {
|
|
error!("Error while receiving message from broadcast channel: {:?}, closing connection", e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct WsMessageBroadcaster {
|
|
pub(crate) pending_messages_tx: broadcast::Sender<Message>,
|
|
}
|
|
|
|
impl WsMessageBroadcaster {
|
|
pub(crate) fn new(pending_messages_tx: broadcast::Sender<Message>) -> WsMessageBroadcaster {
|
|
WsMessageBroadcaster {
|
|
pending_messages_tx,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn send_block(&self, block: &Block) -> Result<()> {
|
|
debug!("Sending block {} to websocket clients", block.header.hash);
|
|
let json = serde_json::to_string::<ApiBlock>(block.into())?;
|
|
let msg = Message::Text(json);
|
|
self.pending_messages_tx.send(msg)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub(crate) struct WsManager {
|
|
pub(crate) listener: Option<TcpListener>,
|
|
pub(crate) ws_address: String,
|
|
pub(crate) pending_messages_tx: broadcast::Sender<Message>,
|
|
_pending_messages_rcv: broadcast::Receiver<Message>,
|
|
}
|
|
|
|
impl WsManager {
|
|
#[allow(clippy::used_underscore_binding)]
|
|
pub(crate) fn new(address: String) -> (WsManager, WsMessageBroadcaster) {
|
|
let (pending_messages_tx, _pending_messages_rcv) = broadcast::channel(1000);
|
|
let ws_message_broadcast = WsMessageBroadcaster::new(pending_messages_tx.clone());
|
|
let manager = WsManager {
|
|
listener: None,
|
|
ws_address: address,
|
|
pending_messages_tx,
|
|
_pending_messages_rcv,
|
|
};
|
|
(manager, ws_message_broadcast)
|
|
}
|
|
|
|
pub(crate) async fn listen(&mut self) -> Result<()> {
|
|
let listener = TcpListener::bind(&self.ws_address).await?;
|
|
info!("Listening for websocket connections on {}", self.ws_address);
|
|
self.listener = Some(listener);
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn run(mut self) -> Result<()> {
|
|
let listener = self.listener.take().expect("Listener not set");
|
|
loop {
|
|
tokio::select! {
|
|
res = listener.accept() => {
|
|
match res {
|
|
Ok((stream, addr)) => {
|
|
debug!("Accepted websocket connection from: {}", addr);
|
|
self.handle_connection(stream, addr);
|
|
}
|
|
Err(err) => {
|
|
return Err(err.into());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn handle_connection(&self, stream: TcpStream, addr: SocketAddr) {
|
|
let pending_messages_rx = self.pending_messages_tx.subscribe();
|
|
tokio::spawn(async move {
|
|
match tokio_tungstenite::accept_async(stream).await {
|
|
Ok(ws_stream) => {
|
|
let connection = WsConnection::new(ws_stream, pending_messages_rx, addr);
|
|
connection.accept_messages().await;
|
|
}
|
|
Err(err) => {
|
|
error!("Error accepting websocket connection: {:?}", err);
|
|
}
|
|
}
|
|
debug!("Websocket connection closed");
|
|
});
|
|
}
|
|
}
|