@@ -42,6 +42,7 @@ use std::{
4242 marker:: PhantomData ,
4343 pin:: Pin ,
4444 prelude:: rust_2024:: Future ,
45+ rc:: Rc ,
4546 task:: { ready, Context , Poll } ,
4647} ;
4748
@@ -89,7 +90,9 @@ pub fn use_websocket<
8990 // Wake up the `.recv()` calls waiting for the connection to be established
9091 waker. wake ( ( ) ) ;
9192
92- connection
93+ // Wrap in Rc so we can clone it out of the Resource without holding
94+ // a borrow guard across await points
95+ connection. map ( Rc :: new)
9396 }
9497 } ) ;
9598
@@ -113,7 +116,8 @@ where
113116 Out : ' static ,
114117 Enc : ' static ,
115118{
116- connection : Resource < Result < Websocket < In , Out , Enc > , CapturedError > > ,
119+ #[ allow( clippy:: type_complexity) ]
120+ connection : Resource < Result < Rc < Websocket < In , Out , Enc > > , CapturedError > > ,
117121 waker : UseWaker < ( ) > ,
118122 status : Signal < WebsocketState > ,
119123 status_read : ReadSignal < WebsocketState > ,
@@ -161,38 +165,35 @@ impl<In, Out, E> UseWebsocket<In, Out, E> {
161165 /// To send a message with a particular type, see the `.send()` method instead.
162166 pub async fn send_raw ( & self , msg : Message ) -> Result < ( ) , WebsocketError > {
163167 self . connect ( ) . await ;
164-
165- self . connection
166- . as_ref ( )
167- . as_deref ( )
168- . ok_or_else ( WebsocketError :: closed_away) ?
169- . as_ref ( )
170- . map_err ( |_| WebsocketError :: AlreadyClosed ) ?
171- . send_raw ( msg)
172- . await
168+ self . get_connection ( ) ?. send_raw ( msg) . await
173169 }
174170
175171 /// Receive a raw message from the WebSocket connection
176172 ///
177173 /// To receive a message with a particular type, see the `.recv()` method instead.
178174 pub async fn recv_raw ( & mut self ) -> Result < Message , WebsocketError > {
179175 self . connect ( ) . await ;
180-
181- let result = self
182- . connection
183- . as_ref ( )
184- . as_deref ( )
185- . ok_or_else ( WebsocketError :: closed_away) ?
186- . as_ref ( )
187- . map_err ( |_| WebsocketError :: AlreadyClosed ) ?
188- . recv_raw ( )
189- . await ;
190-
191- if let Err ( WebsocketError :: ConnectionClosed { .. } ) = result. as_ref ( ) {
192- self . received_shutdown ( ) ;
176+ let ws = self . get_connection ( ) ?;
177+
178+ // Race the recv against the waker — if the connection is being recreated
179+ // (e.g. a reactive dependency changed), the waker fires and we return an error
180+ // so the caller's loop can restart and pick up the new connection.
181+ let recv_fut = ws. recv_raw ( ) ;
182+ let waker_fut = self . waker . wait ( ) ;
183+ futures:: pin_mut!( recv_fut, waker_fut) ;
184+
185+ match futures:: future:: select ( recv_fut, waker_fut) . await {
186+ futures:: future:: Either :: Left ( ( recv_result, _) ) => {
187+ if let Err ( WebsocketError :: ConnectionClosed { .. } ) = recv_result. as_ref ( ) {
188+ self . received_shutdown ( ) ;
189+ }
190+ recv_result
191+ }
192+ futures:: future:: Either :: Right ( _) => Err ( WebsocketError :: ConnectionClosed {
193+ code : CloseCode :: Away ,
194+ description : "Connection replaced by a new one" . to_string ( ) ,
195+ } ) ,
193196 }
194-
195- result
196197 }
197198
198199 pub async fn send ( & self , msg : In ) -> Result < ( ) , WebsocketError >
@@ -219,22 +220,24 @@ impl<In, Out, E> UseWebsocket<In, Out, E> {
219220 E : Encoding ,
220221 {
221222 self . connect ( ) . await ;
223+ let ws = self . get_connection ( ) ?;
222224
223- let result = self
224- . connection
225- . as_ref ( )
226- . as_deref ( )
227- . ok_or_else ( WebsocketError :: closed_away) ?
228- . as_ref ( )
229- . map_err ( |_| WebsocketError :: AlreadyClosed ) ?
230- . recv ( )
231- . await ;
225+ let recv_fut = ws. recv ( ) ;
226+ let waker_fut = self . waker . wait ( ) ;
227+ futures:: pin_mut!( recv_fut, waker_fut) ;
232228
233- if let Err ( WebsocketError :: ConnectionClosed { .. } ) = result. as_ref ( ) {
234- self . received_shutdown ( ) ;
229+ match futures:: future:: select ( recv_fut, waker_fut) . await {
230+ futures:: future:: Either :: Left ( ( recv_result, _) ) => {
231+ if let Err ( WebsocketError :: ConnectionClosed { .. } ) = recv_result. as_ref ( ) {
232+ self . received_shutdown ( ) ;
233+ }
234+ recv_result
235+ }
236+ futures:: future:: Either :: Right ( _) => Err ( WebsocketError :: ConnectionClosed {
237+ code : CloseCode :: Away ,
238+ description : "Connection replaced by a new one" . to_string ( ) ,
239+ } ) ,
235240 }
236-
237- result
238241 }
239242
240243 /// Set the WebSocket connection.
@@ -247,7 +250,8 @@ impl<In, Out, E> UseWebsocket<In, Out, E> {
247250 Err ( _) => self . status . set ( WebsocketState :: FailedToConnect ) ,
248251 }
249252
250- self . connection . set ( Some ( socket. map_err ( |e| e. into ( ) ) ) ) ;
253+ self . connection
254+ . set ( Some ( socket. map ( Rc :: new) . map_err ( |e| e. into ( ) ) ) ) ;
251255 self . waker . wake ( ( ) ) ;
252256 }
253257
@@ -257,6 +261,20 @@ impl<In, Out, E> UseWebsocket<In, Out, E> {
257261 _self. status . set ( WebsocketState :: Closed ) ;
258262 _self. waker . wake ( ( ) ) ;
259263 }
264+
265+ /// Clone the `Rc<Websocket>` out of the Resource using peek, so we don't hold a borrow
266+ /// guard across await points. This prevents AlreadyBorrowed panics when the Resource
267+ /// tries to write while recv() is awaiting.
268+ #[ allow( clippy:: result_large_err) ]
269+ fn get_connection ( & self ) -> Result < Rc < Websocket < In , Out , E > > , WebsocketError > {
270+ self . connection . with_peek ( |opt| {
271+ opt. as_ref ( )
272+ . ok_or_else ( WebsocketError :: closed_away) ?
273+ . as_ref ( )
274+ . map ( Rc :: clone)
275+ . map_err ( |_| WebsocketError :: AlreadyClosed )
276+ } )
277+ }
260278}
261279
262280impl < In , Out , E > Copy for UseWebsocket < In , Out , E > { }
0 commit comments