quad_net/
web_socket.rs

1//! Websocket client. Works through native websockets on web and through ws-rs on the desktop.
2
3#[cfg(target_arch = "wasm32")]
4pub(crate) mod js_web_socket {
5    use std::net::ToSocketAddrs;
6
7    use sapp_jsutils::JsObject;
8
9    use crate::error::Error;
10
11    pub struct WebSocket;
12
13    extern "C" {
14        fn ws_connect(addr: JsObject);
15        fn ws_send(buffer: JsObject);
16        fn ws_try_recv() -> JsObject;
17        fn ws_is_connected() -> i32;
18    }
19
20    impl WebSocket {
21        pub fn send_text(&self, text: &str) {
22            unsafe { ws_send(JsObject::string(text)) };
23        }
24
25        pub fn send_bytes(&self, data: &[u8]) {
26            unsafe { ws_send(JsObject::buffer(data)) };
27        }
28
29        pub fn try_recv(&mut self) -> Option<Vec<u8>> {
30            let data = unsafe { ws_try_recv() };
31            if data.is_nil() == false {
32                let is_text = data.field_u32("text") == 1;
33                let mut buf = vec![];
34                if is_text {
35                    let mut s = String::new();
36                    data.field("data").to_string(&mut s);
37                    buf = s.into_bytes();
38                } else {
39                    data.to_byte_buffer(&mut buf);
40                }
41                return Some(buf);
42            }
43            None
44        }
45
46        pub fn connected(&self) -> bool {
47            unsafe { ws_is_connected() == 1 }
48        }
49
50        pub fn connect<A: ToSocketAddrs + std::fmt::Display>(addr: A) -> Result<WebSocket, Error> {
51            unsafe { ws_connect(JsObject::string(&format!("{}", addr))) };
52
53            Ok(WebSocket)
54        }
55    }
56}
57
58#[cfg(not(target_arch = "wasm32"))]
59mod pc_web_socket {
60    use std::net::ToSocketAddrs;
61    use std::sync::{mpsc, Mutex};
62
63    use crate::error::Error;
64
65    pub struct WebSocket {
66        sender: ws::Sender,
67        rx: Mutex<mpsc::Receiver<Event>>,
68    }
69
70    enum Event {
71        Connect(ws::Sender),
72        Message(Vec<u8>),
73    }
74
75    struct Client {
76        out: ws::Sender,
77        thread_out: mpsc::Sender<Event>,
78    }
79
80    impl ws::Handler for Client {
81        fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> {
82            self.thread_out
83                .send(Event::Connect(self.out.clone()))
84                .unwrap();
85            Ok(())
86        }
87
88        fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
89            self.thread_out
90                .send(Event::Message(msg.into_data()))
91                .unwrap();
92            Ok(())
93        }
94
95        fn on_close(&mut self, code: ws::CloseCode, _reason: &str) {
96            println!("closed {:?}", code);
97        }
98
99        fn on_error(&mut self, error: ws::Error) {
100            println!("{:?}", error);
101        }
102    }
103
104    impl WebSocket {
105        pub fn connect<A: ToSocketAddrs + std::fmt::Display>(addr: A) -> Result<WebSocket, Error> {
106            let (tx, rx) = mpsc::channel();
107            let ws_addr = format!("{}", addr);
108            std::thread::spawn(move || {
109                ws::connect(ws_addr, |out| Client {
110                    out,
111                    thread_out: tx.clone(),
112                })
113                .unwrap()
114            });
115
116            match rx.recv() {
117                Ok(Event::Connect(sender)) => Ok(WebSocket {
118                    sender,
119                    rx: Mutex::new(rx),
120                }),
121                _ => panic!("Failed to connect websocket"),
122            }
123        }
124
125        pub fn connected(&self) -> bool {
126            true
127        }
128
129        pub fn try_recv(&mut self) -> Option<Vec<u8>> {
130            self.rx
131                .lock()
132                .unwrap()
133                .try_recv()
134                .ok()
135                .map(|event| match event {
136                    Event::Message(msg) => msg,
137                    _ => panic!(),
138                })
139        }
140
141        pub fn send_text(&self, text: &str) {
142            self.sender.send(ws::Message::text(text)).unwrap();
143        }
144
145        pub fn send_bytes(&self, data: &[u8]) {
146            self.sender
147                .send(ws::Message::Binary(data.to_vec()))
148                .unwrap();
149        }
150    }
151}
152
153#[cfg(target_arch = "wasm32")]
154pub use js_web_socket::WebSocket;
155
156#[cfg(not(target_arch = "wasm32"))]
157pub use pc_web_socket::WebSocket;