1#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum ProtocolError {
6 InvalidOpcode(u8),
8 ReservedBitsSet {
10 bits: u8,
12 },
13 MaskedFrameFromServer,
15 UnmaskedFrameFromClient,
17 PayloadTooLarge {
19 size: u64,
21 max: u64,
23 },
24 ControlFrameTooLarge {
26 size: u64,
28 },
29 FragmentedControlFrame,
31 InvalidCloseCode(u16),
33 InvalidUtf8InCloseReason,
35 CloseFrameTooShort,
37 ContinuationWithoutStart,
39 NewMessageDuringAssembly,
41 InvalidUtf8,
43 MessageTooLarge {
45 accumulated: usize,
47 max: usize,
49 },
50}
51
52impl std::fmt::Display for ProtocolError {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 Self::InvalidOpcode(op) => write!(f, "invalid opcode: 0x{op:X}"),
56 Self::ReservedBitsSet { bits } => {
57 write!(f, "reserved bits set: 0b{bits:03b}")
58 }
59 Self::MaskedFrameFromServer => write!(f, "server sent masked frame"),
60 Self::UnmaskedFrameFromClient => write!(f, "client sent unmasked frame"),
61 Self::PayloadTooLarge { size, max } => {
62 write!(f, "payload too large: {size} bytes (max {max})")
63 }
64 Self::ControlFrameTooLarge { size } => {
65 write!(f, "control frame too large: {size} bytes (max 125)")
66 }
67 Self::FragmentedControlFrame => write!(f, "fragmented control frame"),
68 Self::InvalidCloseCode(code) => write!(f, "invalid close code: {code}"),
69 Self::InvalidUtf8InCloseReason => write!(f, "invalid UTF-8 in close reason"),
70 Self::CloseFrameTooShort => {
71 write!(f, "close frame too short (1 byte, must be 0 or >= 2)")
72 }
73 Self::ContinuationWithoutStart => {
74 write!(f, "continuation frame without preceding start frame")
75 }
76 Self::NewMessageDuringAssembly => {
77 write!(f, "new data frame received during fragment assembly")
78 }
79 Self::InvalidUtf8 => write!(f, "text message contains invalid UTF-8"),
80 Self::MessageTooLarge { accumulated, max } => {
81 write!(
82 f,
83 "assembled message too large: {accumulated} bytes (max {max})"
84 )
85 }
86 }
87 }
88}
89
90impl std::error::Error for ProtocolError {}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn display_invalid_opcode() {
98 let err = ProtocolError::InvalidOpcode(0x3);
99 assert_eq!(err.to_string(), "invalid opcode: 0x3");
100 }
101
102 #[test]
103 fn display_reserved_bits_set() {
104 let err = ProtocolError::ReservedBitsSet { bits: 0b110 };
105 assert_eq!(err.to_string(), "reserved bits set: 0b110");
106 }
107
108 #[test]
109 fn display_masked_frame_from_server() {
110 assert_eq!(
111 ProtocolError::MaskedFrameFromServer.to_string(),
112 "server sent masked frame"
113 );
114 }
115
116 #[test]
117 fn display_unmasked_frame_from_client() {
118 assert_eq!(
119 ProtocolError::UnmaskedFrameFromClient.to_string(),
120 "client sent unmasked frame"
121 );
122 }
123
124 #[test]
125 fn display_payload_too_large() {
126 let err = ProtocolError::PayloadTooLarge {
127 size: 200,
128 max: 125,
129 };
130 assert_eq!(err.to_string(), "payload too large: 200 bytes (max 125)");
131 }
132
133 #[test]
134 fn display_control_frame_too_large() {
135 let err = ProtocolError::ControlFrameTooLarge { size: 130 };
136 assert_eq!(
137 err.to_string(),
138 "control frame too large: 130 bytes (max 125)"
139 );
140 }
141
142 #[test]
143 fn display_fragmented_control_frame() {
144 assert_eq!(
145 ProtocolError::FragmentedControlFrame.to_string(),
146 "fragmented control frame"
147 );
148 }
149
150 #[test]
151 fn display_invalid_close_code() {
152 let err = ProtocolError::InvalidCloseCode(999);
153 assert_eq!(err.to_string(), "invalid close code: 999");
154 }
155
156 #[test]
157 fn display_invalid_utf8_in_close_reason() {
158 assert_eq!(
159 ProtocolError::InvalidUtf8InCloseReason.to_string(),
160 "invalid UTF-8 in close reason"
161 );
162 }
163
164 #[test]
165 fn display_close_frame_too_short() {
166 assert_eq!(
167 ProtocolError::CloseFrameTooShort.to_string(),
168 "close frame too short (1 byte, must be 0 or >= 2)"
169 );
170 }
171
172 #[test]
173 fn display_continuation_without_start() {
174 assert_eq!(
175 ProtocolError::ContinuationWithoutStart.to_string(),
176 "continuation frame without preceding start frame"
177 );
178 }
179
180 #[test]
181 fn display_new_message_during_assembly() {
182 assert_eq!(
183 ProtocolError::NewMessageDuringAssembly.to_string(),
184 "new data frame received during fragment assembly"
185 );
186 }
187
188 #[test]
189 fn display_invalid_utf8() {
190 assert_eq!(
191 ProtocolError::InvalidUtf8.to_string(),
192 "text message contains invalid UTF-8"
193 );
194 }
195
196 #[test]
197 fn display_message_too_large() {
198 let err = ProtocolError::MessageTooLarge {
199 accumulated: 2000,
200 max: 1024,
201 };
202 assert_eq!(
203 err.to_string(),
204 "assembled message too large: 2000 bytes (max 1024)"
205 );
206 }
207
208 #[test]
209 fn protocol_error_eq() {
210 assert_eq!(
211 ProtocolError::InvalidOpcode(0x3),
212 ProtocolError::InvalidOpcode(0x3)
213 );
214 assert_ne!(
215 ProtocolError::InvalidOpcode(0x3),
216 ProtocolError::InvalidOpcode(0x4)
217 );
218 assert_ne!(
219 ProtocolError::MaskedFrameFromServer,
220 ProtocolError::UnmaskedFrameFromClient
221 );
222 }
223}