Skip to content

Commit c85b6bc

Browse files
authored
Merge pull request #1554 from rust-lang/embed-resources
Embed static resources and templates into the `site` binary in release mode
2 parents c544284 + 5ee1b16 commit c85b6bc

File tree

7 files changed

+111
-68
lines changed

7 files changed

+111
-68
lines changed

Cargo.lock

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,5 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
3434

3535
COPY --from=build /target/release/postgres-to-sqlite /usr/local/bin/rustc-perf-postgres-to-sqlite
3636
COPY --from=build /target/release/site /usr/local/bin/rustc-perf-site
37-
COPY --from=build site/static /site/static
38-
COPY --from=build site/templates /site/templates
3937

4038
CMD rustc-perf-site

site/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ mime = "0.3"
4646
prometheus = "0.13"
4747
uuid = { version = "1.3.0", features = ["v4"] }
4848
tera = "1.18"
49+
rust-embed = { version = "6.6.0", features = ["include-exclude"] }
4950

5051
[target.'cfg(unix)'.dependencies]
5152
jemallocator = "0.5"

site/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ mod average;
1919
mod comparison;
2020
mod interpolate;
2121
mod request_handlers;
22+
mod resources;
2223
mod selector;
2324
mod self_profile;
24-
mod templates;

site/src/resources.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use tera::Tera;
2+
use tokio::sync::RwLock;
3+
4+
use rust_embed::RustEmbed;
5+
6+
/// Static files and templates are embedded into the binary (in release mode) or hot-reloaded
7+
/// from the `site` directory (in debug mode).
8+
#[derive(RustEmbed)]
9+
#[folder = "static/"]
10+
#[include = "*.js"]
11+
#[include = "*.css"]
12+
#[include = "*.svg"]
13+
#[include = "*.png"]
14+
struct StaticAssets;
15+
16+
#[derive(RustEmbed)]
17+
#[folder = "templates/"]
18+
#[include = "*.html"]
19+
struct TemplateAssets;
20+
21+
pub struct ResourceResolver {
22+
tera: RwLock<Tera>,
23+
}
24+
25+
impl ResourceResolver {
26+
pub fn new() -> anyhow::Result<Self> {
27+
let tera = load_templates()?;
28+
29+
Ok(Self {
30+
tera: RwLock::new(tera),
31+
})
32+
}
33+
34+
pub fn get_static_asset(&self, path: &str) -> Option<Vec<u8>> {
35+
StaticAssets::get(path).map(|file| file.data.to_vec())
36+
}
37+
38+
pub async fn get_template(&self, path: &str) -> anyhow::Result<Vec<u8>> {
39+
// Live-reload the template if we're in debug mode
40+
#[cfg(debug_assertions)]
41+
{
42+
*self.tera.write().await = load_templates()?;
43+
}
44+
45+
let context = tera::Context::new();
46+
let rendered = self.tera.read().await.render(path, &context)?;
47+
Ok(rendered.into_bytes())
48+
}
49+
}
50+
51+
fn load_templates() -> anyhow::Result<Tera> {
52+
let templates = TemplateAssets::iter().map(|path| {
53+
(
54+
path.to_string(),
55+
String::from_utf8(TemplateAssets::get(&path).unwrap().data.to_vec()).unwrap(),
56+
)
57+
});
58+
let mut tera = Tera::default();
59+
tera.add_raw_templates(templates)?;
60+
Ok(tera)
61+
}

site/src/server.rs

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use brotli::enc::BrotliEncoderParams;
22
use brotli::BrotliCompress;
33
use std::collections::HashMap;
44
use std::net::SocketAddr;
5-
use std::path::{Path, PathBuf};
5+
use std::path::Path;
66
use std::str::FromStr;
77
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
88
use std::sync::Arc;
99
use std::time::Instant;
10-
use std::{fmt, fs, str};
10+
use std::{fmt, str};
1111

1212
use futures::{future::FutureExt, stream::StreamExt};
1313
use headers::{Authorization, CacheControl, ContentType, ETag, Header, HeaderMapExt, IfNoneMatch};
@@ -28,7 +28,7 @@ pub use crate::api::{
2828
use crate::db::{self, ArtifactId};
2929
use crate::load::{Config, SiteCtxt};
3030
use crate::request_handlers;
31-
use crate::templates::TemplateRenderer;
31+
use crate::resources::ResourceResolver;
3232

3333
pub type Request = http::Request<hyper::Body>;
3434
pub type Response = http::Response<hyper::Body>;
@@ -566,23 +566,12 @@ where
566566

567567
lazy_static::lazy_static! {
568568
static ref VERSION_UUID: Uuid = Uuid::new_v4(); // random UUID used as ETag for cache revalidation
569-
static ref TEMPLATES: TemplateRenderer = {
570-
let livereload = std::env::var("LIVERELOAD").is_ok();
571-
TemplateRenderer::new(PathBuf::from("site/templates"), livereload).unwrap()
572-
};
569+
static ref TEMPLATES: ResourceResolver = ResourceResolver::new().expect("Cannot load resources");
573570
}
574571

575572
/// Handle the case where the path is to a static file
576573
async fn handle_fs_path(req: &Request, path: &str) -> Option<http::Response<hyper::Body>> {
577-
let fs_path = format!(
578-
"site/static{}",
579-
match path {
580-
"" | "/" => "/index.html",
581-
_ => path,
582-
}
583-
);
584-
585-
if fs_path.contains("./") | fs_path.contains("../") {
574+
if path.contains("./") | path.contains("../") {
586575
return Some(not_found());
587576
}
588577

@@ -598,31 +587,26 @@ async fn handle_fs_path(req: &Request, path: &str) -> Option<http::Response<hype
598587
}
599588
}
600589

601-
async fn render_page(path: &str) -> Vec<u8> {
590+
async fn resolve_template(path: &str) -> Vec<u8> {
602591
TEMPLATES
603-
.render(&format!("pages/{}", path))
592+
.get_template(&format!("pages/{}", path))
604593
.await
605594
.unwrap()
606-
.into_bytes()
607595
}
608596

597+
let relative_path = path.trim_start_matches("/");
609598
let source = match path {
610-
"" | "/" | "/index.html" => render_page("graphs.html").await,
599+
"" | "/" | "/index.html" => resolve_template("graphs.html").await,
611600
"/bootstrap.html"
612601
| "/compare.html"
613602
| "/dashboard.html"
614603
| "/detailed-query.html"
615604
| "/help.html"
616-
| "/status.html" => render_page(path.trim_start_matches("/")).await,
617-
_ => {
618-
if !Path::new(&fs_path).is_file() {
619-
return None;
620-
}
621-
fs::read(&fs_path).unwrap()
622-
}
605+
| "/status.html" => resolve_template(relative_path).await,
606+
_ => TEMPLATES.get_static_asset(relative_path)?,
623607
};
624608

625-
let p = Path::new(&fs_path);
609+
let p = Path::new(&path);
626610
match p.extension().and_then(|x| x.to_str()) {
627611
Some("html") => response = response.header_typed(ContentType::html()),
628612
Some("png") => response = response.header_typed(ContentType::png()),

site/src/templates.rs

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)