vertigo_cli/serve/
vertigo_handler.rs

1use actix_web::{HttpRequest, http::StatusCode, web};
2use std::time::Instant;
3
4use crate::serve::MountConfig;
5
6use super::server_state::ServerState;
7
8/// Directly attach SSR mechanism to the actix web server (no static files mounting)
9///
10/// To root path:
11///
12/// ```no_run
13/// # use actix_web::web;
14/// # use vertigo_cli::serve::{MountConfigBuilder, vertigo_handler};
15/// # let mount_config = MountConfigBuilder::new("/", "/").build().unwrap();
16/// # let app = web::scope("/");
17/// app.default_service(vertigo_handler(&mount_config));
18/// ```
19///
20/// To custom mount point:
21///
22/// ```no_run
23/// # use actix_web::web;
24/// # use vertigo_cli::serve::{MountConfigBuilder, vertigo_handler};
25/// # let mount_config = MountConfigBuilder::new("/", "/").build().unwrap();
26/// # let app = web::scope("/");
27/// app.service(
28///     web::scope(mount_config.mount_point())
29///         .default_service(vertigo_handler(&mount_config)),
30/// );
31/// ```
32pub fn vertigo_handler(mount_config: &MountConfig) -> actix_web::Route {
33    let mount_point = mount_config.mount_point().to_string();
34
35    web::route().to(move |req: HttpRequest| {
36        let mount_point = mount_point.clone();
37        async move {
38            let state = ServerState::global(&mount_point);
39            let now = Instant::now();
40            let url = req.uri();
41
42            let uri = {
43                let path = url.path();
44                // Strip mount point to get local url
45                let local_url = if state.mount_config.mount_point() != "/" {
46                    path.trim_start_matches(state.mount_config.mount_point())
47                } else {
48                    path
49                };
50
51                match url.query() {
52                    Some(query) => format!("{local_url}?{query}"),
53                    None => local_url.to_string(),
54                }
55            };
56
57            log::debug!("Incoming request: {uri}");
58            let mut response_state = state.request(&uri).await;
59
60            let time = now.elapsed().as_millis();
61            let log_level = if time > 1000 {
62                log::Level::Warn
63            } else {
64                log::Level::Info
65            };
66            log::log!(
67                log_level,
68                "Response for request: {} {time}ms {uri}",
69                response_state.status,
70            );
71
72            if let Some(port_watch) = state.port_watch {
73                response_state.add_watch_script(port_watch);
74            }
75
76            // Checking for error status to log
77            if StatusCode::from_u16(response_state.status)
78                .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
79                .is_server_error()
80            {
81                log::error!("WASM status: {}", response_state.status);
82
83                match String::from_utf8(response_state.body.clone()) {
84                    Ok(messagee) => {
85                        log::error!("WASM response: text={}", messagee);
86                    }
87                    Err(_) => {
88                        log::error!("WASM response: bytes={:#?}", response_state.body);
89                    }
90                }
91            }
92
93            actix_web::HttpResponse::from(response_state)
94        }
95    })
96}