use anyhow::{Result, bail};
use eframe::egui;
use ewebsock::{WsEvent, WsMessage, WsReceiver, WsSender};
use snow::{HandshakeState, TransportState, params::NoiseParams};
use std::sync::LazyLock;
use freezeout_core::message::SignedMessage;
static NOISE_PARAMS: LazyLock<NoiseParams> =
LazyLock::new(|| "Noise_NN_25519_ChaChaPoly_BLAKE2s".parse().unwrap());
pub struct Connection {
ws_sender: WsSender,
ws_receiver: WsReceiver,
noise_handshake: Option<HandshakeState>,
noise_transport: Option<TransportState>,
noise_buf: Vec<u8>,
}
#[derive(Debug)]
pub enum ConnectionEvent {
Open,
Close,
Error(String),
Message(SignedMessage),
}
impl Connection {
pub fn connect(url: &str, ctx: egui::Context) -> Result<Self> {
let wakeup = move || ctx.request_repaint();
match ewebsock::connect_with_wakeup(url, Default::default(), wakeup) {
Ok((ws_sender, ws_receiver)) => Ok(Connection {
ws_sender,
ws_receiver,
noise_handshake: None,
noise_transport: None,
noise_buf: vec![0u8; 8192],
}),
Err(e) => bail!("Connection error {e}"),
}
}
pub fn close(&mut self) {
self.ws_sender.close();
}
pub fn send(&mut self, msg: &SignedMessage) {
if let Some(noise) = self.noise_transport.as_mut() {
let len = noise
.write_message(&msg.serialize(), &mut self.noise_buf)
.expect("Cannot write noise message");
self.ws_sender
.send(WsMessage::Binary(self.noise_buf[..len].to_vec()));
}
}
pub fn poll(&mut self) -> Option<ConnectionEvent> {
if let Some(event) = self.ws_receiver.try_recv() {
match event {
WsEvent::Opened => {
let mut noise = snow::Builder::new(NOISE_PARAMS.clone())
.build_initiator()
.expect("Cannot initiate noise protocol");
let len = noise
.write_message(&[], &mut self.noise_buf)
.expect("Cannot initiate noise handshake");
self.ws_sender
.send(WsMessage::Binary(self.noise_buf[..len].to_vec()));
self.noise_handshake = Some(noise);
None
}
WsEvent::Message(msg) => {
if let WsMessage::Binary(bytes) = msg {
if let Some(mut noise) = self.noise_handshake.take() {
if noise.read_message(&bytes, &mut self.noise_buf).is_err() {
return Some(ConnectionEvent::Error(
"Cannot complete noise handshake".to_string(),
));
}
let Ok(transport) = noise.into_transport_mode() else {
return Some(ConnectionEvent::Error(
"Cannot create noise transport".to_string(),
));
};
self.noise_transport = Some(transport);
Some(ConnectionEvent::Open)
} else if let Some(noise) = self.noise_transport.as_mut() {
let res = noise
.read_message(&bytes, &mut self.noise_buf)
.map_err(anyhow::Error::from)
.and_then(|len| {
SignedMessage::deserialize_and_verify(&self.noise_buf[..len])
});
match res {
Ok(msg) => Some(ConnectionEvent::Message(msg)),
Err(e) => Some(ConnectionEvent::Error(e.to_string())),
}
} else {
self.ws_sender.close();
Some(ConnectionEvent::Close)
}
} else {
None
}
}
WsEvent::Error(e) => Some(ConnectionEvent::Error(e)),
WsEvent::Closed => Some(ConnectionEvent::Close),
}
} else {
None
}
}
}