浏览代码

attempt to reconnect to server after sever disconnects in debug mode

Evan Almloff 2 年之前
父节点
当前提交
970d993758

+ 2 - 1
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<String>,
 }

+ 34 - 9
packages/server/src/adapters/axum_adapter.rs

@@ -267,6 +267,7 @@ where
                 .collect::<Vec<_>>()
                 .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<P: Clone + serde::Serialize + Send + Sync + 'static>(
     State((cfg, ssr_state)): State<(ServeConfig<P>, SSRState)>,
     request: Request<Body>,
 ) -> impl IntoResponse {
+    println!("Rendering");
     let (parts, _) = request.into_parts();
     let parts: Arc<RequestParts> = Arc::new(parts.into());
     let server_context = DioxusServerContext::new(parts);

+ 24 - 1
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
+}

+ 26 - 7
packages/server/src/adapters/warp_adapter.rs

@@ -360,7 +360,7 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
 {
     #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))]
     {
-        warp::path("_dioxus/hot_reload")
+        warp::path!("_dioxus" / "hot_reload")
             .and(warp::ws())
             .map(warp::reply)
             .map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND));
@@ -368,20 +368,19 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
     #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
     {
         use crate::hot_reload::HotReloadState;
+        use futures_util::sink::SinkExt;
+        use futures_util::StreamExt;
+        use warp::ws::Message;
+
         let state = HotReloadState::default();
 
-        warp::path("_dioxus")
-            .and(warp::path("hot_reload"))
+        warp::path!("_dioxus" / "hot_reload")
             .and(warp::ws())
             .and(warp::any().map(move || state.clone()))
             .map(move |ws: warp::ws::Ws, state: HotReloadState| {
                 #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
                 ws.on_upgrade(move |mut websocket| {
                     async move {
-                        use futures_util::sink::SinkExt;
-                        use futures_util::StreamExt;
-                        use warp::ws::Message;
-
                         println!("🔥 Hot Reload WebSocket connected");
                         {
                             // update any rsx calls that changed before the websocket connected.
@@ -418,5 +417,25 @@ pub fn connect_hot_reload() -> impl Filter<Extract = (impl Reply,), Error = warp
                     }
                 })
             })
+            .or(warp::path!("_dioxus" / "disconnect").and(warp::ws()).map(
+                move |ws: warp::ws::Ws| {
+                    #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
+                    ws.on_upgrade(move |mut websocket| async move {
+                        struct DisconnectOnDrop(Option<warp::ws::WebSocket>);
+                        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;
+                        }
+                    })
+                },
+            ))
     }
 }

+ 36 - 3
packages/server/src/render.rs

@@ -25,9 +25,7 @@ impl Default for SSRState {
 impl SSRState {
     /// Render the application to HTML.
     pub fn render<P: 'static + Clone + serde::Serialize>(&self, cfg: &ServeConfig<P>) -> 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#"<script>"#;
+            html += disconnect_js;
+            html += r#"</script>"#;
+        }
+
         html += &index.post_main;
 
         html