From 970d9937580e4c2f2e2c1d5ac982442714da17c9 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 1 May 2023 11:27:19 -0500 Subject: [PATCH] attempt to reconnect to server after sever disconnects in debug mode --- .../server/examples/axum-router/src/main.rs | 3 +- packages/server/src/adapters/axum_adapter.rs | 43 +++++++++++++++---- packages/server/src/adapters/salvo_adapter.rs | 25 ++++++++++- packages/server/src/adapters/warp_adapter.rs | 33 +++++++++++--- packages/server/src/render.rs | 39 +++++++++++++++-- 5 files changed, 122 insertions(+), 21 deletions(-) diff --git a/packages/server/examples/axum-router/src/main.rs b/packages/server/examples/axum-router/src/main.rs index 2afa4a481..3994eeb51 100644 --- a/packages/server/examples/axum-router/src/main.rs +++ b/packages/server/examples/axum-router/src/main.rs @@ -9,6 +9,7 @@ use dioxus::prelude::*; use dioxus_router::*; use dioxus_server::prelude::*; +use serde::{Deserialize, Serialize}; fn main() { #[cfg(feature = "web")] @@ -62,7 +63,7 @@ fn main() { } } -#[derive(Clone, Debug, Props, PartialEq)] +#[derive(Clone, Debug, Props, PartialEq, Serialize, Deserialize)] struct AppProps { route: Option, } diff --git a/packages/server/src/adapters/axum_adapter.rs b/packages/server/src/adapters/axum_adapter.rs index 1b572c804..541094674 100644 --- a/packages/server/src/adapters/axum_adapter.rs +++ b/packages/server/src/adapters/axum_adapter.rs @@ -267,6 +267,7 @@ where .collect::>() .join("/"); let route = format!("/{}", route); + println!("Serving static asset at {}", route); if path.is_dir() { self = self.nest_service(&route, ServeDir::new(path)); } else { @@ -275,20 +276,43 @@ where } // Add server functions and render index.html - self.connect_hot_reload() - .register_server_fns(server_fn_route) - .route( - "/", - get(render_handler).with_state((cfg, SSRState::default())), - ) + self.route( + "/", + get(render_handler).with_state((cfg, SSRState::default())), + ) + .connect_hot_reload() + .register_server_fns(server_fn_route) } fn connect_hot_reload(self) -> Self { #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] { - self.route( - "/_dioxus/hot_reload", - get(hot_reload_handler).with_state(crate::hot_reload::HotReloadState::default()), + self.nest( + "/_dioxus", + Router::new() + .route( + "/hot_reload", + get(hot_reload_handler) + .with_state(crate::hot_reload::HotReloadState::default()), + ) + .route( + "/disconnect", + get(|ws: WebSocketUpgrade| async { + ws.on_failed_upgrade(|error| { + println!("failed to upgrade: {}", error); + todo!() + }) + .on_upgrade(|mut ws| async move { + use axum::extract::ws::Message; + let _ = ws.send(Message::Text("connected".into())).await; + loop { + if ws.recv().await.is_none() { + break; + } + } + }) + }), + ), ) } #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))] @@ -302,6 +326,7 @@ async fn render_handler( State((cfg, ssr_state)): State<(ServeConfig

, SSRState)>, request: Request, ) -> impl IntoResponse { + println!("Rendering"); let (parts, _) = request.into_parts(); let parts: Arc = Arc::new(parts.into()); let server_context = DioxusServerContext::new(parts); diff --git a/packages/server/src/adapters/salvo_adapter.rs b/packages/server/src/adapters/salvo_adapter.rs index 6ed4c2b3e..3751211e4 100644 --- a/packages/server/src/adapters/salvo_adapter.rs +++ b/packages/server/src/adapters/salvo_adapter.rs @@ -261,7 +261,14 @@ impl DioxusRouterExt for Router { } fn connect_hot_reload(self) -> Self { - self.push(Router::with_path("/_dioxus/hot_reload").get(HotReloadHandler::default())) + let mut _dioxus_router = Router::with_path("_dioxus"); + _dioxus_router = _dioxus_router + .push(Router::with_path("hot_reload").handle(HotReloadHandler::default())); + #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] + { + _dioxus_router = _dioxus_router.push(Router::with_path("disconnect").handle(ignore_ws)); + } + self.push(_dioxus_router) } } @@ -504,3 +511,19 @@ impl HotReloadHandler { .await } } + +#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] +#[handler] +async fn ignore_ws(req: &mut Request, res: &mut Response) -> Result<(), salvo::http::StatusError> { + use salvo::ws::WebSocketUpgrade; + WebSocketUpgrade::new() + .upgrade(req, res, |mut ws| async move { + let _ = dbg!(ws.send(salvo::ws::Message::text("connected")).await); + while let Some(msg) = ws.recv().await { + if msg.is_err() { + return; + }; + } + }) + .await +} diff --git a/packages/server/src/adapters/warp_adapter.rs b/packages/server/src/adapters/warp_adapter.rs index 5e9d23562..3d41d76cf 100644 --- a/packages/server/src/adapters/warp_adapter.rs +++ b/packages/server/src/adapters/warp_adapter.rs @@ -360,7 +360,7 @@ pub fn connect_hot_reload() -> impl Filter impl Filter impl Filter); + impl Drop for DisconnectOnDrop { + fn drop(&mut self) { + let _ = self.0.take().unwrap().close(); + } + } + + let _ = websocket.send(Message::text("connected")).await; + let mut ws = DisconnectOnDrop(Some(websocket)); + + loop { + ws.0.as_mut().unwrap().next().await; + } + }) + }, + )) } } diff --git a/packages/server/src/render.rs b/packages/server/src/render.rs index e8f6a67ec..1d32e2acd 100644 --- a/packages/server/src/render.rs +++ b/packages/server/src/render.rs @@ -25,9 +25,7 @@ impl Default for SSRState { impl SSRState { /// Render the application to HTML. pub fn render(&self, cfg: &ServeConfig

) -> String { - let ServeConfig { - app, props, .. - } = cfg; + let ServeConfig { app, props, .. } = cfg; let mut vdom = VirtualDom::new_with_props(*app, props.clone()); @@ -55,6 +53,41 @@ impl SSRState { // serialize the props let _ = crate::props_html::serialize_props::encode_in_element(&cfg.props, &mut html); + #[cfg(all(debug_assertions, feature = "hot-reload"))] + { + let disconnect_js = r#"(function () { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const url = protocol + '//' + window.location.host + '/_dioxus/disconnect'; + const poll_interval = 1000; + const reload_upon_connect = () => { + console.log('Disconnected from server. Attempting to reconnect...'); + window.setTimeout( + () => { + // Try to reconnect to the websocket + const ws = new WebSocket(url); + ws.onopen = () => { + // If we reconnect, reload the page + window.location.reload(); + } + // Otherwise, try again in a second + reload_upon_connect(); + }, + poll_interval); + }; + + // on initial page load connect to the disconnect ws + const ws = new WebSocket(url); + ws.onopen = () => { + // if we disconnect, start polling + ws.onclose = reload_upon_connect; + }; +})()"#; + + html += r#""#; + } + html += &index.post_main; html