attempt to reconnect to server after sever disconnects in debug mode
This commit is contained in:
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>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in new issue
Block a user