@@ -26,6 +26,7 @@ const INDEX_TEMPLATE_NAME: &str = "index_html";
2626const PATH_PREFIX_CRATES : & str = "/crates/" ;
2727
2828type TemplateEnvFut = Shared < BoxFuture < ' static , minijinja:: Environment < ' static > > > ;
29+ type TemplateCache = moka:: future:: Cache < Cow < ' static , str > , String > ;
2930
3031/// Initialize [`minijinja::Environment`] given the path to the index.html file. This should
3132/// only be done once as it will load said file from persistent storage.
@@ -42,8 +43,16 @@ async fn init_template_env(
4243 env
4344}
4445
46+ /// Initialize the [`moka::future::Cache`] used to cache the rendered HTML.
47+ fn init_html_cache ( max_capacity : u64 ) -> TemplateCache {
48+ moka:: future:: CacheBuilder :: new ( max_capacity)
49+ . name ( "rendered_index_html" )
50+ . build ( )
51+ }
52+
4553pub async fn serve_html ( state : AppState , request : Request , next : Next ) -> Response {
4654 static TEMPLATE_ENV : OnceLock < TemplateEnvFut > = OnceLock :: new ( ) ;
55+ static RENDERED_HTML_CACHE : OnceLock < TemplateCache > = OnceLock :: new ( ) ;
4756
4857 let path = & request. uri ( ) . path ( ) ;
4958 // The "/git/" prefix is only used in development (when within a docker container)
@@ -64,8 +73,7 @@ pub async fn serve_html(state: AppState, request: Request, next: Next) -> Respon
6473 // Come up with an Open Graph image URL. In case a crate page is requested,
6574 // we use the crate's name and the OG image base URL from config to
6675 // generate one, otherwise we use the fallback image.
67-
68- let og_image_url: Cow < ' _ , _ > = ' og: {
76+ let og_image_url = ' og: {
6977 if let Some ( suffix) = path. strip_prefix ( PATH_PREFIX_CRATES ) {
7078 let len = suffix. find ( '/' ) . unwrap_or ( suffix. len ( ) ) ;
7179 let krate = & suffix[ ..len] ;
@@ -81,26 +89,32 @@ pub async fn serve_html(state: AppState, request: Request, next: Next) -> Respon
8189 OG_IMAGE_FALLBACK_URL . into ( )
8290 } ;
8391
84- // `OnceLock::get_or_init` blocks as long as its intializer is running in another thread.
85- // Note that this won't take long, as the constructed Futures are not awaited
86- // during initialization.
87- let template_env = TEMPLATE_ENV . get_or_init ( || {
88- // At this point we can safely assume `state.config.index_html_template_path` is `Some`,
89- // as this middleware won't be executed otherwise; see `crate::middleware::apply_axum_middleware`.
90- init_template_env ( state. config . index_html_template_path . clone ( ) . unwrap ( ) )
91- . boxed ( )
92- . shared ( )
93- } ) ;
94-
95- // TODO use moka caching here with og_image_url as key and the rendered html as value
96-
97- // Render the HTML given the OG image URL
98- let env = template_env. clone ( ) . await ;
99- let html = env
100- . get_template ( INDEX_TEMPLATE_NAME )
101- . unwrap ( )
102- . render ( context ! { og_image_url} )
103- . expect ( "Error rendering index" ) ;
92+ // Fetch the HTML from cache given `og_image_url` as key or render it
93+ let html = RENDERED_HTML_CACHE
94+ . get_or_init ( || init_html_cache ( state. config . html_render_cache_max_capacity ) )
95+ . get_with_by_ref ( & og_image_url, async {
96+ // `OnceLock::get_or_init` blocks as long as its intializer is running in another thread.
97+ // Note that this won't take long, as the constructed Futures are not awaited
98+ // during initialization.
99+ let template_env = TEMPLATE_ENV . get_or_init ( || {
100+ // At this point we can safely assume `state.config.index_html_template_path` is `Some`,
101+ // as this middleware won't be executed otherwise; see `crate::middleware::apply_axum_middleware`.
102+ init_template_env ( state. config . index_html_template_path . clone ( ) . unwrap ( ) )
103+ . boxed ( )
104+ . shared ( )
105+ } ) ;
106+
107+ // Render the HTML given the OG image URL
108+ let env = template_env. clone ( ) . await ;
109+ let html = env
110+ . get_template ( INDEX_TEMPLATE_NAME )
111+ . unwrap ( )
112+ . render ( context ! { og_image_url} )
113+ . expect ( "Error rendering index" ) ;
114+
115+ html
116+ } )
117+ . await ;
104118
105119 // Serve static Ember page to bootstrap the frontend
106120 Response :: builder ( )
0 commit comments