1 //! Various codes defined in RFC 6455. 2 3 use std::{ 4 convert::{From, Into}, 5 fmt, 6 }; 7 8 /// WebSocket message opcode as in RFC 6455. 9 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 10 pub enum OpCode { 11 /// Data (text or binary). 12 Data(Data), 13 /// Control message (close, ping, pong). 14 Control(Control), 15 } 16 17 /// Data opcodes as in RFC 6455 18 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 19 pub enum Data { 20 /// 0x0 denotes a continuation frame 21 Continue, 22 /// 0x1 denotes a text frame 23 Text, 24 /// 0x2 denotes a binary frame 25 Binary, 26 /// 0x3-7 are reserved for further non-control frames 27 Reserved(u8), 28 } 29 30 /// Control opcodes as in RFC 6455 31 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 32 pub enum Control { 33 /// 0x8 denotes a connection close 34 Close, 35 /// 0x9 denotes a ping 36 Ping, 37 /// 0xa denotes a pong 38 Pong, 39 /// 0xb-f are reserved for further control frames 40 Reserved(u8), 41 } 42 43 impl fmt::Display for Data { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 match *self { 46 Data::Continue => write!(f, "CONTINUE"), 47 Data::Text => write!(f, "TEXT"), 48 Data::Binary => write!(f, "BINARY"), 49 Data::Reserved(x) => write!(f, "RESERVED_DATA_{}", x), 50 } 51 } 52 } 53 54 impl fmt::Display for Control { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 match *self { 57 Control::Close => write!(f, "CLOSE"), 58 Control::Ping => write!(f, "PING"), 59 Control::Pong => write!(f, "PONG"), 60 Control::Reserved(x) => write!(f, "RESERVED_CONTROL_{}", x), 61 } 62 } 63 } 64 65 impl fmt::Display for OpCode { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 67 match *self { 68 OpCode::Data(d) => d.fmt(f), 69 OpCode::Control(c) => c.fmt(f), 70 } 71 } 72 } 73 74 impl From<OpCode> for u8 { from(code: OpCode) -> Self75 fn from(code: OpCode) -> Self { 76 use self::{ 77 Control::{Close, Ping, Pong}, 78 Data::{Binary, Continue, Text}, 79 OpCode::*, 80 }; 81 match code { 82 Data(Continue) => 0, 83 Data(Text) => 1, 84 Data(Binary) => 2, 85 Data(self::Data::Reserved(i)) => i, 86 87 Control(Close) => 8, 88 Control(Ping) => 9, 89 Control(Pong) => 10, 90 Control(self::Control::Reserved(i)) => i, 91 } 92 } 93 } 94 95 impl From<u8> for OpCode { from(byte: u8) -> OpCode96 fn from(byte: u8) -> OpCode { 97 use self::{ 98 Control::{Close, Ping, Pong}, 99 Data::{Binary, Continue, Text}, 100 OpCode::*, 101 }; 102 match byte { 103 0 => Data(Continue), 104 1 => Data(Text), 105 2 => Data(Binary), 106 i @ 3..=7 => Data(self::Data::Reserved(i)), 107 8 => Control(Close), 108 9 => Control(Ping), 109 10 => Control(Pong), 110 i @ 11..=15 => Control(self::Control::Reserved(i)), 111 _ => panic!("Bug: OpCode out of range"), 112 } 113 } 114 } 115 116 use self::CloseCode::*; 117 /// Status code used to indicate why an endpoint is closing the WebSocket connection. 118 #[derive(Debug, Eq, PartialEq, Clone, Copy)] 119 pub enum CloseCode { 120 /// Indicates a normal closure, meaning that the purpose for 121 /// which the connection was established has been fulfilled. 122 Normal, 123 /// Indicates that an endpoint is "going away", such as a server 124 /// going down or a browser having navigated away from a page. 125 Away, 126 /// Indicates that an endpoint is terminating the connection due 127 /// to a protocol error. 128 Protocol, 129 /// Indicates that an endpoint is terminating the connection 130 /// because it has received a type of data it cannot accept (e.g., an 131 /// endpoint that understands only text data MAY send this if it 132 /// receives a binary message). 133 Unsupported, 134 /// Indicates that no status code was included in a closing frame. This 135 /// close code makes it possible to use a single method, `on_close` to 136 /// handle even cases where no close code was provided. 137 Status, 138 /// Indicates an abnormal closure. If the abnormal closure was due to an 139 /// error, this close code will not be used. Instead, the `on_error` method 140 /// of the handler will be called with the error. However, if the connection 141 /// is simply dropped, without an error, this close code will be sent to the 142 /// handler. 143 Abnormal, 144 /// Indicates that an endpoint is terminating the connection 145 /// because it has received data within a message that was not 146 /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\] 147 /// data within a text message). 148 Invalid, 149 /// Indicates that an endpoint is terminating the connection 150 /// because it has received a message that violates its policy. This 151 /// is a generic status code that can be returned when there is no 152 /// other more suitable status code (e.g., Unsupported or Size) or if there 153 /// is a need to hide specific details about the policy. 154 Policy, 155 /// Indicates that an endpoint is terminating the connection 156 /// because it has received a message that is too big for it to 157 /// process. 158 Size, 159 /// Indicates that an endpoint (client) is terminating the 160 /// connection because it has expected the server to negotiate one or 161 /// more extension, but the server didn't return them in the response 162 /// message of the WebSocket handshake. The list of extensions that 163 /// are needed should be given as the reason for closing. 164 /// Note that this status code is not used by the server, because it 165 /// can fail the WebSocket handshake instead. 166 Extension, 167 /// Indicates that a server is terminating the connection because 168 /// it encountered an unexpected condition that prevented it from 169 /// fulfilling the request. 170 Error, 171 /// Indicates that the server is restarting. A client may choose to reconnect, 172 /// and if it does, it should use a randomized delay of 5-30 seconds between attempts. 173 Restart, 174 /// Indicates that the server is overloaded and the client should either connect 175 /// to a different IP (when multiple targets exist), or reconnect to the same IP 176 /// when a user has performed an action. 177 Again, 178 #[doc(hidden)] 179 Tls, 180 #[doc(hidden)] 181 Reserved(u16), 182 #[doc(hidden)] 183 Iana(u16), 184 #[doc(hidden)] 185 Library(u16), 186 #[doc(hidden)] 187 Bad(u16), 188 } 189 190 impl CloseCode { 191 /// Check if this CloseCode is allowed. is_allowed(self) -> bool192 pub fn is_allowed(self) -> bool { 193 !matches!(self, Bad(_) | Reserved(_) | Status | Abnormal | Tls) 194 } 195 } 196 197 impl fmt::Display for CloseCode { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 199 let code: u16 = self.into(); 200 write!(f, "{}", code) 201 } 202 } 203 204 impl From<CloseCode> for u16 { from(code: CloseCode) -> u16205 fn from(code: CloseCode) -> u16 { 206 match code { 207 Normal => 1000, 208 Away => 1001, 209 Protocol => 1002, 210 Unsupported => 1003, 211 Status => 1005, 212 Abnormal => 1006, 213 Invalid => 1007, 214 Policy => 1008, 215 Size => 1009, 216 Extension => 1010, 217 Error => 1011, 218 Restart => 1012, 219 Again => 1013, 220 Tls => 1015, 221 Reserved(code) => code, 222 Iana(code) => code, 223 Library(code) => code, 224 Bad(code) => code, 225 } 226 } 227 } 228 229 impl<'t> From<&'t CloseCode> for u16 { from(code: &'t CloseCode) -> u16230 fn from(code: &'t CloseCode) -> u16 { 231 (*code).into() 232 } 233 } 234 235 impl From<u16> for CloseCode { from(code: u16) -> CloseCode236 fn from(code: u16) -> CloseCode { 237 match code { 238 1000 => Normal, 239 1001 => Away, 240 1002 => Protocol, 241 1003 => Unsupported, 242 1005 => Status, 243 1006 => Abnormal, 244 1007 => Invalid, 245 1008 => Policy, 246 1009 => Size, 247 1010 => Extension, 248 1011 => Error, 249 1012 => Restart, 250 1013 => Again, 251 1015 => Tls, 252 1..=999 => Bad(code), 253 1016..=2999 => Reserved(code), 254 3000..=3999 => Iana(code), 255 4000..=4999 => Library(code), 256 _ => Bad(code), 257 } 258 } 259 } 260 261 #[cfg(test)] 262 mod tests { 263 use super::*; 264 265 #[test] opcode_from_u8()266 fn opcode_from_u8() { 267 let byte = 2u8; 268 assert_eq!(OpCode::from(byte), OpCode::Data(Data::Binary)); 269 } 270 271 #[test] opcode_into_u8()272 fn opcode_into_u8() { 273 let text = OpCode::Data(Data::Text); 274 let byte: u8 = text.into(); 275 assert_eq!(byte, 1u8); 276 } 277 278 #[test] closecode_from_u16()279 fn closecode_from_u16() { 280 let byte = 1008u16; 281 assert_eq!(CloseCode::from(byte), CloseCode::Policy); 282 } 283 284 #[test] closecode_into_u16()285 fn closecode_into_u16() { 286 let text = CloseCode::Away; 287 let byte: u16 = text.into(); 288 assert_eq!(byte, 1001u16); 289 assert_eq!(u16::from(text), 1001u16); 290 } 291 } 292