use std::borrow::Cow; use std::convert::Into; use mio; use mio::Token; use mio_extras::timer::Timeout; use url; use io::ALL; use message; use protocol::CloseCode; use result::{Error, Result}; use std::cmp::PartialEq; use std::hash::{Hash, Hasher}; use std::fmt; #[derive(Debug, Clone)] pub enum Signal { Message(message::Message), Close(CloseCode, Cow<'static, str>), Ping(Vec), Pong(Vec), Connect(url::Url), Shutdown, Timeout { delay: u64, token: Token }, Cancel(Timeout), } #[derive(Debug, Clone)] pub struct Command { token: Token, signal: Signal, connection_id: u32, } impl Command { pub fn token(&self) -> Token { self.token } pub fn into_signal(self) -> Signal { self.signal } pub fn connection_id(&self) -> u32 { self.connection_id } } /// A representation of the output of the WebSocket connection. Use this to send messages to the /// other endpoint. #[derive(Clone)] pub struct Sender { token: Token, channel: mio::channel::SyncSender, connection_id: u32, } impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Sender {{ token: {:?}, channel: mio::channel::SyncSender, connection_id: {:?} }}", self.token, self.connection_id) } } impl PartialEq for Sender { fn eq(&self, other: &Sender) -> bool { self.token == other.token && self.connection_id == other.connection_id } } impl Eq for Sender { } impl Hash for Sender { fn hash(&self, state: &mut H) { self.connection_id.hash(state); self.token.hash(state); } } impl Sender { #[doc(hidden)] #[inline] pub fn new( token: Token, channel: mio::channel::SyncSender, connection_id: u32, ) -> Sender { Sender { token, channel, connection_id, } } /// A Token identifying this sender within the WebSocket. #[inline] pub fn token(&self) -> Token { self.token } /// A connection_id identifying this sender within the WebSocket. #[inline] pub fn connection_id(&self) -> u32 { self.connection_id } /// Send a message over the connection. #[inline] pub fn send(&self, msg: M) -> Result<()> where M: Into, { self.channel .send(Command { token: self.token, signal: Signal::Message(msg.into()), connection_id: self.connection_id, }) .map_err(Error::from) } /// Send a message to the endpoints of all connections. /// /// Be careful with this method. It does not discriminate between client and server connections. /// If your WebSocket is only functioning as a server, then usage is simple, this method will /// send a copy of the message to each connected client. However, if you have a WebSocket that /// is listening for connections and is also connected to another WebSocket, this method will /// broadcast a copy of the message to all the clients connected and to that WebSocket server. #[inline] pub fn broadcast(&self, msg: M) -> Result<()> where M: Into, { self.channel .send(Command { token: ALL, signal: Signal::Message(msg.into()), connection_id: self.connection_id, }) .map_err(Error::from) } /// Send a close code to the other endpoint. #[inline] pub fn close(&self, code: CloseCode) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Close(code, "".into()), connection_id: self.connection_id, }) .map_err(Error::from) } /// Send a close code and provide a descriptive reason for closing. #[inline] pub fn close_with_reason(&self, code: CloseCode, reason: S) -> Result<()> where S: Into>, { self.channel .send(Command { token: self.token, signal: Signal::Close(code, reason.into()), connection_id: self.connection_id, }) .map_err(Error::from) } /// Send a ping to the other endpoint with the given test data. #[inline] pub fn ping(&self, data: Vec) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Ping(data), connection_id: self.connection_id, }) .map_err(Error::from) } /// Send a pong to the other endpoint responding with the given test data. #[inline] pub fn pong(&self, data: Vec) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Pong(data), connection_id: self.connection_id, }) .map_err(Error::from) } /// Queue a new connection on this WebSocket to the specified URL. #[inline] pub fn connect(&self, url: url::Url) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Connect(url), connection_id: self.connection_id, }) .map_err(Error::from) } /// Request that all connections terminate and that the WebSocket stop running. #[inline] pub fn shutdown(&self) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Shutdown, connection_id: self.connection_id, }) .map_err(Error::from) } /// Schedule a `token` to be sent to the WebSocket Handler's `on_timeout` method /// after `ms` milliseconds #[inline] pub fn timeout(&self, ms: u64, token: Token) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Timeout { delay: ms, token }, connection_id: self.connection_id, }) .map_err(Error::from) } /// Queue the cancellation of a previously scheduled timeout. /// /// This method is not guaranteed to prevent the timeout from occurring, because it is /// possible to call this method after a timeout has already occurred. It is still necessary to /// handle spurious timeouts. #[inline] pub fn cancel(&self, timeout: Timeout) -> Result<()> { self.channel .send(Command { token: self.token, signal: Signal::Cancel(timeout), connection_id: self.connection_id, }) .map_err(Error::from) } }