use async_trait::async_trait;
use futures::StreamExt;
use std::collections::HashMap;
use std::net::SocketAddr;
use tokio::sync::mpsc;
use tokio::{
io::AsyncWriteExt,
net::{TcpListener, TcpStream},
sync::mpsc::{Receiver, Sender},
};
use tokio_util::codec::{FramedRead, LengthDelimitedCodec};
use crate::Link;
use crate::{
hex8,
msg::{Addr, YgwMessage},
LinkStatus, Result, YgwError, YgwLinkNodeProperties, YgwNode,
};
const MAX_FRAME_LENGTH: usize = 16 * 1024 * 1024;
pub struct RelayNode {
props: YgwLinkNodeProperties,
addr: SocketAddr,
links: Vec<Link>,
}
#[async_trait]
impl YgwNode for RelayNode {
fn properties(&self) -> &YgwLinkNodeProperties {
&self.props
}
fn sub_links(&self) -> &[Link] {
&self.links
}
async fn run(
mut self: Box<Self>,
node_id: u32,
msg_tx: Sender<YgwMessage>,
mut msg_rx: Receiver<YgwMessage>,
) -> Result<()> {
let addr = Addr::new(node_id, 0);
let link_status = LinkStatus::new(addr);
link_status.send(&msg_tx).await?;
let listener = TcpListener::bind(self.addr)
.await
.map_err(|e| YgwError::IOError(format!("Cannot bind to {}", self.addr), e))?;
log::info!("RelayNode listening on {}", self.addr);
let (main_tx, mut main_rx) = mpsc::channel::<ClientCommand>(100);
let mut client_map = HashMap::new();
loop {
tokio::select! {
Ok((stream, client_addr)) = listener.accept() => {
log::info!("New client from {}", client_addr);
let msg_tx = msg_tx.clone();
let main_tx = main_tx.clone();
tokio::spawn(async move {
if let Err(e) = handle_client(stream, msg_tx, node_id, main_tx).await {
log::warn!("Client handling error: {}", e);
}
});
}
Some(cmd) = main_rx.recv() => {
match cmd {
ClientCommand::Register { link_id, sender } => {
client_map.insert(link_id, sender);
log::info!("Registered link_id {}", link_id);
}
ClientCommand::Unregister { link_id } => {
client_map.remove(&link_id);
log::info!("Unregistered link_id {}", link_id);
}
}
}
msg = msg_rx.recv() => {
if let Some(msg) = msg {
let link_id = msg.link_id();
if let Some(client_tx) = client_map.get(&link_id) {
if let Err(e) = client_tx.send(msg).await {
log::warn!("Failed to send message to link_id {}: {}", link_id, e);
}
} else {
log::warn!("No client found for link_id {}", link_id);
}
} else {
break;
}
}
}
}
log::debug!("RelayNode exiting");
Ok(())
}
}
async fn handle_client(
stream: TcpStream,
tx: Sender<YgwMessage>,
node_id: u32,
main_tx: mpsc::Sender<ClientCommand>,
) -> Result<()> {
let peer_addr = stream.peer_addr()?;
let (reader, mut writer) = stream.into_split();
let mut codec = LengthDelimitedCodec::new();
codec.set_max_frame_length(MAX_FRAME_LENGTH);
let mut framed_reader = FramedRead::new(reader, codec);
let mut link_id: Option<u32> = None;
let mut client_rx: Option<Receiver<YgwMessage>> = None;
let mut link_status: Option<LinkStatus> = None;
let mut interval = tokio::time::interval(std::time::Duration::from_secs(1));
loop {
tokio::select! {
result = framed_reader.next() => {
match result {
Some(Ok(buf)) => {
let mut buf = buf.freeze();
match YgwMessage::decode(&mut buf) {
Ok(mut msg) => {
log::trace!("Received message {:?}", msg);
msg.set_node_id(node_id);
if link_id.is_none() {
let (client_tx, rx) = tokio::sync::mpsc::channel(100);
if let Err(_) = main_tx.send(ClientCommand::Register {
link_id: msg.link_id(),
sender: client_tx,
}).await { break;
}
client_rx = Some(rx);
link_status = Some(LinkStatus::new(Addr::new(node_id, msg.link_id())));
link_id.replace(msg.link_id());
}
if let Some(link_status) = &mut link_status {
link_status.data_in(1, buf.len() as u64);
}
if let Err(_) = tx.send(msg).await {
break;
}
}
Err(err) => log::warn!("Cannot decode data {}: {:?}", hex8(&buf), err),
}
}
Some(Err(e)) => {
log::warn!("Error reading from {}: {:?}", peer_addr, e);
return Err(YgwError::IOError(format!("Error reading from {peer_addr}"), e));
}
None => {
log::info!("Client connection {} closed", peer_addr);
break;
}
}
}
msg = async {
match &mut client_rx {
Some(rx) => rx.recv().await,
None => None
}
}, if client_rx.is_some() => {
match msg {
Some(msg) => {
let buf = msg.encode(0);
if let Err(e) = writer.write_all(&buf).await {
log::warn!("Error sending to client {}: {:?}", peer_addr, e);
break;
}
}
None => { break;}
}
}
_ = interval.tick() => {
if let Some(link_status) = &link_status {
if let Err(_) = link_status.send(&tx).await {
break;
}
}
}
}
}
if let Some(link_id) = link_id {
let _ = main_tx.send(ClientCommand::Unregister { link_id }).await;
}
Ok(())
}
enum ClientCommand {
Register {
link_id: u32,
sender: Sender<YgwMessage>,
},
Unregister {
link_id: u32,
},
}
pub struct RelayNodeBuilder {
name: String,
addr: SocketAddr,
links: Vec<Link>,
}
impl RelayNodeBuilder {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
addr: ([127, 0, 0, 1], 7899).into(),
links: Vec::new(),
}
}
pub fn set_addr(mut self, addr: SocketAddr) -> Self {
self.addr = addr;
self
}
pub fn add_link(mut self, id: u32, props: YgwLinkNodeProperties) -> Self {
self.links.push(Link { id, props });
self
}
pub fn build(self) -> RelayNode {
RelayNode {
props: YgwLinkNodeProperties::new(self.name, "relays data from external clients"),
addr: self.addr,
links: self.links,
}
}
}