1use std::io;
4
5mod close;
6pub use close::WebSocketPeerClosed;
7use close::{parse_close_payload, protocol_error};
8
9use moirai_async::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
10use moirai_async::timer::timeout;
11
12use crate::upgrade::WebSocketConfig;
13
14const FIN: u8 = 0x80;
15const RSV_MASK: u8 = 0x70;
16const OPCODE_MASK: u8 = 0x0f;
17const MASK: u8 = 0x80;
18const CLOSE: u8 = 0x8;
19const PING: u8 = 0x9;
20const PONG: u8 = 0xa;
21const BINARY: u8 = 0x2;
22
23pub struct WebSocketStream<S> {
25 stream: S,
26 config: WebSocketConfig,
27 prefix: Vec<u8>,
28 prefix_position: usize,
29 closed: bool,
30}
31
32impl<S> WebSocketStream<S> {
33 pub(crate) fn new(stream: S, config: WebSocketConfig, prefix: Vec<u8>) -> Self {
34 Self {
35 stream,
36 config,
37 prefix,
38 prefix_position: 0,
39 closed: false,
40 }
41 }
42}
43
44impl<S: AsyncRead + AsyncWrite + Unpin> WebSocketStream<S> {
45 pub async fn recv_message(&mut self) -> io::Result<Vec<u8>> {
56 if self.closed {
57 return Err(closed_error());
58 }
59 let timeout_duration = self.config.frame_timeout;
60 match timeout(timeout_duration, self.recv_message_inner()).await {
61 Ok(Ok(message)) => Ok(message),
62 Ok(Err(error)) => {
63 self.closed = true;
64 Err(error)
65 }
66 Err(_) => {
67 self.closed = true;
68 Err(timed_out("WebSocket frame receive"))
69 }
70 }
71 }
72
73 pub async fn send_binary(&mut self, payload: &[u8]) -> io::Result<()> {
81 if self.closed {
82 return Err(closed_error());
83 }
84 if payload.len() > self.config.max_message_bytes {
85 return Err(io::Error::new(
86 io::ErrorKind::InvalidData,
87 "WebSocket message exceeds configured byte bound",
88 ));
89 }
90 let timeout_duration = self.config.frame_timeout;
91 match timeout(timeout_duration, self.send_frame(BINARY, payload)).await {
92 Ok(Ok(())) => Ok(()),
93 Ok(Err(error)) => {
94 self.closed = true;
95 Err(error)
96 }
97 Err(_) => {
98 self.closed = true;
99 Err(timed_out("WebSocket binary send"))
100 }
101 }
102 }
103
104 pub async fn close(&mut self, code: u16, reason: &[u8]) -> io::Result<()> {
109 if self.closed {
110 return Ok(());
111 }
112 if reason.len() > 123 || std::str::from_utf8(reason).is_err() {
113 return Err(io::Error::new(
114 io::ErrorKind::InvalidInput,
115 "WebSocket close reason must be valid UTF-8 and at most 123 bytes",
116 ));
117 }
118 let capacity = reason.len().checked_add(2).ok_or_else(|| {
119 io::Error::new(io::ErrorKind::InvalidInput, "close reason is too large")
120 })?;
121 let mut payload = Vec::with_capacity(capacity);
122 payload.extend_from_slice(&code.to_be_bytes());
123 payload.extend_from_slice(reason);
124 parse_close_payload(&payload)?;
125 let timeout_duration = self.config.frame_timeout;
126 let result = match timeout(timeout_duration, self.send_frame(CLOSE, &payload)).await {
127 Ok(result) => result,
128 Err(_) => Err(timed_out("WebSocket close send")),
129 };
130 self.closed = true;
131 result
132 }
133
134 async fn recv_message_inner(&mut self) -> io::Result<Vec<u8>> {
135 loop {
136 let mut header = [0u8; 2];
137 self.read_exact(&mut header).await?;
138 let first = header[0];
139 let second = header[1];
140 if first & RSV_MASK != 0 {
141 return Err(protocol_error("WebSocket reserved bits are not supported"));
142 }
143 let opcode = first & OPCODE_MASK;
144 let final_frame = first & FIN != 0;
145 let masked = second & MASK != 0;
146 let length_code = second & 0x7f;
147 let payload_length = self.read_length(length_code).await?;
148 if (length_code == 126 && payload_length < 126)
149 || (length_code == 127 && payload_length <= 65_535)
150 {
151 return Err(protocol_error(
152 "WebSocket payload length is not minimally encoded",
153 ));
154 }
155 let control = opcode >= CLOSE;
156 if control {
157 if !final_frame || payload_length > 125 {
158 return Err(protocol_error("WebSocket control frame is invalid"));
159 }
160 } else if !final_frame {
161 return Err(protocol_error(
162 "Fragmented WebSocket messages are not supported",
163 ));
164 }
165 if (!control && opcode != BINARY) || (control && !matches!(opcode, CLOSE | PING | PONG))
166 {
167 return Err(protocol_error(
168 "WebSocket frame opcode is not a supported message or control frame",
169 ));
170 }
171 if !masked {
172 return Err(protocol_error("Client WebSocket frames must be masked"));
173 }
174 let mask = self.read_mask().await?;
175 if opcode == BINARY {
176 if payload_length > self.config.max_message_bytes {
177 return Err(io::Error::new(
178 io::ErrorKind::InvalidData,
179 "WebSocket message exceeds configured byte bound",
180 ));
181 }
182 return self.read_payload(payload_length, mask).await;
183 }
184 if opcode == PING {
185 let payload = self.read_payload(payload_length, mask).await?;
186 self.send_frame(PONG, &payload).await?;
187 continue;
188 }
189 if opcode == PONG {
190 let _ = self.read_payload(payload_length, mask).await?;
191 continue;
192 }
193 if opcode == CLOSE {
194 let payload = self.read_payload(payload_length, mask).await?;
195 let peer_close = WebSocketPeerClosed::from(parse_close_payload(&payload)?);
196 self.closed = true;
197 self.send_frame(CLOSE, &payload).await?;
198 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, peer_close));
199 }
200 return Err(protocol_error("WebSocket frame opcode is not supported"));
201 }
202 }
203
204 async fn read_length(&mut self, length_code: u8) -> io::Result<usize> {
205 match length_code {
206 0..=125 => Ok(usize::from(length_code)),
207 126 => {
208 let mut bytes = [0u8; 2];
209 self.read_exact(&mut bytes).await?;
210 Ok(usize::from(u16::from_be_bytes(bytes)))
211 }
212 127 => {
213 let mut bytes = [0u8; 8];
214 self.read_exact(&mut bytes).await?;
215 let length = u64::from_be_bytes(bytes);
216 if length & (1u64 << 63) != 0 {
217 return Err(protocol_error(
218 "WebSocket payload length has its high bit set",
219 ));
220 }
221 usize::try_from(length).map_err(|_| {
222 io::Error::new(
223 io::ErrorKind::InvalidData,
224 "WebSocket payload length cannot be represented",
225 )
226 })
227 }
228 _ => Err(protocol_error("WebSocket payload length code is invalid")),
229 }
230 }
231
232 async fn read_mask(&mut self) -> io::Result<[u8; 4]> {
233 let mut mask = [0u8; 4];
234 self.read_exact(&mut mask).await?;
235 Ok(mask)
236 }
237
238 async fn read_payload(&mut self, length: usize, mask: [u8; 4]) -> io::Result<Vec<u8>> {
239 let mut payload = vec![0u8; length];
240 self.read_exact(&mut payload).await?;
241 for (byte, mask_byte) in payload.iter_mut().zip(mask.iter().cycle()) {
242 *byte ^= *mask_byte;
243 }
244 Ok(payload)
245 }
246
247 async fn read_exact(&mut self, output: &mut [u8]) -> io::Result<()> {
248 let prefix_available = self.prefix.len().saturating_sub(self.prefix_position);
249 let from_prefix = output.len().min(prefix_available);
250 if from_prefix != 0 {
251 let start = self.prefix_position;
252 let end = start.checked_add(from_prefix).ok_or_else(|| {
253 io::Error::other("WebSocket prefix position arithmetic overflowed")
254 })?;
255 let source = self
256 .prefix
257 .get(start..end)
258 .ok_or_else(|| io::Error::other("WebSocket prefix bounds are inconsistent"))?;
259 let destination = output
260 .get_mut(..from_prefix)
261 .ok_or_else(|| io::Error::other("WebSocket output bounds are inconsistent"))?;
262 destination.copy_from_slice(source);
263 self.prefix_position = end;
264 }
265 let mut filled = from_prefix;
266 while filled < output.len() {
267 let destination = output
268 .get_mut(filled..)
269 .ok_or_else(|| io::Error::other("WebSocket output bounds are inconsistent"))?;
270 let count = self.stream.read(destination).await?;
271 if count == 0 {
272 return Err(io::Error::new(
273 io::ErrorKind::UnexpectedEof,
274 "connection closed inside a WebSocket frame",
275 ));
276 }
277 filled = filled
278 .checked_add(count)
279 .ok_or_else(|| io::Error::other("WebSocket read length overflow"))?;
280 }
281 Ok(())
282 }
283
284 async fn send_frame(&mut self, opcode: u8, payload: &[u8]) -> io::Result<()> {
285 let capacity = payload
286 .len()
287 .checked_add(10)
288 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "message is too large"))?;
289 let mut frame = Vec::with_capacity(capacity);
290 frame.push(FIN | opcode);
291 match payload.len() {
292 length @ 0..=125 => frame.push(u8::try_from(length).expect("invariant: length <= 125")),
293 length @ 126..=65_535 => {
294 frame.push(126);
295 let length =
296 u16::try_from(length).expect("invariant: extended WebSocket length fits u16");
297 frame.extend_from_slice(&length.to_be_bytes());
298 }
299 length => {
300 frame.push(127);
301 let length = u64::try_from(length).map_err(|_| {
302 io::Error::new(io::ErrorKind::InvalidInput, "message is too large")
303 })?;
304 frame.extend_from_slice(&length.to_be_bytes());
305 }
306 }
307 frame.extend_from_slice(payload);
308 self.stream.write_all(&frame).await?;
309 self.stream.flush().await
310 }
311
312 #[cfg(test)]
313 pub(crate) fn initial_bytes(&self) -> &[u8] {
314 self.prefix
315 .get(self.prefix_position..)
316 .expect("invariant: WebSocket prefix position stays within the prefix")
317 }
318
319 #[cfg(test)]
320 pub(crate) fn output_bytes(&self) -> &[u8]
321 where
322 S: OutputBytes,
323 {
324 self.stream.output_bytes()
325 }
326}
327
328fn timed_out(operation: &str) -> io::Error {
329 io::Error::new(io::ErrorKind::TimedOut, operation)
330}
331
332fn closed_error() -> io::Error {
333 io::Error::new(io::ErrorKind::BrokenPipe, "WebSocket is closed")
334}
335
336#[cfg(test)]
337pub(crate) trait OutputBytes {
338 fn output_bytes(&self) -> &[u8];
339}
340
341#[cfg(test)]
342#[path = "websocket_tests.rs"]
343mod tests;