extern crate env_logger;
extern crate mio_extras;
extern crate time;
extern crate solana_ws as ws;
use std::str::from_utf8;
use mio_extras::timer::Timeout;
use ws::util::Token;
use ws::{listen, CloseCode, Error, ErrorKind, Frame, Handler, Handshake, Message, OpCode, Result,
Sender};
const PING: Token = Token(1);
const EXPIRE: Token = Token(2);
fn main() {
env_logger::init();
listen("127.0.0.1:3012", |out| Server {
out,
ping_timeout: None,
expire_timeout: None,
}).unwrap();
}
struct Server {
out: Sender,
ping_timeout: Option<Timeout>,
expire_timeout: Option<Timeout>,
}
impl Handler for Server {
fn on_open(&mut self, _: Handshake) -> Result<()> {
self.out.timeout(5_000, PING)?;
self.out.timeout(30_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)
}
}
struct DefaultHandler;
impl Handler for DefaultHandler {}