diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/ws/examples/pong.rs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/third_party/rust/ws/examples/pong.rs b/third_party/rust/ws/examples/pong.rs new file mode 100644 index 0000000000..5c9c62c2e0 --- /dev/null +++ b/third_party/rust/ws/examples/pong.rs @@ -0,0 +1,130 @@ +extern crate env_logger; +extern crate mio_extras; +extern crate time; +/// An example demonstrating how to send and recieve a custom ping/pong frame. +extern crate 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() { + // Setup logging + env_logger::init(); + + // Run the WebSocket + listen("127.0.0.1:3012", |out| Server { + out, + ping_timeout: None, + expire_timeout: None, + }).unwrap(); +} + +// Server WebSocket handler +struct Server { + out: Sender, + ping_timeout: Option<Timeout>, + expire_timeout: Option<Timeout>, +} + +impl Handler for Server { + fn on_open(&mut self, _: Handshake) -> Result<()> { + // schedule a timeout to send a ping every 5 seconds + self.out.timeout(5_000, PING)?; + // schedule a timeout to close the connection if there is no activity for 30 seconds + 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); + + // NOTE: This code demonstrates cleaning up timeouts + 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) { + // Shutdown on any error + println!("Shutting down server for error: {}", err); + self.out.shutdown().unwrap(); + } + + fn on_timeout(&mut self, event: Token) -> Result<()> { + match event { + // PING timeout has occured, send a ping and reschedule + PING => { + self.out.ping(time::precise_time_ns().to_string().into())?; + self.ping_timeout.take(); + self.out.timeout(5_000, PING) + } + // EXPIRE timeout has occured, this means that the connection is inactive, let's close + EXPIRE => self.out.close(CloseCode::Away), + // No other timeouts are possible + _ => Err(Error::new( + ErrorKind::Internal, + "Invalid timeout token encountered!", + )), + } + } + + fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> Result<()> { + // Cancel the old timeout and replace. + if event == EXPIRE { + if let Some(t) = self.expire_timeout.take() { + self.out.cancel(t)? + } + self.expire_timeout = Some(timeout) + } else { + // This ensures there is only one ping timeout at a time + 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 the frame is a pong, print the round-trip time. + // The pong should contain data from out ping, but it isn't guaranteed to. + 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."); + } + } + + // Some activity has occured, so reset the expiration + self.out.timeout(30_000, EXPIRE)?; + + // Run default frame validation + DefaultHandler.on_frame(frame) + } +} + +// For accessing the default handler implementation +struct DefaultHandler; + +impl Handler for DefaultHandler {} |