attempt to reconnect to server after sever disconnects in debug mode

This commit is contained in:
Evan Almloff committed 2023-05-01 11:27:19 -05:00
1 parent 35dcacb956
commit 970d993758
5 files changed
+122 -21

No files matched your search

@@ -9,6 +9,7 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_router::*; use dioxus_router::*;
use dioxus_server::prelude::*; use dioxus_server::prelude::*;
use serde::{Deserialize, Serialize};
fn main() { fn main() {
#[cfg(feature = "web")] #[cfg(feature = "web")]
@@ -62,7 +63,7 @@ fn main() {
} }
} }
#[derive(Clone, Debug, Props, PartialEq)] #[derive(Clone, Debug, Props, PartialEq, Serialize, Deserialize)]
struct AppProps { struct AppProps {
route: Option<String>, route: Option<String>,
} }
+34 -9
View File
@@ -267,6 +267,7 @@ where
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("/"); .join("/");
let route = format!("/{}", route); let route = format!("/{}", route);
println!("Serving static asset at {}", route);
if path.is_dir() { if path.is_dir() {
self = self.nest_service(&route, ServeDir::new(path)); self = self.nest_service(&route, ServeDir::new(path));
} else { } else {
@@ -275,20 +276,43 @@ where
} }
// Add server functions and render index.html // Add server functions and render index.html
self.connect_hot_reload() self.route(
.register_server_fns(server_fn_route) "/",
.route( get(render_handler).with_state((cfg, SSRState::default())),
"/", )
get(render_handler).with_state((cfg, SSRState::default())), .connect_hot_reload()
) .register_server_fns(server_fn_route)
} }
fn connect_hot_reload(self) -> Self { fn connect_hot_reload(self) -> Self {
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
{ {
self.route( self.nest(
"/_dioxus/hot_reload", "/_dioxus",
get(hot_reload_handler).with_state(crate::hot_reload::HotReloadState::default()), 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")))] #[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)>, State((cfg, ssr_state)): State<(ServeConfig<P>, SSRState)>,
request: Request<Body>, request: Request<Body>,
) -> impl IntoResponse { ) -> impl IntoResponse {
println!("Rendering");
let (parts, _) = request.into_parts(); let (parts, _) = request.into_parts();
let parts: Arc<RequestParts> = Arc::new(parts.into()); let parts: Arc<RequestParts> = Arc::new(parts.into());
let server_context = DioxusServerContext::new(parts); let server_context = DioxusServerContext::new(parts);
+24 -1
View File
@@ -261,7 +261,14 @@ impl DioxusRouterExt for Router {
} }
fn connect_hot_reload(self) -> Self { 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 .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
View File
@@ -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")))] #[cfg(not(all(debug_assertions, feature = "hot-reload", feature = "ssr")))]
{ {
warp::path("_dioxus/hot_reload") warp::path!("_dioxus" / "hot_reload")
.and(warp::ws()) .and(warp::ws())
.map(warp::reply) .map(warp::reply)
.map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND)); .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"))] #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
{ {
use crate::hot_reload::HotReloadState; use crate::hot_reload::HotReloadState;
use futures_util::sink::SinkExt;
use futures_util::StreamExt;
use warp::ws::Message;
let state = HotReloadState::default(); let state = HotReloadState::default();
warp::path("_dioxus") warp::path!("_dioxus" / "hot_reload")
.and(warp::path("hot_reload"))
.and(warp::ws()) .and(warp::ws())
.and(warp::any().map(move || state.clone())) .and(warp::any().map(move || state.clone()))
.map(move |ws: warp::ws::Ws, state: HotReloadState| { .map(move |ws: warp::ws::Ws, state: HotReloadState| {
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
ws.on_upgrade(move |mut websocket| { ws.on_upgrade(move |mut websocket| {
async move { async move {
use futures_util::sink::SinkExt;
use futures_util::StreamExt;
use warp::ws::Message;
println!("🔥 Hot Reload WebSocket connected"); println!("🔥 Hot Reload WebSocket connected");
{ {
// update any rsx calls that changed before the 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
View File
@@ -25,9 +25,7 @@ impl Default for SSRState {
impl SSRState { impl SSRState {
/// Render the application to HTML. /// Render the application to HTML.
pub fn render<P: 'static + Clone + serde::Serialize>(&self, cfg: &ServeConfig<P>) -> String { pub fn render<P: 'static + Clone + serde::Serialize>(&self, cfg: &ServeConfig<P>) -> String {
let ServeConfig { let ServeConfig { app, props, .. } = cfg;
app, props, ..
} = cfg;
let mut vdom = VirtualDom::new_with_props(*app, props.clone()); let mut vdom = VirtualDom::new_with_props(*app, props.clone());
@@ -55,6 +53,41 @@ impl SSRState {
// serialize the props // serialize the props
let _ = crate::props_html::serialize_props::encode_in_element(&cfg.props, &mut html); 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 += &index.post_main;
html html