oxedyne/fe2o3/fe2o3_net/src/ws/status.rs
4.2 KiB, 14 runs
created by r1870400018:611, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | use oxedyne_fe2o3_core::{ |
| 2 | prelude::*, |
| 3 | }; |
| 4 | |
| 5 | use std::{ |
| 6 | convert::TryFrom, |
| 7 | }; |
| 8 | |
| 9 | |
| 10 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| 11 | pub enum WebSocketStatusCode { |
| 12 | NormalClosure, |
| 13 | GoingAway, |
| 14 | ProtocolError, |
| 15 | UnsupportedData, |
| 16 | NoStatusReceived, |
| 17 | AbnormalClosure, |
| 18 | InvalidFramePayloadData, |
| 19 | PolicyViolation, |
| 20 | MessageTooBig, |
| 21 | MandatoryExtension, |
| 22 | InternalServerError, |
| 23 | ServiceRestart, |
| 24 | TryAgainLater, |
| 25 | TlsHandshake, |
| 26 | // Application defined |
| 27 | Private(u16), // 3000-4999; RFC 6455 §7.4.2 leaves the meaning to the application |
| 28 | } |
| 29 | |
| 30 | impl TryFrom<u16> for WebSocketStatusCode { |
| 31 | type Error = Error<ErrTag>; |
| 32 | |
| 33 | fn try_from(value: u16) -> Outcome<Self> { |
| 34 | Ok(match value { |
| 35 | 1000 => WebSocketStatusCode::NormalClosure, |
| 36 | 1001 => WebSocketStatusCode::GoingAway, |
| 37 | 1002 => WebSocketStatusCode::ProtocolError, |
| 38 | 1003 => WebSocketStatusCode::UnsupportedData, |
| 39 | 1005 => WebSocketStatusCode::NoStatusReceived, |
| 40 | 1006 => WebSocketStatusCode::AbnormalClosure, |
| 41 | 1007 => WebSocketStatusCode::InvalidFramePayloadData, |
| 42 | 1008 => WebSocketStatusCode::PolicyViolation, |
| 43 | 1009 => WebSocketStatusCode::MessageTooBig, |
| 44 | 1010 => WebSocketStatusCode::MandatoryExtension, |
| 45 | 1011 => WebSocketStatusCode::InternalServerError, |
| 46 | 1012 => WebSocketStatusCode::ServiceRestart, |
| 47 | 1013 => WebSocketStatusCode::TryAgainLater, |
| 48 | 1015 => WebSocketStatusCode::TlsHandshake, |
| 49 | // An application's own code. A peer that closes with one is not |
| 50 | // speaking a code this crate has to understand, and refusing to |
| 51 | // decode the frame would turn the peer's goodbye into a read error |
| 52 | // for a caller whose whole protocol lives in this range. |
| 53 | 3000..=4999 => WebSocketStatusCode::Private(value), |
| 54 | _ => return Err(err!( |
| 55 | "Unrecognised websocket status code {}.", value; |
| 56 | Conversion, Integer)), |
| 57 | }) |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | impl From<WebSocketStatusCode> for u16 { |
| 62 | fn from(status_code: WebSocketStatusCode) -> Self { |
| 63 | match status_code { |
| 64 | WebSocketStatusCode::NormalClosure => 1000, |
| 65 | WebSocketStatusCode::GoingAway => 1001, |
| 66 | WebSocketStatusCode::ProtocolError => 1002, |
| 67 | WebSocketStatusCode::UnsupportedData => 1003, |
| 68 | WebSocketStatusCode::NoStatusReceived => 1005, |
| 69 | WebSocketStatusCode::AbnormalClosure => 1006, |
| 70 | WebSocketStatusCode::InvalidFramePayloadData => 1007, |
| 71 | WebSocketStatusCode::PolicyViolation => 1008, |
| 72 | WebSocketStatusCode::MessageTooBig => 1009, |
| 73 | WebSocketStatusCode::MandatoryExtension => 1010, |
| 74 | WebSocketStatusCode::InternalServerError => 1011, |
| 75 | WebSocketStatusCode::ServiceRestart => 1012, |
| 76 | WebSocketStatusCode::TryAgainLater => 1013, |
| 77 | WebSocketStatusCode::TlsHandshake => 1015, |
| 78 | WebSocketStatusCode::Private(code) => code, |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | impl WebSocketStatusCode { |
| 84 | pub fn to_bytes(&self) -> [u8; 2] { |
| 85 | u16::from(*self).to_be_bytes() |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | |
| 90 | #[cfg(test)] |
| 91 | mod tests { |
| 92 | use super::*; |
| 93 | |
| 94 | #[test] |
| 95 | fn test_an_application_code_survives_the_round_trip() -> Outcome<()> { |
| 96 | for code in [3000u16, 4401, 4402, 4403, 4429, 4999] { |
| 97 | let parsed = res!(WebSocketStatusCode::try_from(code)); |
| 98 | assert_eq!(parsed, WebSocketStatusCode::Private(code)); |
| 99 | assert_eq!(u16::from(parsed), code); |
| 100 | assert_eq!(parsed.to_bytes(), code.to_be_bytes()); |
| 101 | } |
| 102 | Ok(()) |
| 103 | } |
| 104 | |
| 105 | #[test] |
| 106 | fn test_a_code_outside_the_application_range_is_still_refused() { |
| 107 | // The gap below the application range, and the space above it. Widening |
| 108 | // the range to "anything" would let a peer's typo through as a meaning. |
| 109 | for code in [0u16, 999, 1004, 1014, 2999, 5000, 65_535] { |
| 110 | assert!(WebSocketStatusCode::try_from(code).is_err(), |
| 111 | "close code {} was accepted", code); |
| 112 | } |
| 113 | } |
| 114 | } |