web_dev_server/
internal_scope.rs

1use actix_web::{HttpRequest, HttpResponse, web};
2use actix_ws::Message;
3use futures_util::StreamExt;
4
5use crate::startup::AppState;
6
7pub fn build_internal_scope() -> actix_web::Scope {
8    web::scope("/_live")
9        .route("/health", web::get().to(|| async { "OK" }))
10        .route("/script.js", web::get().to(script))
11        .route("/ws", web::get().to(ws_handler))
12}
13
14async fn script() -> HttpResponse {
15    HttpResponse::Ok()
16        .append_header(("Cache-Control", "no-store, max-age=0"))
17        .content_type("application/javascript")
18        .body(include_str!("./js/script.js"))
19}
20
21async fn ws_handler(
22    req: HttpRequest,
23    stream: web::Payload,
24    state: web::Data<AppState>,
25) -> actix_web::Result<HttpResponse> {
26    let (response, mut session, mut msg_stream) = actix_ws::handle(&req, stream)?;
27    let mut rx = state.broadcaster.subscribe();
28
29    let mut session_for_messages = session.clone();
30
31    actix_web::rt::spawn(async move {
32        while let Some(Ok(message)) = msg_stream.next().await {
33            match message {
34                Message::Ping(bytes) => {
35                    if session_for_messages.pong(&bytes).await.is_err() {
36                        break;
37                    }
38                }
39                Message::Close(reason) => {
40                    let _ = session_for_messages.close(reason).await;
41                    break;
42                }
43                Message::Text(_)
44                | Message::Binary(_)
45                | Message::Continuation(_)
46                | Message::Pong(_) => {}
47                Message::Nop => {}
48            }
49        }
50    });
51
52    actix_web::rt::spawn(async move {
53        while let Ok(event) = rx.recv().await {
54            match serde_json::to_string(&event) {
55                Ok(payload) => {
56                    if session.text(payload).await.is_err() {
57                        break;
58                    }
59                }
60                Err(error) => {
61                    eprintln!("[web-dev-server] failed to serialize live message: {error}")
62                }
63            }
64        }
65    });
66
67    Ok(response)
68}