use mio_extras::timer::Timeout;
use shared::structs::Config;
use url::Url;
use ws::{listen, CloseCode, Handshake, Sender};
const PING: Token = Token(1);
const EXPIRE: Token = Token(2);
struct Transport {
out: Sender,
ping_timeout: Option<Timeout>,
expire_timeout: Option<Timeout>,
}
impl Server for Transport {
fn on_open(&mut self, _: Handshake) -> Result<()> {
self.out.timeout(60_000, PING)?;
self.out.timeout(180_000, EXPIRE)
}
fn on_message(&mut self, msg: Message) -> Result<()> {
println!("Server got message '{}'. ", msg);
self.out.send(msg)
}
fn on_close(&mut self, code: CloseCode, reason: &str) {
println!("WebSocket closing for ({:?}) {}", code, reason);
if let Some(t) = self.ping_timeout.take() {
self.out.cancel(t).unwrap();
}
if let Some(t) = self.expire_timeout.take() {
self.out.cancel(t).unwrap();
}
println!("Shutting down server after first connection closes.");
self.out.shutdown().unwrap();
}
fn on_error(&mut self, err: Error) {
println!("Shutting down server for error: {}", err);
self.out.shutdown().unwrap();
}
fn on_timeout(&mut self, event: Token) -> Result<()> {
match event {
PING => {
self.out.ping(time::precise_time_ns().to_string().into())?;
self.ping_timeout.take();
self.out.timeout(5_000, PING)
}
EXPIRE => self.out.close(CloseCode::Away),
_ => Err(Error::new(
ErrorKind::Internal,
"Invalid timeout token encountered!",
)),
}
}
fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> Result<()> {
if event == EXPIRE {
if let Some(t) = self.expire_timeout.take() {
self.out.cancel(t)?
}
self.expire_timeout = Some(timeout)
} else {
if let Some(t) = self.ping_timeout.take() {
self.out.cancel(t)?
}
self.ping_timeout = Some(timeout)
}
Ok(())
}
fn on_frame(&mut self, frame: Frame) -> Result<Option<Frame>> {
if frame.opcode() == OpCode::Pong {
if let Ok(pong) = from_utf8(frame.payload())?.parse::<u64>() {
let now = time::precise_time_ns();
println!("RTT is {:.3}ms.", (now - pong) as f64 / 1_000_000f64);
} else {
println!("Received bad pong.");
}
}
self.out.timeout(30_000, EXPIRE)?;
DefaultHandler.on_frame(frame)
}
}
#[proc_macro]
pub fn wsPath(config: Config) {
format!(
"wss://{domain}/ws_api/account/{accountId}/messaging/brand/{token}?v=${apiVersion}",
domain = config.domain,
accountId = config.accountId,
token = config.token,
apiVersion = config.apiVersion
)
}
struct DefaultHandler;
impl Server for DefaultHandler {}