@@ -51,10 +51,9 @@ use web_sys::{Blob, MessageEvent};
51
51
/// Wrapper around browser's WebSocket API.
52
52
#[ allow( missing_debug_implementations) ]
53
53
#[ pin_project( PinnedDrop ) ]
54
- #[ derive( Clone ) ]
55
54
pub struct WebSocket {
56
55
ws : web_sys:: WebSocket ,
57
- sink_wakers : Rc < RefCell < Vec < Waker > > > ,
56
+ sink_waker : Rc < RefCell < Option < Waker > > > ,
58
57
#[ pin]
59
58
message_receiver : Receiver < StreamMessage > ,
60
59
#[ allow( clippy:: type_complexity) ]
@@ -77,23 +76,22 @@ impl WebSocket {
77
76
/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#exceptions_thrown)
78
77
/// to learn more.
79
78
pub fn open ( url : & str ) -> Result < Self , JsError > {
80
- let wakers : Rc < RefCell < Vec < Waker > > > = Rc :: new ( RefCell :: new ( vec ! [ ] ) ) ;
79
+ let waker : Rc < RefCell < Option < Waker > > > = Rc :: new ( RefCell :: new ( None ) ) ;
81
80
let ws = web_sys:: WebSocket :: new ( url) . map_err ( js_to_js_error) ?;
82
81
83
82
let ( sender, receiver) = async_broadcast:: broadcast ( 10 ) ;
84
83
85
84
let open_callback: Closure < dyn FnMut ( ) > = {
86
- let wakers = Rc :: clone ( & wakers ) ;
85
+ let waker = Rc :: clone ( & waker ) ;
87
86
Closure :: wrap ( Box :: new ( move || {
88
- for waker in wakers . borrow_mut ( ) . drain ( .. ) {
87
+ if let Some ( waker) = waker . borrow_mut ( ) . take ( ) {
89
88
waker. wake ( ) ;
90
89
}
91
90
} ) as Box < dyn FnMut ( ) > )
92
91
} ;
93
92
94
93
ws. set_onopen ( Some ( open_callback. as_ref ( ) . unchecked_ref ( ) ) ) ;
95
- // open_callback.forget();
96
- //
94
+
97
95
let message_callback: Closure < dyn FnMut ( MessageEvent ) > = {
98
96
let sender = sender. clone ( ) ;
99
97
Closure :: wrap ( Box :: new ( move |e : MessageEvent | {
@@ -106,7 +104,6 @@ impl WebSocket {
106
104
} ;
107
105
108
106
ws. set_onmessage ( Some ( message_callback. as_ref ( ) . unchecked_ref ( ) ) ) ;
109
- // message_callback.forget();
110
107
111
108
let error_callback: Closure < dyn FnMut ( web_sys:: ErrorEvent ) > = {
112
109
let sender = sender. clone ( ) ;
@@ -123,7 +120,6 @@ impl WebSocket {
123
120
} ;
124
121
125
122
ws. set_onerror ( Some ( error_callback. as_ref ( ) . unchecked_ref ( ) ) ) ;
126
- // error_callback.forget();
127
123
128
124
let close_callback: Closure < dyn FnMut ( web_sys:: CloseEvent ) > = {
129
125
Closure :: wrap ( Box :: new ( move |e : web_sys:: CloseEvent | {
@@ -144,11 +140,10 @@ impl WebSocket {
144
140
} ;
145
141
146
142
ws. set_onerror ( Some ( close_callback. as_ref ( ) . unchecked_ref ( ) ) ) ;
147
- // close_callback.forget();
148
143
149
144
Ok ( Self {
150
145
ws,
151
- sink_wakers : wakers ,
146
+ sink_waker : waker ,
152
147
message_receiver : receiver,
153
148
closures : Rc :: new ( (
154
149
open_callback,
@@ -163,9 +158,6 @@ impl WebSocket {
163
158
///
164
159
/// See the [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/close#parameters)
165
160
/// to learn about parameters passed to this function and when it can return an `Err(_)`
166
- ///
167
- /// **Note**: If *only one* of the instances of websocket is closed, the entire connection closes.
168
- /// This is unlikely to happen in real-world as [`wasm_bindgen_futures::spawn_local`] requires `'static`.
169
161
pub fn close ( self , code : Option < u16 > , reason : Option < & str > ) -> Result < ( ) , JsError > {
170
162
let result = match ( code, reason) {
171
163
( None , None ) => self . ws . close ( ) ,
@@ -240,7 +232,7 @@ impl Sink<Message> for WebSocket {
240
232
fn poll_ready ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Result < ( ) , Self :: Error > > {
241
233
let ready_state = self . ws . ready_state ( ) ;
242
234
if ready_state == 0 {
243
- self . sink_wakers . borrow_mut ( ) . push ( cx. waker ( ) . clone ( ) ) ;
235
+ * self . sink_waker . borrow_mut ( ) = Some ( cx. waker ( ) . clone ( ) ) ;
244
236
Poll :: Pending
245
237
} else {
246
238
Poll :: Ready ( Ok ( ( ) ) )
@@ -298,23 +290,35 @@ mod tests {
298
290
use super :: * ;
299
291
use futures:: { SinkExt , StreamExt } ;
300
292
use wasm_bindgen_test:: * ;
293
+ use wasm_bindgen_futures:: spawn_local;
301
294
302
295
wasm_bindgen_test_configure ! ( run_in_browser) ;
303
296
304
297
const ECHO_SERVER_URL : & str = env ! ( "ECHO_SERVER_URL" ) ;
305
298
306
299
#[ wasm_bindgen_test]
307
- async fn websocket_works ( ) {
308
- let mut ws = WebSocket :: open ( ECHO_SERVER_URL ) . unwrap ( ) ;
309
-
310
- ws. send ( Message :: Text ( "test" . to_string ( ) ) ) . await . unwrap ( ) ;
311
-
312
- // ignore first message
313
- // the echo-server used sends it's info in the first message
314
- let _ = ws. next ( ) . await ;
315
- assert_eq ! (
316
- ws. next( ) . await . unwrap( ) . unwrap( ) ,
317
- Message :: Text ( "test" . to_string( ) )
318
- )
300
+ fn websocket_works ( ) {
301
+ let ws = WebSocket :: open ( ECHO_SERVER_URL ) . unwrap ( ) ;
302
+ let ( mut sender, mut receiver) = ws. split ( ) ;
303
+
304
+ spawn_local ( async move {
305
+ sender. send ( Message :: Text ( String :: from ( "test 1" ) ) ) . await . unwrap ( ) ;
306
+ sender. send ( Message :: Text ( String :: from ( "test 2" ) ) ) . await . unwrap ( ) ;
307
+ } ) ;
308
+
309
+ spawn_local ( async move {
310
+ // ignore first message
311
+ // the echo-server used sends it's info in the first message
312
+ // let _ = ws.next().await;
313
+
314
+ assert_eq ! (
315
+ receiver. next( ) . await . unwrap( ) . unwrap( ) ,
316
+ Message :: Text ( "test 1" . to_string( ) )
317
+ ) ;
318
+ assert_eq ! (
319
+ receiver. next( ) . await . unwrap( ) . unwrap( ) ,
320
+ Message :: Text ( "test 2" . to_string( ) )
321
+ ) ;
322
+ } ) ;
319
323
}
320
324
}
0 commit comments