Fix fullstack render server context (#2139)
* Fix fullstack render server context * only set the server context while polling futures --------- Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
This commit is contained in:
1 parent
e464294c66
commit
fb396b0448
3 files changed
+26
-35
No files matched your search
@@ -60,8 +60,6 @@ use axum::{
|
|||||||
extract::State,
|
extract::State,
|
||||||
http::{Request, Response, StatusCode},
|
http::{Request, Response, StatusCode},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::{get, post},
|
|
||||||
Router,
|
|
||||||
};
|
};
|
||||||
use dioxus_lib::prelude::VirtualDom;
|
use dioxus_lib::prelude::VirtualDom;
|
||||||
use futures_util::Future;
|
use futures_util::Future;
|
||||||
@@ -69,9 +67,7 @@ use http::header::*;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{
|
use crate::prelude::*;
|
||||||
prelude::*, render::SSRState, serve_config::ServeConfig, server_context::DioxusServerContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A extension trait with utilities for integrating Dioxus with your Axum router.
|
/// A extension trait with utilities for integrating Dioxus with your Axum router.
|
||||||
pub trait DioxusRouterExt<S> {
|
pub trait DioxusRouterExt<S> {
|
||||||
@@ -508,8 +504,8 @@ async fn handle_server_fns_inner(
|
|||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
let referrer = req.headers().get(REFERER).cloned();
|
let referrer = req.headers().get(REFERER).cloned();
|
||||||
|
|
||||||
// actually run the server fn
|
// actually run the server fn (which may use the server context)
|
||||||
let mut res = service.run(req).await;
|
let mut res = ProvideServerContext::new(service.run(req), server_context.clone()).await;
|
||||||
|
|
||||||
// it it accepts text/html (i.e., is a plain form post) and doesn't already have a
|
// it it accepts text/html (i.e., is a plain form post) and doesn't already have a
|
||||||
// Location set, then redirect to Referer
|
// Location set, then redirect to Referer
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
//! A shared pool of renderers for efficient server side rendering.
|
//! A shared pool of renderers for efficient server side rendering.
|
||||||
use crate::render::dioxus_core::NoOpMutations;
|
use crate::{render::dioxus_core::NoOpMutations, server_context::with_server_context};
|
||||||
use crate::server_context::SERVER_CONTEXT;
|
|
||||||
use dioxus_lib::prelude::VirtualDom;
|
|
||||||
use dioxus_ssr::{
|
use dioxus_ssr::{
|
||||||
incremental::{IncrementalRendererConfig, RenderFreshness, WrapBody},
|
incremental::{RenderFreshness, WrapBody},
|
||||||
Renderer,
|
Renderer,
|
||||||
};
|
};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
@@ -53,7 +51,7 @@ impl SsrRendererPool {
|
|||||||
};
|
};
|
||||||
match self {
|
match self {
|
||||||
Self::Renderer(pool) => {
|
Self::Renderer(pool) => {
|
||||||
let server_context = Box::new(server_context.clone());
|
let server_context = server_context.clone();
|
||||||
let mut renderer = pool.write().unwrap().pop().unwrap_or_else(pre_renderer);
|
let mut renderer = pool.write().unwrap().pop().unwrap_or_else(pre_renderer);
|
||||||
|
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
@@ -61,15 +59,13 @@ impl SsrRendererPool {
|
|||||||
spawn_platform(move || async move {
|
spawn_platform(move || async move {
|
||||||
let mut vdom = virtual_dom_factory();
|
let mut vdom = virtual_dom_factory();
|
||||||
let mut to = WriteBuffer { buffer: Vec::new() };
|
let mut to = WriteBuffer { buffer: Vec::new() };
|
||||||
// before polling the future, we need to set the context
|
|
||||||
let prev_context = SERVER_CONTEXT.with(|ctx| ctx.replace(server_context));
|
|
||||||
// poll the future, which may call server_context()
|
// poll the future, which may call server_context()
|
||||||
tracing::info!("Rebuilding vdom");
|
tracing::info!("Rebuilding vdom");
|
||||||
block_in_place(|| vdom.rebuild(&mut NoOpMutations));
|
with_server_context(server_context.clone(), || {
|
||||||
vdom.wait_for_suspense().await;
|
block_in_place(|| vdom.rebuild(&mut NoOpMutations));
|
||||||
|
});
|
||||||
|
ProvideServerContext::new(vdom.wait_for_suspense(), server_context).await;
|
||||||
tracing::info!("Suspense resolved");
|
tracing::info!("Suspense resolved");
|
||||||
// after polling the future, we need to restore the context
|
|
||||||
SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context));
|
|
||||||
|
|
||||||
if let Err(err) = wrapper.render_before_body(&mut *to) {
|
if let Err(err) = wrapper.render_before_body(&mut *to) {
|
||||||
let _ = tx.send(Err(err));
|
let _ = tx.send(Err(err));
|
||||||
@@ -120,16 +116,17 @@ impl SsrRendererPool {
|
|||||||
&mut *to,
|
&mut *to,
|
||||||
|vdom| {
|
|vdom| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// before polling the future, we need to set the context
|
|
||||||
let prev_context = SERVER_CONTEXT
|
|
||||||
.with(|ctx| ctx.replace(Box::new(server_context)));
|
|
||||||
// poll the future, which may call server_context()
|
// poll the future, which may call server_context()
|
||||||
tracing::info!("Rebuilding vdom");
|
tracing::info!("Rebuilding vdom");
|
||||||
block_in_place(|| vdom.rebuild(&mut NoOpMutations));
|
with_server_context(server_context.clone(), || {
|
||||||
vdom.wait_for_suspense().await;
|
block_in_place(|| vdom.rebuild(&mut NoOpMutations));
|
||||||
|
});
|
||||||
|
ProvideServerContext::new(
|
||||||
|
vdom.wait_for_suspense(),
|
||||||
|
server_context,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
tracing::info!("Suspense resolved");
|
tracing::info!("Suspense resolved");
|
||||||
// after polling the future, we need to restore the context
|
|
||||||
SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context));
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
&wrapper,
|
&wrapper,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ impl Default for DioxusServerContext {
|
|||||||
mod server_fn_impl {
|
mod server_fn_impl {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::sync::LockResult;
|
use std::sync::LockResult;
|
||||||
use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use std::sync::{PoisonError, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
use anymap::{any::Any, Map};
|
use anymap::{any::Any, Map};
|
||||||
type SendSyncAnyMap = Map<dyn Any + Send + Sync + 'static>;
|
type SendSyncAnyMap = Map<dyn Any + Send + Sync + 'static>;
|
||||||
@@ -159,22 +159,20 @@ pub async fn extract<E: FromServerContext<I>, I>() -> Result<E, E::Rejection> {
|
|||||||
E::from_request(&server_context()).await
|
E::from_request(&server_context()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn with_server_context<O>(
|
pub(crate) fn with_server_context<O>(context: DioxusServerContext, f: impl FnOnce() -> O) -> O {
|
||||||
context: Box<DioxusServerContext>,
|
|
||||||
f: impl FnOnce() -> O,
|
|
||||||
) -> (O, Box<DioxusServerContext>) {
|
|
||||||
// before polling the future, we need to set the context
|
// before polling the future, we need to set the context
|
||||||
let prev_context = SERVER_CONTEXT.with(|ctx| ctx.replace(context));
|
let prev_context = SERVER_CONTEXT.with(|ctx| ctx.replace(Box::new(context)));
|
||||||
// poll the future, which may call server_context()
|
// poll the future, which may call server_context()
|
||||||
let result = f();
|
let result = f();
|
||||||
// after polling the future, we need to restore the context
|
// after polling the future, we need to restore the context
|
||||||
(result, SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context)))
|
SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context));
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A future that provides the server context to the inner future
|
/// A future that provides the server context to the inner future
|
||||||
#[pin_project::pin_project]
|
#[pin_project::pin_project]
|
||||||
pub struct ProvideServerContext<F: std::future::Future> {
|
pub struct ProvideServerContext<F: std::future::Future> {
|
||||||
context: Option<Box<DioxusServerContext>>,
|
context: Option<DioxusServerContext>,
|
||||||
#[pin]
|
#[pin]
|
||||||
f: F,
|
f: F,
|
||||||
}
|
}
|
||||||
@@ -183,7 +181,7 @@ impl<F: std::future::Future> ProvideServerContext<F> {
|
|||||||
/// Create a new future that provides the server context to the inner future
|
/// Create a new future that provides the server context to the inner future
|
||||||
pub fn new(f: F, context: DioxusServerContext) -> Self {
|
pub fn new(f: F, context: DioxusServerContext) -> Self {
|
||||||
Self {
|
Self {
|
||||||
context: Some(Box::new(context)),
|
context: Some(context),
|
||||||
f,
|
f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +196,7 @@ impl<F: std::future::Future> std::future::Future for ProvideServerContext<F> {
|
|||||||
) -> std::task::Poll<Self::Output> {
|
) -> std::task::Poll<Self::Output> {
|
||||||
let this = self.project();
|
let this = self.project();
|
||||||
let context = this.context.take().unwrap();
|
let context = this.context.take().unwrap();
|
||||||
let (result, context) = with_server_context(context, || this.f.poll(cx));
|
let result = with_server_context(context.clone(), || this.f.poll(cx));
|
||||||
*this.context = Some(context);
|
*this.context = Some(context);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user