1use std::collections::HashMap;
17use std::hash::{BuildHasher, Hasher};
18use std::io::{Read, Write};
19use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
20use std::sync::{Arc, Mutex, OnceLock};
21use std::time::{Duration, Instant};
22
23use super::{close_payload, encode_frame, read_raw_frame, Frame, RawFrame, WebSocket};
24use crate::core::New;
25use crate::header::Header;
26use crate::request::Request;
27use crate::response::{Response, STATUS_CODE_REASON_PHRASE};
28
29const UPGRADE_HEADER: &str = "X-Rws-WebSocket-Upgrade";
31pub const UPGRADE_TTL: Duration = Duration::from_secs(60);
33pub const MAX_MESSAGE: usize = 16 * 1024 * 1024;
35
36type Handler = Box<dyn FnOnce(Box<dyn Read + Send>, Box<dyn Write + Send>) + Send>;
38
39#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum Message {
43 Text(String),
44 Binary(Vec<u8>),
45}
46
47pub struct WebSocketConnection {
52 reader: Box<dyn Read + Send>,
53 sender: WebSocketSender,
54 max_message: usize,
55 closed: bool,
56}
57
58#[derive(Clone)]
60pub struct WebSocketSender {
61 writer: Arc<Mutex<Box<dyn Write + Send>>>,
62 closed: Arc<AtomicBool>,
63}
64
65impl WebSocketSender {
66 fn send(&self, frame: Frame) -> Result<(), String> {
67 let closing = matches!(frame, Frame::Close(..));
68 if self.closed.load(Ordering::Acquire) {
69 return Err("the WebSocket is closed".to_string());
70 }
71 if closing {
72 self.closed.store(true, Ordering::Release);
73 }
74 let bytes = encode_frame(frame);
75 let mut writer = self.writer.lock().unwrap_or_else(|e| e.into_inner());
76 writer.write_all(&bytes).and_then(|_| writer.flush()).map_err(|e| format!("write error: {e}"))
77 }
78
79 pub fn send_text(&self, text: &str) -> Result<(), String> {
81 self.send(Frame::Text(text.to_string()))
82 }
83
84 pub fn send_binary(&self, data: &[u8]) -> Result<(), String> {
86 self.send(Frame::Binary(data.to_vec()))
87 }
88
89 pub fn ping(&self, payload: &[u8]) -> Result<(), String> {
92 if payload.len() > 125 {
93 return Err("a ping payload is at most 125 bytes".to_string());
94 }
95 self.send(Frame::Ping(payload.to_vec()))
96 }
97
98 pub fn close(&self, code: u16, reason: &str) -> Result<(), String> {
101 self.send(Frame::Close(Some(code), reason.to_string()))
102 }
103
104 pub fn is_closed(&self) -> bool {
106 self.closed.load(Ordering::Acquire)
107 }
108}
109
110fn valid_close_code(code: u16) -> bool {
112 matches!(code, 1000..=1003 | 1007..=1011 | 3000..=4999)
113}
114
115impl WebSocketConnection {
116 #[doc(hidden)]
118 pub fn new(reader: Box<dyn Read + Send>, writer: Box<dyn Write + Send>) -> Self {
119 WebSocketConnection {
120 reader,
121 sender: WebSocketSender { writer: Arc::new(Mutex::new(writer)), closed: Arc::new(AtomicBool::new(false)) },
122 max_message: MAX_MESSAGE,
123 closed: false,
124 }
125 }
126
127 pub fn max_message_size(mut self, bytes: usize) -> Self {
130 self.max_message = bytes;
131 self
132 }
133
134 pub fn sender(&self) -> WebSocketSender {
136 self.sender.clone()
137 }
138
139 pub fn send_text(&self, text: &str) -> Result<(), String> {
141 self.sender.send_text(text)
142 }
143
144 pub fn send_binary(&self, data: &[u8]) -> Result<(), String> {
146 self.sender.send_binary(data)
147 }
148
149 pub fn close(&self, code: u16, reason: &str) -> Result<(), String> {
152 self.sender.close(code, reason)
153 }
154
155 fn fail<T>(&mut self, code: u16, reason: String) -> Result<T, String> {
157 self.closed = true;
158 let _ = self.sender.close(code, "");
159 Err(reason)
160 }
161
162 pub fn recv(&mut self) -> Result<Option<Message>, String> {
169 if self.closed {
170 return Ok(None);
171 }
172 let mut message: Option<(u8, Vec<u8>)> = None;
173 loop {
174 let assembled = message.as_ref().map_or(0, |(_, data)| data.len());
175 let limit = self.max_message.saturating_sub(assembled) as u64;
176 let RawFrame { fin, rsv, opcode, masked, payload } = match read_raw_frame(&mut self.reader, limit) {
177 Ok(frame) => frame,
178 Err(e) if e.contains("exceeds") => return self.fail(1009, format!("message over {} bytes", self.max_message)),
179 Err(e) => {
180 self.closed = true;
181 return Err(e);
182 }
183 };
184 if rsv != 0 {
185 return self.fail(1002, "reserved bits set (no extension was negotiated)".to_string());
186 }
187 if !masked {
188 return self.fail(1002, "a client frame wasn't masked".to_string());
189 }
190 if opcode >= 0x8 && (!fin || payload.len() > 125) {
191 return self.fail(1002, "a control frame was fragmented or over 125 bytes".to_string());
192 }
193 match opcode {
194 0x8 => {
195 let (code, _) = match close_payload(&payload) {
196 Ok(close) => close,
197 Err(e) => return self.fail(1007, e),
198 };
199 if payload.len() == 1 || code.is_some_and(|c| !valid_close_code(c)) {
200 return self.fail(1002, format!("invalid close code {code:?}"));
201 }
202 self.closed = true;
203 if !self.sender.is_closed() {
205 let _ = self.sender.close(code.unwrap_or(1000), "");
206 }
207 return Ok(None);
208 }
209 0x9 => {
210 let _ = self.sender.send(Frame::Pong(payload));
211 }
212 0xA => {}
213 0x1 | 0x2 if message.is_some() => return self.fail(1002, "a new message began inside a fragmented one".to_string()),
214 0x1 | 0x2 => message = Some((opcode, payload)),
215 0x0 => match message.as_mut() {
216 Some((_, data)) => data.extend_from_slice(&payload),
217 None => return self.fail(1002, "a continuation frame with no message".to_string()),
218 },
219 other => return self.fail(1002, format!("unknown opcode 0x{other:X}")),
220 }
221 if fin && opcode < 0x8 {
222 let Some((kind, data)) = message.take() else { continue };
223 return match kind {
224 0x1 => match String::from_utf8(data) {
225 Ok(text) => Ok(Some(Message::Text(text))),
226 Err(_) => self.fail(1007, "a text message isn't UTF-8".to_string()),
227 },
228 _ => Ok(Some(Message::Binary(data))),
229 };
230 }
231 }
232 }
233}
234
235#[doc(hidden)]
239pub struct Upgrade(Handler);
240
241impl Upgrade {
242 #[doc(hidden)]
245 pub fn run(self, reader: Box<dyn Read + Send>, writer: Box<dyn Write + Send>) {
246 (self.0)(reader, writer)
247 }
248}
249
250fn waiting() -> &'static Mutex<HashMap<String, (Instant, Handler)>> {
251 static WAITING: OnceLock<Mutex<HashMap<String, (Instant, Handler)>>> = OnceLock::new();
252 WAITING.get_or_init(Default::default)
253}
254
255fn token() -> String {
258 static KEYS: OnceLock<std::collections::hash_map::RandomState> = OnceLock::new();
259 static COUNTER: AtomicU64 = AtomicU64::new(0);
260 let keys = KEYS.get_or_init(Default::default);
261 let n = COUNTER.fetch_add(1, Ordering::Relaxed);
262 let nanos = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map_or(0, |d| d.as_nanos());
263 let half = |salt: u8| {
264 let mut hasher = keys.build_hasher();
265 hasher.write_u8(salt);
266 hasher.write_u64(n);
267 hasher.write_u128(nanos);
268 hasher.finish()
269 };
270 format!("{:016x}{:016x}", half(1), half(2))
271}
272
273impl WebSocket {
274 pub fn upgrade<F>(request: &Request, handler: F) -> Response
296 where
297 F: FnOnce(WebSocketConnection) + Send + 'static,
298 {
299 let version = request.header("Sec-WebSocket-Version").map(|h| h.value.trim().to_string()).unwrap_or_default();
300 let mut response = match WebSocket::handshake_response(request) {
301 Ok(response) if Self::is_upgrade_request(request) && version == "13" && request.method == "GET" => response,
302 _ => {
303 let mut refused = Response::new();
304 refused.status_code = *STATUS_CODE_REASON_PHRASE.n426_upgrade_required.status_code;
305 refused.reason_phrase = STATUS_CODE_REASON_PHRASE.n426_upgrade_required.reason_phrase.to_string();
306 refused.headers.push(Header { name: "Upgrade".into(), value: "websocket".into() });
307 refused.headers.push(Header { name: "Sec-WebSocket-Version".into(), value: "13".into() });
308 return refused;
309 }
310 };
311 Self::register(&mut response, Box::new(move |reader, writer| handler(WebSocketConnection::new(reader, writer))));
312 response
313 }
314
315 fn register(response: &mut Response, handler: Handler) {
318 let token = token();
319 let mut waiting = waiting().lock().unwrap_or_else(|e| e.into_inner());
320 let now = Instant::now();
321 waiting.retain(|_, (since, _)| now.duration_since(*since) < UPGRADE_TTL);
322 waiting.insert(token.clone(), (now, handler));
323 response.headers.push(Header { name: UPGRADE_HEADER.into(), value: token.into() });
324 }
325
326 #[doc(hidden)]
329 pub fn upgrade_raw<F>(mut response: Response, relay: F) -> Response
330 where
331 F: FnOnce(Box<dyn Read + Send>, Box<dyn Write + Send>) + Send + 'static,
332 {
333 Self::register(&mut response, Box::new(relay));
334 response
335 }
336}
337
338#[doc(hidden)]
341pub fn take_upgrade(response: &mut Response) -> Option<Upgrade> {
342 let at = response.headers.iter().position(|h| h.name.eq_ignore_ascii_case(UPGRADE_HEADER))?;
343 let token = response.headers.remove(at).value;
344 if response.status_code != 101 {
345 return None;
346 }
347 let (_, handler) = waiting().lock().unwrap_or_else(|e| e.into_inner()).remove(token.as_ref())?;
348 Some(Upgrade(handler))
349}
350
351#[doc(hidden)]
354pub fn upgrade_head(response: &Response) -> Vec<u8> {
355 let mut head = format!("HTTP/1.1 101 {}\r\n", response.reason_phrase);
356 for header in &response.headers {
357 if header.name.eq_ignore_ascii_case("content-length") || header.name.eq_ignore_ascii_case("transfer-encoding") {
358 continue;
359 }
360 head.push_str(&header.name);
361 head.push_str(": ");
362 head.push_str(&header.value);
363 head.push_str("\r\n");
364 }
365 head.push_str("\r\n");
366 head.into_bytes()
367}
368
369#[cfg(feature = "http2")]
372struct Blocking<T> {
373 inner: T,
374 handle: tokio::runtime::Handle,
375}
376
377#[cfg(feature = "http2")]
378impl<T: tokio::io::AsyncRead + Unpin> Read for Blocking<T> {
379 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
380 self.handle.block_on(tokio::io::AsyncReadExt::read(&mut self.inner, buf))
381 }
382}
383
384#[cfg(feature = "http2")]
385impl<T: tokio::io::AsyncWrite + Unpin> Write for Blocking<T> {
386 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
387 self.handle.block_on(tokio::io::AsyncWriteExt::write(&mut self.inner, buf))
388 }
389
390 fn flush(&mut self) -> std::io::Result<()> {
391 self.handle.block_on(tokio::io::AsyncWriteExt::flush(&mut self.inner))
392 }
393}
394
395#[cfg(feature = "http2")]
398#[doc(hidden)]
399pub fn hand_over_async<S>(stream: S, pending: Vec<u8>, upgrade: Upgrade)
400where
401 S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send + 'static,
402{
403 let handle = tokio::runtime::Handle::current();
404 let (read, write) = tokio::io::split(stream);
405 let reader = std::io::Cursor::new(pending).chain(Blocking { inner: read, handle: handle.clone() });
406 let writer = Blocking { inner: write, handle };
407 tokio::task::spawn_blocking(move || upgrade.run(Box::new(reader), Box::new(writer)));
408}