Broadcast text & binary messages received from a client, respond to ping messages, and monitor connection health to detect network issues and free up resources.
(
mut session: actix_ws::Session,
mut msg_stream: actix_ws::MessageStream,
mut rx: broadcast::Receiver<web::Bytes>,
)
| 160 | /// Broadcast text & binary messages received from a client, respond to ping messages, and monitor |
| 161 | /// connection health to detect network issues and free up resources. |
| 162 | pub async fn broadcast_ws( |
| 163 | mut session: actix_ws::Session, |
| 164 | mut msg_stream: actix_ws::MessageStream, |
| 165 | mut rx: broadcast::Receiver<web::Bytes>, |
| 166 | ) { |
| 167 | log::info!("connected"); |
| 168 | |
| 169 | let mut last_heartbeat = Instant::now(); |
| 170 | let mut interval = interval(HEARTBEAT_INTERVAL); |
| 171 | |
| 172 | let reason = loop { |
| 173 | // waits for either `msg_stream` to receive a message from the client, the broadcast channel |
| 174 | // to send a message, or the heartbeat interval timer to tick, yielding the value of |
| 175 | // whichever one is ready first |
| 176 | select! { |
| 177 | broadcast_msg = rx.recv() => { |
| 178 | let msg = match broadcast_msg { |
| 179 | Ok(msg) => msg, |
| 180 | Err(broadcast::error::RecvError::Closed) => break None, |
| 181 | Err(broadcast::error::RecvError::Lagged(_)) => continue, |
| 182 | }; |
| 183 | |
| 184 | let res = match std::str::from_utf8(&msg) { |
| 185 | Ok(val) => session.text(val).await, |
| 186 | Err(_) => session.binary(msg).await, |
| 187 | }; |
| 188 | |
| 189 | if let Err(err) = res { |
| 190 | log::error!("{err}"); |
| 191 | break None; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | // heartbeat interval ticked |
| 196 | _tick = interval.tick() => { |
| 197 | // if no heartbeat ping/pong received recently, close the connection |
| 198 | if Instant::now().duration_since(last_heartbeat) > CLIENT_TIMEOUT { |
| 199 | log::info!( |
| 200 | "client has not sent heartbeat in over {CLIENT_TIMEOUT:?}; disconnecting" |
| 201 | ); |
| 202 | |
| 203 | break None; |
| 204 | } |
| 205 | |
| 206 | // send heartbeat ping |
| 207 | let _ = session.ping(b"").await; |
| 208 | }, |
| 209 | |
| 210 | msg = msg_stream.next() => { |
| 211 | let msg = match msg { |
| 212 | // received message from WebSocket client |
| 213 | Some(Ok(msg)) => msg, |
| 214 | |
| 215 | // client WebSocket stream error |
| 216 | Some(Err(err)) => { |
| 217 | log::error!("{err}"); |
| 218 | break None; |
| 219 | } |
nothing calls this directly
no outgoing calls
no test coverage detected