1use crate::error::{WsError, WsResult};
16use crate::frame::{CloseCode, CloseFrame, Frame, OpCode, Role};
17use crate::message::Message;
18use rustlavel_core::{Config, Event, events};
19use rustlavel_http::Upgraded;
20use std::time::{Duration, Instant};
21use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
22use tokio::sync::mpsc;
23
24#[derive(Debug, Clone)]
26pub struct WebSocketConfig {
27 pub max_message_size: usize,
29 pub max_frame_size: usize,
32 pub idle_timeout: Option<Duration>,
35 pub close_timeout: Duration,
37 pub send_queue: usize,
40}
41
42impl Default for WebSocketConfig {
43 fn default() -> Self {
44 WebSocketConfig {
45 max_message_size: 1 << 20,
46 max_frame_size: 1 << 20,
47 idle_timeout: Some(Duration::from_secs(60)),
48 close_timeout: Duration::from_secs(5),
49 send_queue: 64,
50 }
51 }
52}
53
54impl WebSocketConfig {
55 pub fn from_config(config: &Config) -> Self {
60 let defaults = WebSocketConfig::default();
61 let idle = config.int("websocket.idle_timeout", 60).max(0) as u64;
62
63 WebSocketConfig {
64 max_message_size: config
65 .int("websocket.max_message_size", defaults.max_message_size as i64)
66 .max(1) as usize,
67 max_frame_size: config
68 .int("websocket.max_frame_size", defaults.max_frame_size as i64)
69 .max(1) as usize,
70 idle_timeout: (idle > 0).then(|| Duration::from_secs(idle)),
71 close_timeout: Duration::from_secs(
72 config.int("websocket.close_timeout", 5).max(1) as u64
73 ),
74 send_queue: config.int("websocket.send_queue", defaults.send_queue as i64).max(1)
75 as usize,
76 }
77 }
78}
79
80#[derive(Clone)]
86pub struct Sender {
87 queue: mpsc::Sender<Message>,
88}
89
90impl Sender {
91 pub async fn send(&self, message: impl Into<Message>) -> WsResult<()> {
94 self.queue.send(message.into()).await.map_err(|_| WsError::Closed)
95 }
96
97 pub fn try_send(&self, message: impl Into<Message>) -> WsResult<()> {
103 match self.queue.try_send(message.into()) {
104 Ok(()) => Ok(()),
105 Err(mpsc::error::TrySendError::Full(_)) => Err(WsError::Full),
106 Err(mpsc::error::TrySendError::Closed(_)) => Err(WsError::Closed),
107 }
108 }
109
110 pub fn is_closed(&self) -> bool {
111 self.queue.is_closed()
112 }
113
114 pub fn room(&self) -> usize {
116 self.queue.capacity()
117 }
118}
119
120pub fn channel(capacity: usize) -> (Sender, mpsc::Receiver<Message>) {
123 let (queue, receiver) = mpsc::channel(capacity.max(1));
124 (Sender { queue }, receiver)
125}
126
127pub struct WebSocket {
128 reader: Box<dyn AsyncRead + Send + Unpin>,
129 buffer: Vec<u8>,
132 outgoing: Sender,
133 config: WebSocketConfig,
134 partial: Option<(OpCode, Vec<u8>, usize)>,
143 awaiting_pong: bool,
144 closed: bool,
145 close_frame: Option<CloseFrame>,
146 path: String,
147 protocol: Option<String>,
148 opened_at: Instant,
149 received: u64,
150}
151
152impl WebSocket {
153 pub fn new(upgraded: Upgraded, config: WebSocketConfig) -> WebSocket {
155 let (queue, receiver) = mpsc::channel(config.send_queue.max(1));
156 tokio::spawn(write_loop(upgraded.writer, receiver));
157
158 WebSocket {
159 reader: upgraded.reader,
160 buffer: upgraded.buffered,
161 outgoing: Sender { queue },
162 config,
163 partial: None,
164 awaiting_pong: false,
165 closed: false,
166 close_frame: None,
167 path: String::new(),
168 protocol: None,
169 opened_at: Instant::now(),
170 received: 0,
171 }
172 }
173
174 pub fn on_path(mut self, path: impl Into<String>) -> Self {
176 self.path = path.into();
177 self
178 }
179
180 pub fn with_protocol(mut self, protocol: Option<String>) -> Self {
181 self.protocol = protocol;
182 self
183 }
184
185 pub fn sender(&self) -> Sender {
187 self.outgoing.clone()
188 }
189
190 pub fn path(&self) -> &str {
191 &self.path
192 }
193
194 pub fn protocol(&self) -> Option<&str> {
196 self.protocol.as_deref()
197 }
198
199 pub fn is_closed(&self) -> bool {
200 self.closed
201 }
202
203 pub fn close_frame(&self) -> Option<&CloseFrame> {
206 self.close_frame.as_ref()
207 }
208
209 pub fn received(&self) -> u64 {
211 self.received
212 }
213
214 pub async fn send(&mut self, message: impl Into<Message>) -> WsResult<()> {
215 if self.closed {
216 return Err(WsError::Closed);
217 }
218 self.outgoing.send(message).await
219 }
220
221 pub async fn recv(&mut self) -> Option<Message> {
227 loop {
228 if self.closed {
229 return None;
230 }
231
232 let frame = match self.next_frame().await {
233 Ok(Some(frame)) => frame,
234 Ok(None) => {
236 self.mark_closed(Some(CloseFrame::new(CloseCode::ABNORMAL, "")));
237 return None;
238 }
239 Err(error) => {
240 self.fail(error).await;
241 return None;
242 }
243 };
244
245 self.awaiting_pong = false;
247
248 match frame.opcode {
249 OpCode::Ping => {
250 if self.outgoing.send(Message::Pong(frame.payload)).await.is_err() {
253 self.mark_closed(Some(CloseFrame::new(CloseCode::ABNORMAL, "")));
254 return None;
255 }
256 }
257 OpCode::Pong => {}
258 OpCode::Close => {
259 let peer = match CloseFrame::parse(&frame.payload) {
260 Ok(close) => close,
261 Err(error) => {
262 self.fail(error).await;
263 return None;
264 }
265 };
266 let echo =
269 peer.clone().unwrap_or_else(|| CloseFrame::new(CloseCode::NORMAL, ""));
270 let _ = self.outgoing.send(Message::Close(Some(echo))).await;
271 self.mark_closed(
272 peer.or_else(|| Some(CloseFrame::new(CloseCode::NO_STATUS, ""))),
273 );
274 return None;
275 }
276 OpCode::Text | OpCode::Binary => {
277 if self.partial.is_some() {
278 self.fail(WsError::protocol(
279 "a new data frame arrived while a fragmented message was still open",
280 ))
281 .await;
282 return None;
283 }
284 if frame.fin {
285 return self.deliver(frame.opcode, frame.payload).await;
286 }
287 let mut checked = 0;
288 if frame.opcode == OpCode::Text
289 && !text_so_far_is_valid(&frame.payload, &mut checked)
290 {
291 self.fail(WsError::InvalidUtf8).await;
292 return None;
293 }
294 self.partial = Some((frame.opcode, frame.payload, checked));
295 }
296 OpCode::Continuation => {
297 let Some((opcode, mut assembled, mut checked)) = self.partial.take() else {
298 self.fail(WsError::protocol(
299 "a continuation frame arrived with no message to continue",
300 ))
301 .await;
302 return None;
303 };
304
305 let total = assembled.len() + frame.payload.len();
306 if total > self.config.max_message_size {
307 self.fail(WsError::TooLarge {
310 size: total,
311 limit: self.config.max_message_size,
312 })
313 .await;
314 return None;
315 }
316
317 assembled.extend_from_slice(&frame.payload);
318 if frame.fin {
319 return self.deliver(opcode, assembled).await;
323 }
324 if opcode == OpCode::Text && !text_so_far_is_valid(&assembled, &mut checked) {
329 self.fail(WsError::InvalidUtf8).await;
330 return None;
331 }
332 self.partial = Some((opcode, assembled, checked));
333 }
334 }
335 }
336 }
337
338 pub async fn close(&mut self, code: CloseCode, reason: impl Into<String>) {
341 if self.closed {
342 return;
343 }
344 let close = CloseFrame::new(code, reason);
345
346 if self.outgoing.send(Message::Close(Some(close.clone()))).await.is_err() {
347 self.mark_closed(Some(close));
348 return;
349 }
350
351 let peer = tokio::time::timeout(self.config.close_timeout, self.drain_until_close())
355 .await
356 .ok()
357 .flatten();
358 self.mark_closed(peer.or(Some(close)));
359 }
360
361 async fn deliver(&mut self, opcode: OpCode, payload: Vec<u8>) -> Option<Message> {
363 match Message::from_data(opcode, payload) {
364 Ok(message) => {
365 self.received += 1;
366 if events::has_subscribers() {
367 Event::new("ws.message")
370 .with("path", self.path.as_str())
371 .with("kind", message.kind())
372 .with("bytes", message.len())
373 .dispatch();
374 }
375 Some(message)
376 }
377 Err(error) => {
378 self.fail(error).await;
379 None
380 }
381 }
382 }
383
384 async fn fail(&mut self, error: WsError) {
386 let close = CloseFrame::new(error.close_code(), error.to_string());
387 let _ = self.outgoing.send(Message::Close(Some(close.clone()))).await;
388 self.mark_closed(Some(close));
389 }
390
391 fn mark_closed(&mut self, close: Option<CloseFrame>) {
392 self.closed = true;
393 if self.close_frame.is_none() {
394 self.close_frame = close;
395 }
396 }
397
398 async fn drain_until_close(&mut self) -> Option<CloseFrame> {
399 loop {
400 match self.read_frame().await {
401 Ok(Some(frame)) if frame.opcode == OpCode::Close => {
402 return CloseFrame::parse(&frame.payload).ok().flatten();
403 }
404 Ok(Some(_)) => continue,
405 Ok(None) | Err(_) => return None,
406 }
407 }
408 }
409
410 async fn next_frame(&mut self) -> WsResult<Option<Frame>> {
412 let Some(idle) = self.config.idle_timeout else {
413 return self.read_frame().await;
414 };
415
416 loop {
417 match tokio::time::timeout(idle, self.read_frame()).await {
420 Ok(result) => return result,
421 Err(_) if self.awaiting_pong => return Err(WsError::Idle),
422 Err(_) => {
423 self.awaiting_pong = true;
427 if self.outgoing.send(Message::Ping(Vec::new())).await.is_err() {
428 return Ok(None);
429 }
430 }
431 }
432 }
433 }
434
435 async fn read_frame(&mut self) -> WsResult<Option<Frame>> {
438 loop {
439 if let Some((frame, used)) =
440 Frame::decode(&self.buffer, Role::Server, self.config.max_frame_size)?
441 {
442 self.buffer.drain(..used);
443 return Ok(Some(frame));
444 }
445
446 let mut chunk = [0u8; 8192];
447 let read = self.reader.read(&mut chunk).await?;
448 if read == 0 {
449 return Ok(None);
450 }
451 self.buffer.extend_from_slice(&chunk[..read]);
452 }
453 }
454}
455
456impl Drop for WebSocket {
459 fn drop(&mut self) {
460 if !events::has_subscribers() {
461 return;
462 }
463 let code = self.close_frame.as_ref().map_or(CloseCode::ABNORMAL, |close| close.code);
464
465 Event::new("ws.disconnected")
466 .with("path", self.path.as_str())
467 .with("messages", self.received)
468 .with("code", code.code())
469 .took(self.opened_at.elapsed())
470 .dispatch();
471 }
472}
473
474async fn write_loop(
480 mut writer: Box<dyn AsyncWrite + Send + Unpin>,
481 mut queue: mpsc::Receiver<Message>,
482) {
483 while let Some(message) = queue.recv().await {
484 let closing = matches!(message, Message::Close(_));
485 let bytes = message.into_frame().encode();
486
487 if writer.write_all(&bytes).await.is_err() || writer.flush().await.is_err() {
488 break;
489 }
490 if closing {
491 break;
493 }
494 }
495
496 queue.close();
499 let _ = writer.shutdown().await;
500}
501
502fn text_so_far_is_valid(bytes: &[u8], checked: &mut usize) -> bool {
515 match std::str::from_utf8(&bytes[*checked..]) {
516 Ok(_) => {
517 *checked = bytes.len();
518 true
519 }
520 Err(error) if error.error_len().is_none() => {
521 *checked += error.valid_up_to();
524 true
525 }
526 Err(_) => false,
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533 use tokio::io::DuplexStream;
534
535 const MASK: [u8; 4] = [0xa1, 0xb2, 0xc3, 0xd4];
537
538 struct Peer {
541 stream: DuplexStream,
542 buffer: Vec<u8>,
543 }
544
545 impl Peer {
546 async fn send(&mut self, frame: Frame) {
547 self.stream.write_all(&frame.encode()).await.unwrap();
548 }
549
550 async fn send_masked(&mut self, opcode: OpCode, payload: &[u8]) {
552 self.send(Frame::client(opcode, payload.to_vec(), MASK)).await;
553 }
554
555 async fn send_fragment(&mut self, opcode: OpCode, payload: &[u8], fin: bool) {
556 self.send(Frame { fin, opcode, mask: Some(MASK), payload: payload.to_vec() }).await;
557 }
558
559 async fn recv(&mut self) -> Frame {
562 loop {
563 if let Some((frame, used)) =
564 Frame::decode(&self.buffer, Role::Client, 1 << 20).unwrap()
565 {
566 self.buffer.drain(..used);
567 return frame;
568 }
569 let mut chunk = [0u8; 4096];
570 let read = self.stream.read(&mut chunk).await.unwrap();
571 assert!(read > 0, "the server closed without sending an expected frame");
572 self.buffer.extend_from_slice(&chunk[..read]);
573 }
574 }
575
576 async fn hang_up(&mut self) {
577 self.stream.shutdown().await.unwrap();
578 }
579 }
580
581 fn pair(config: WebSocketConfig) -> (WebSocket, Peer) {
582 let (client, server) = tokio::io::duplex(64 * 1024);
583 let (reader, writer) = tokio::io::split(server);
584 let upgraded = Upgraded {
585 reader: Box::new(reader),
586 writer: Box::new(writer),
587 buffered: Vec::new(),
588 };
589 (WebSocket::new(upgraded, config), Peer { stream: client, buffer: Vec::new() })
590 }
591
592 async fn close_from_server(peer: &mut Peer) -> CloseFrame {
594 loop {
595 let frame = peer.recv().await;
596 if frame.opcode == OpCode::Close {
597 return CloseFrame::parse(&frame.payload).unwrap().unwrap();
598 }
599 }
600 }
601
602 #[tokio::test]
603 async fn echoes_a_message_the_client_sent() {
604 let (mut socket, mut peer) = pair(WebSocketConfig::default());
605
606 peer.send_masked(OpCode::Text, b"hello").await;
607 let message = socket.recv().await.unwrap();
608 assert_eq!(message, Message::text("hello"));
609
610 socket.send(message).await.unwrap();
611 let echoed = peer.recv().await;
612 assert_eq!(echoed.opcode, OpCode::Text);
613 assert_eq!(echoed.mask, None, "a server must never mask");
614 assert_eq!(echoed.payload, b"hello");
615 }
616
617 #[tokio::test]
618 async fn bytes_already_buffered_by_the_http_server_are_not_lost() {
619 let (client, server) = tokio::io::duplex(1024);
620 let (reader, writer) = tokio::io::split(server);
621 let early = Frame::client(OpCode::Text, b"first".to_vec(), MASK).encode();
623
624 let mut socket = WebSocket::new(
625 Upgraded { reader: Box::new(reader), writer: Box::new(writer), buffered: early },
626 WebSocketConfig::default(),
627 );
628
629 assert_eq!(socket.recv().await.unwrap(), Message::text("first"));
630 drop(client);
631 }
632
633 #[tokio::test]
634 async fn answers_a_ping_with_a_pong_carrying_the_same_payload() {
635 let (mut socket, mut peer) = pair(WebSocketConfig::default());
636
637 peer.send_masked(OpCode::Ping, b"keepalive").await;
638 peer.send_masked(OpCode::Text, b"after").await;
639
640 assert_eq!(socket.recv().await.unwrap(), Message::text("after"));
642
643 let pong = peer.recv().await;
644 assert_eq!(pong.opcode, OpCode::Pong);
645 assert_eq!(pong.payload, b"keepalive");
646 }
647
648 #[tokio::test]
649 async fn reassembles_a_fragmented_message_around_an_interleaved_ping() {
650 let (mut socket, mut peer) = pair(WebSocketConfig::default());
651
652 peer.send_fragment(OpCode::Text, b"Hel", false).await;
655 peer.send_masked(OpCode::Ping, b"mid").await;
656 peer.send_fragment(OpCode::Continuation, b"lo ", false).await;
657 peer.send_fragment(OpCode::Continuation, b"there", true).await;
658
659 assert_eq!(socket.recv().await.unwrap(), Message::text("Hello there"));
660
661 let pong = peer.recv().await;
662 assert_eq!(pong.opcode, OpCode::Pong);
663 assert_eq!(pong.payload, b"mid");
664 }
665
666 #[tokio::test]
667 async fn a_continuation_with_nothing_to_continue_closes_with_1002() {
668 let (mut socket, mut peer) = pair(WebSocketConfig::default());
669
670 peer.send_fragment(OpCode::Continuation, b"orphan", true).await;
671
672 assert!(socket.recv().await.is_none());
673 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::PROTOCOL_ERROR);
674 }
675
676 #[tokio::test]
677 async fn a_text_frame_that_is_not_utf8_closes_with_1007() {
678 let (mut socket, mut peer) = pair(WebSocketConfig::default());
679
680 peer.send_masked(OpCode::Text, &[0xf0, 0x28, 0x8c, 0x28]).await;
681
682 assert!(socket.recv().await.is_none());
683 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::INVALID_PAYLOAD);
684 assert_eq!(socket.close_frame().unwrap().code, CloseCode::INVALID_PAYLOAD);
685 }
686
687 #[test]
691 fn the_checked_pointer_stops_at_an_unfinished_character_and_moves_on_when_it_is_finished() {
692 let mut checked = 0;
693
694 let mut bytes = vec![b'c', b'a', b'f', 0xc3];
696 assert!(text_so_far_is_valid(&bytes, &mut checked));
697 assert_eq!(checked, 3, "the unfinished character was counted as checked");
698
699 bytes.push(0xa9);
701 assert!(text_so_far_is_valid(&bytes, &mut checked));
702 assert_eq!(checked, 5);
703
704 bytes[0] = 0xff;
708 bytes.push(b'!');
709 assert!(text_so_far_is_valid(&bytes, &mut checked));
710 assert_eq!(checked, 6);
711 }
712
713 #[test]
714 fn bytes_that_can_never_become_valid_utf8_are_refused_at_once() {
715 for bad in [&[0xffu8][..], &[0xc0, 0xaf], &[0x80], &[0xed, 0xa0, 0x80], &[0xf4, 0x90, 0x80, 0x80]] {
716 let mut checked = 0;
717 assert!(!text_so_far_is_valid(bad, &mut checked), "{bad:02x?} was accepted");
718 }
719 let mut checked = 0;
722 assert!(text_so_far_is_valid(&[0xe0], &mut checked));
723 assert!(!text_so_far_is_valid(&[0xe0, 0x80], &mut checked));
724 }
725
726 #[tokio::test]
738 async fn an_invalid_first_fragment_closes_with_1007_before_the_message_ends() {
739 let (mut socket, mut peer) = pair(WebSocketConfig::default());
740
741 peer.send_fragment(OpCode::Text, &[b'o', b'k', 0xff], false).await;
743
744 let closed = tokio::time::timeout(Duration::from_secs(2), socket.recv())
745 .await
746 .expect("the server waited for a final fragment that was never coming");
747 assert!(closed.is_none());
748 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::INVALID_PAYLOAD);
749 }
750
751 #[tokio::test]
752 async fn an_invalid_later_fragment_closes_with_1007_when_it_arrives() {
753 let (mut socket, mut peer) = pair(WebSocketConfig::default());
754
755 peer.send_fragment(OpCode::Text, b"fine so far ", false).await;
756 peer.send_fragment(OpCode::Continuation, b"still fine", false).await;
757 peer.send_fragment(OpCode::Continuation, &[0xc0, 0xaf], false).await; let closed = tokio::time::timeout(Duration::from_secs(2), socket.recv())
760 .await
761 .expect("the server waited for a final fragment that was never coming");
762 assert!(closed.is_none());
763 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::INVALID_PAYLOAD);
764 }
765
766 #[tokio::test]
771 async fn a_character_split_across_fragments_is_not_an_error() {
772 let (mut socket, mut peer) = pair(WebSocketConfig::default());
773
774 peer.send_fragment(OpCode::Text, &[b'c', b'a', b'f', 0xc3], false).await;
776 peer.send_fragment(OpCode::Continuation, &[0xa9, b' ', 0xf0], false).await;
777 peer.send_fragment(OpCode::Continuation, &[0x9f, 0x98], false).await;
778 peer.send_fragment(OpCode::Continuation, &[0x80], true).await;
779
780 match socket.recv().await {
781 Some(Message::Text(text)) => assert_eq!(text, "café 😀"),
782 other => panic!("a valid message cut mid-character was refused: {other:?}"),
783 }
784 }
785
786 #[tokio::test]
790 async fn a_message_that_ends_mid_character_is_still_refused() {
791 let (mut socket, mut peer) = pair(WebSocketConfig::default());
792
793 peer.send_fragment(OpCode::Text, b"caf", false).await;
794 peer.send_fragment(OpCode::Continuation, &[0xc3], true).await;
795
796 assert!(socket.recv().await.is_none());
797 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::INVALID_PAYLOAD);
798 }
799
800 #[tokio::test]
803 async fn a_fragmented_binary_message_is_never_validated_as_text() {
804 let (mut socket, mut peer) = pair(WebSocketConfig::default());
805
806 peer.send_fragment(OpCode::Binary, &[0xff, 0xfe], false).await;
807 peer.send_fragment(OpCode::Continuation, &[0xc0, 0xaf], true).await;
808
809 match socket.recv().await {
810 Some(Message::Binary(bytes)) => assert_eq!(bytes, vec![0xff, 0xfe, 0xc0, 0xaf]),
811 other => panic!("binary was validated as if it were text: {other:?}"),
812 }
813 }
814
815 #[tokio::test]
816 async fn an_unmasked_client_frame_closes_with_1002() {
817 let (mut socket, mut peer) = pair(WebSocketConfig::default());
818
819 peer.send(Frame::server(OpCode::Text, b"naked".to_vec())).await;
821
822 assert!(socket.recv().await.is_none());
823 let close = close_from_server(&mut peer).await;
824 assert_eq!(close.code, CloseCode::PROTOCOL_ERROR);
825 assert!(close.reason.contains("must be masked"), "{}", close.reason);
826 }
827
828 #[tokio::test]
829 async fn an_oversize_frame_closes_with_1009() {
830 let config = WebSocketConfig { max_frame_size: 64, ..WebSocketConfig::default() };
831 let (mut socket, mut peer) = pair(config);
832
833 peer.send_masked(OpCode::Binary, &[7u8; 200]).await;
834
835 assert!(socket.recv().await.is_none());
836 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::TOO_LARGE);
837 }
838
839 #[tokio::test]
840 async fn fragments_that_add_up_to_more_than_the_limit_close_with_1009() {
841 let config = WebSocketConfig {
842 max_message_size: 8,
843 max_frame_size: 1024,
844 ..WebSocketConfig::default()
845 };
846 let (mut socket, mut peer) = pair(config);
847
848 peer.send_fragment(OpCode::Binary, &[0u8; 5], false).await;
850 peer.send_fragment(OpCode::Continuation, &[0u8; 5], true).await;
851
852 assert!(socket.recv().await.is_none());
853 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::TOO_LARGE);
854 }
855
856 #[tokio::test]
857 async fn a_close_from_the_peer_is_echoed_back() {
858 let (mut socket, mut peer) = pair(WebSocketConfig::default());
859
860 peer.send(Frame::client(
861 OpCode::Close,
862 CloseFrame::new(CloseCode::NORMAL, "bye").to_payload(),
863 MASK,
864 ))
865 .await;
866
867 assert!(socket.recv().await.is_none());
868
869 let echo = close_from_server(&mut peer).await;
870 assert_eq!(echo.code, CloseCode::NORMAL);
871 assert_eq!(echo.reason, "bye");
872 assert_eq!(socket.close_frame().unwrap().code, CloseCode::NORMAL);
874 }
875
876 #[tokio::test]
877 async fn closing_waits_for_the_peers_half_of_the_handshake() {
878 let (mut socket, mut peer) = pair(WebSocketConfig::default());
879
880 let reply = async {
881 let frame = peer.recv().await;
882 assert_eq!(frame.opcode, OpCode::Close);
883 assert_eq!(
884 CloseFrame::parse(&frame.payload).unwrap().unwrap().code,
885 CloseCode::GOING_AWAY
886 );
887 peer.send(Frame::client(
888 OpCode::Close,
889 CloseFrame::new(CloseCode(4001), "acknowledged").to_payload(),
890 MASK,
891 ))
892 .await;
893 };
894
895 tokio::join!(socket.close(CloseCode::GOING_AWAY, "restarting"), reply);
896
897 assert!(socket.is_closed());
898 assert_eq!(socket.close_frame().unwrap().code, CloseCode(4001));
900 assert!(socket.send(Message::text("late")).await.is_err());
901 }
902
903 #[tokio::test(start_paused = true)]
904 async fn an_idle_socket_is_pinged_and_then_closed() {
905 let config = WebSocketConfig {
906 idle_timeout: Some(Duration::from_secs(30)),
907 ..WebSocketConfig::default()
908 };
909 let (mut socket, mut peer) = pair(config);
910
911 let reading = tokio::spawn(async move {
912 let message = socket.recv().await;
913 (message, socket.close_frame().cloned())
914 });
915
916 let ping = peer.recv().await;
918 assert_eq!(ping.opcode, OpCode::Ping);
919
920 assert_eq!(close_from_server(&mut peer).await.code, CloseCode::GOING_AWAY);
922
923 let (message, close) = reading.await.unwrap();
924 assert!(message.is_none());
925 assert_eq!(close.unwrap().code, CloseCode::GOING_AWAY);
926 }
927
928 #[tokio::test(start_paused = true)]
929 async fn a_pong_keeps_an_otherwise_idle_socket_open() {
930 let config = WebSocketConfig {
931 idle_timeout: Some(Duration::from_secs(30)),
932 ..WebSocketConfig::default()
933 };
934 let (mut socket, mut peer) = pair(config);
935
936 let reading = tokio::spawn(async move { socket.recv().await });
937
938 let ping = peer.recv().await;
939 assert_eq!(ping.opcode, OpCode::Ping);
940 peer.send_masked(OpCode::Pong, &ping.payload).await;
941 peer.send_masked(OpCode::Text, b"still here").await;
942
943 assert_eq!(reading.await.unwrap(), Some(Message::text("still here")));
944 }
945
946 #[tokio::test]
947 async fn a_socket_that_just_ends_reports_an_abnormal_close() {
948 let (mut socket, mut peer) = pair(WebSocketConfig::default());
949
950 peer.hang_up().await;
951
952 assert!(socket.recv().await.is_none());
953 assert_eq!(socket.close_frame().unwrap().code, CloseCode::ABNORMAL);
954 }
955
956 #[tokio::test]
957 async fn a_full_send_queue_is_reported_rather_than_waited_on() {
958 let (sender, mut receiver) = channel(2);
959
960 assert!(sender.try_send(Message::text("one")).is_ok());
961 assert!(sender.try_send(Message::text("two")).is_ok());
962 assert!(matches!(sender.try_send(Message::text("three")), Err(WsError::Full)));
965
966 assert_eq!(receiver.recv().await, Some(Message::text("one")));
967 assert!(sender.try_send(Message::text("three")).is_ok());
968 }
969
970 #[tokio::test]
971 async fn senders_report_closed_once_the_socket_is_gone() {
972 let (sender, receiver) = channel(2);
973 drop(receiver);
974
975 assert!(sender.is_closed());
976 assert!(matches!(sender.try_send(Message::text("x")), Err(WsError::Closed)));
977 }
978
979 #[test]
980 fn limits_come_from_the_application_configuration() {
981 let config = Config::new();
982 config.set("websocket.max_message_size", 4096);
983 config.set("websocket.idle_timeout", 0);
984 config.set("websocket.send_queue", 8);
985
986 let settings = WebSocketConfig::from_config(&config);
987
988 assert_eq!(settings.max_message_size, 4096);
989 assert_eq!(settings.send_queue, 8);
990 assert_eq!(settings.idle_timeout, None);
992 assert_eq!(settings.max_frame_size, WebSocketConfig::default().max_frame_size);
994 }
995}