use crate::models::{ActixWsSession, AppState, ConnectRequestQuery};
use actix_web::{Error, HttpRequest, HttpResponse, get, rt, web};
use actix_ws::{Message, handle};
use futures_util::StreamExt;
use tokio::select;
use tracing::{info, warn};
#[get("/ws/pipey")]
pub async fn open_pipe(
req: HttpRequest,
body: web::Payload,
state: web::Data<AppState>,
query: web::Query<ConnectRequestQuery>,
) -> Result<HttpResponse, Error> {
let (resp, session, mut msg_stream) = handle(&req, body)?;
let user_channel = format!("User::{}", query.user_id);
let channels = vec![user_channel];
let arifa = state.arifa.clone();
let heartbeats = state.heartbeats.clone();
let user_id = query.user_id.to_string();
rt::spawn(async move {
let mut session = session;
let session_id = arifa
.subscribe(&channels, ActixWsSession::new(session.clone()), user_id)
.await;
info!(%session_id, "subscribed");
heartbeats.register(session_id.clone(), session.clone());
'connection: loop {
select! {
Some(frame) = msg_stream.next() => {
let msg = match frame {
Ok(msg) => msg,
Err(err) => {
warn!(%session_id, %err, "websocket error");
break 'connection;
}
};
match msg {
Message::Ping(bytes) => {
let _ = session.pong(&bytes).await;
}
Message::Pong(_) => {
heartbeats.touch(&session_id);
}
Message::Close(reason) => {
info!(%session_id, ?reason, "client disconnected");
break 'connection;
}
Message::Text(_) => {}
Message::Binary(_) => {}
Message::Continuation(_) => {}
Message::Nop => {}
}
}
else => break 'connection,
}
}
heartbeats.remove(&session_id);
let _ = arifa.remove_online_user(&session_id).await;
arifa.unsubscribe(&session_id, &channels);
info!(%session_id, "unsubscribed");
let _ = session.close(None).await;
});
Ok(resp)
}