use std::fmt; use std::convert::{Into, From}; /// WebSocket message opcode as in RFC 6455. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum OpCode { Data(Data), Control(Control), } /// Data opcodes as in RFC 6455 #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Data { /// 0x0 denotes a continuation frame Continue, /// 0x1 denotes a text frame Text, /// 0x2 denotes a binary frame Binary, /// 0x3-7 are reserved for further non-control frames Reserved(u8), } /// Control opcodes as in RFC 6455 #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Control { /// 0x8 denotes a connection close Close, /// 0x9 denotes a ping Ping, /// 0xa denotes a pong Pong, /// 0xb-f are reserved for further control frames Reserved(u8), } impl fmt::Display for Data { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Data::Continue => write!(f, "CONTINUE"), Data::Text => write!(f, "TEXT"), Data::Binary => write!(f, "BINARY"), Data::Reserved(x) => write!(f, "RESERVED_DATA_{}", x), } } } impl fmt::Display for Control { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Control::Close => write!(f, "CLOSE"), Control::Ping => write!(f, "PING"), Control::Pong => write!(f, "PONG"), Control::Reserved(x) => write!(f, "RESERVED_CONTROL_{}", x), } } } impl fmt::Display for OpCode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { OpCode::Data(d) => d.fmt(f), OpCode::Control(c) => c.fmt(f), } } } impl Into for OpCode { fn into(self) -> u8 { use self::Data::{Continue, Text, Binary}; use self::Control::{Close, Ping, Pong}; use self::OpCode::*; match self { Data(Continue) => 0, Data(Text) => 1, Data(Binary) => 2, Data(self::Data::Reserved(i)) => i, Control(Close) => 8, Control(Ping) => 9, Control(Pong) => 10, Control(self::Control::Reserved(i)) => i, } } } impl From for OpCode { fn from(byte: u8) -> OpCode { use self::Data::{Continue, Text, Binary}; use self::Control::{Close, Ping, Pong}; use self::OpCode::*; match byte { 0 => Data(Continue), 1 => Data(Text), 2 => Data(Binary), i @ 3 ... 7 => Data(self::Data::Reserved(i)), 8 => Control(Close), 9 => Control(Ping), 10 => Control(Pong), i @ 11 ... 15 => Control(self::Control::Reserved(i)), _ => panic!("Bug: OpCode out of range"), } } } use self::CloseCode::*; /// Status code used to indicate why an endpoint is closing the WebSocket connection. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum CloseCode { /// Indicates a normal closure, meaning that the purpose for /// which the connection was established has been fulfilled. Normal, /// Indicates that an endpoint is "going away", such as a server /// going down or a browser having navigated away from a page. Away, /// Indicates that an endpoint is terminating the connection due /// to a protocol error. Protocol, /// Indicates that an endpoint is terminating the connection /// because it has received a type of data it cannot accept (e.g., an /// endpoint that understands only text data MAY send this if it /// receives a binary message). Unsupported, /// Indicates that no status code was included in a closing frame. This /// close code makes it possible to use a single method, `on_close` to /// handle even cases where no close code was provided. Status, /// Indicates an abnormal closure. If the abnormal closure was due to an /// error, this close code will not be used. Instead, the `on_error` method /// of the handler will be called with the error. However, if the connection /// is simply dropped, without an error, this close code will be sent to the /// handler. Abnormal, /// Indicates that an endpoint is terminating the connection /// because it has received data within a message that was not /// consistent with the type of the message (e.g., non-UTF-8 [RFC3629] /// data within a text message). Invalid, /// Indicates that an endpoint is terminating the connection /// because it has received a message that violates its policy. This /// is a generic status code that can be returned when there is no /// other more suitable status code (e.g., Unsupported or Size) or if there /// is a need to hide specific details about the policy. Policy, /// Indicates that an endpoint is terminating the connection /// because it has received a message that is too big for it to /// process. Size, /// Indicates that an endpoint (client) is terminating the /// connection because it has expected the server to negotiate one or /// more extension, but the server didn't return them in the response /// message of the WebSocket handshake. The list of extensions that /// are needed should be given as the reason for closing. /// Note that this status code is not used by the server, because it /// can fail the WebSocket handshake instead. Extension, /// Indicates that a server is terminating the connection because /// it encountered an unexpected condition that prevented it from /// fulfilling the request. Error, /// Indicates that the server is restarting. A client may choose to reconnect, /// and if it does, it should use a randomized delay of 5-30 seconds between attempts. Restart, /// Indicates that the server is overloaded and the client should either connect /// to a different IP (when multiple targets exist), or reconnect to the same IP /// when a user has performed an action. Again, #[doc(hidden)] Tls, #[doc(hidden)] Reserved(u16), #[doc(hidden)] Iana(u16), #[doc(hidden)] Library(u16), #[doc(hidden)] Bad(u16), } impl CloseCode { /// Check if this CloseCode is allowed. pub fn is_allowed(&self) -> bool { match *self { Bad(_) => false, Reserved(_) => false, Status => false, Abnormal => false, Tls => false, _ => true, } } } impl Into for CloseCode { fn into(self) -> u16 { match self { Normal => 1000, Away => 1001, Protocol => 1002, Unsupported => 1003, Status => 1005, Abnormal => 1006, Invalid => 1007, Policy => 1008, Size => 1009, Extension => 1010, Error => 1011, Restart => 1012, Again => 1013, Tls => 1015, Reserved(code) => code, Iana(code) => code, Library(code) => code, Bad(code) => code, } } } impl From for CloseCode { fn from(code: u16) -> CloseCode { match code { 1000 => Normal, 1001 => Away, 1002 => Protocol, 1003 => Unsupported, 1005 => Status, 1006 => Abnormal, 1007 => Invalid, 1008 => Policy, 1009 => Size, 1010 => Extension, 1011 => Error, 1012 => Restart, 1013 => Again, 1015 => Tls, 1...999 => Bad(code), 1000...2999 => Reserved(code), 3000...3999 => Iana(code), 4000...4999 => Library(code), _ => Bad(code) } } } #[cfg(test)] mod tests { use super::*; #[test] fn opcode_from_u8() { let byte = 2u8; assert_eq!(OpCode::from(byte), OpCode::Data(Data::Binary)); } #[test] fn opcode_into_u8() { let text = OpCode::Data(Data::Text); let byte: u8 = text.into(); assert_eq!(byte, 1u8); } #[test] fn closecode_from_u16() { let byte = 1008u16; assert_eq!(CloseCode::from(byte), CloseCode::Policy); } #[test] fn closecode_into_u16() { let text = CloseCode::Away; let byte: u16 = text.into(); assert_eq!(byte, 1001u16); } }