Skip to content

Commit e911ec5

Browse files
committed
use spawn_blocking for cpu intensive template rendering
1 parent ef6dd16 commit e911ec5

File tree

2 files changed

+53
-7
lines changed

2 files changed

+53
-7
lines changed

src/web/crate_details.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ struct CrateDetailsPage {
302302

303303
impl_axum_webpage! {
304304
CrateDetailsPage = "crate/details.html",
305+
cpu_intensive_rendering = true,
305306
}
306307

307308
#[derive(Deserialize, Clone, Debug)]

src/web/page/web_page.rs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use http::header::CONTENT_LENGTH;
1414
use iron::{headers::ContentType, response::Response, status::Status, IronResult, Request};
1515
use serde::Serialize;
1616
use std::{borrow::Cow, sync::Arc};
17-
use tera::{Context, Tera};
17+
use tera::Context;
18+
use tokio::task::spawn_blocking;
1819

1920
/// When making using a custom status, use a closure that coerces to a `fn(&Self) -> Status`
2021
#[macro_export]
@@ -48,11 +49,28 @@ macro_rules! impl_webpage {
4849

4950
#[macro_export]
5051
macro_rules! impl_axum_webpage {
51-
($page:ty = $template:literal $(, status = $status:expr)? $(, content_type = $content_type:expr)? $(,)?) => {
52-
$crate::impl_axum_webpage!($page = |_| ::std::borrow::Cow::Borrowed($template) $(, status = $status)? $(, content_type = $content_type)?);
52+
(
53+
$page:ty = $template:literal
54+
$(, status = $status:expr)?
55+
$(, content_type = $content_type:expr)?
56+
$(, cpu_intensive_rendering = $cpu_intensive_rendering:expr)?
57+
$(,)?
58+
) => {
59+
$crate::impl_axum_webpage!(
60+
$page = |_| ::std::borrow::Cow::Borrowed($template)
61+
$(, status = $status)?
62+
$(, content_type = $content_type)?
63+
$(, cpu_intensive_rendering = $cpu_intensive_rendering )?
64+
);
5365
};
5466

55-
($page:ty = $template:expr $(, status = $status:expr)? $(, content_type = $content_type:expr)? $(,)?) => {
67+
(
68+
$page:ty = $template:expr
69+
$(, status = $status:expr)?
70+
$(, content_type = $content_type:expr)?
71+
$(, cpu_intensive_rendering = $cpu_intensive_rendering:expr)?
72+
$(,)?
73+
) => {
5674
impl axum::response::IntoResponse for $page
5775
{
5876
fn into_response(self) -> ::axum::response::Response {
@@ -63,6 +81,12 @@ macro_rules! impl_axum_webpage {
6381
ct = $content_type;
6482
)?
6583

84+
#[allow(unused_mut, unused_assignments)]
85+
let mut cpu_intensive_rendering = false;
86+
$(
87+
cpu_intensive_rendering = $cpu_intensive_rendering;
88+
)?
89+
6690
let mut response = ::axum::http::Response::builder()
6791
.header(::axum::http::header::CONTENT_TYPE, ct)
6892
$(
@@ -83,6 +107,7 @@ macro_rules! impl_axum_webpage {
83107
let template: fn(&Self) -> ::std::borrow::Cow<'static, str> = $template;
84108
template(&self).to_string()
85109
},
110+
cpu_intensive_rendering,
86111
});
87112
response
88113
}
@@ -164,14 +189,20 @@ pub trait WebPage: Serialize + Sized {
164189
pub(crate) struct DelayedTemplateRender {
165190
pub template: String,
166191
pub context: Context,
192+
pub cpu_intensive_rendering: bool,
167193
}
168194

169-
fn render_response(mut response: AxumResponse, templates: &Tera, csp_nonce: &str) -> AxumResponse {
195+
fn render_response(
196+
mut response: AxumResponse,
197+
templates: Arc<TemplateData>,
198+
csp_nonce: String,
199+
) -> AxumResponse {
170200
if let Some(render) = response.extensions().get::<DelayedTemplateRender>() {
201+
let tera = &templates.templates;
171202
let mut context = render.context.clone();
172203
context.insert("csp_nonce", &csp_nonce);
173204

174-
let rendered = match templates.render(&render.template, &context) {
205+
let rendered = match tera.render(&render.template, &context) {
175206
Ok(content) => content,
176207
Err(err) => {
177208
if response.status().is_server_error() {
@@ -214,5 +245,19 @@ pub(crate) async fn render_templates_middleware<B>(
214245
.nonce()
215246
.to_owned();
216247

217-
render_response(next.run(req).await, &templates.templates, &csp_nonce)
248+
let response = next.run(req).await;
249+
250+
let cpu_intensive_rendering: bool = response
251+
.extensions()
252+
.get::<DelayedTemplateRender>()
253+
.map(|render| render.cpu_intensive_rendering)
254+
.unwrap_or(false);
255+
256+
if cpu_intensive_rendering {
257+
spawn_blocking(|| render_response(response, templates, csp_nonce))
258+
.await
259+
.expect("tokio task join error when rendering templates")
260+
} else {
261+
render_response(response, templates, csp_nonce)
262+
}
218263
}

0 commit comments

Comments
 (0)