web: Add zstd compression feature for HTML templates

grcov
Manos Pitsidianakis 2023-04-24 19:24:14 +03:00
parent 7246815df8
commit 9ceb11c761
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
9 changed files with 526 additions and 2 deletions

326
Cargo.lock generated
View File

@ -383,6 +383,12 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]]
name = "base64"
version = "0.21.0"
@ -470,6 +476,71 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
[[package]]
name = "build-info"
version = "0.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2915568baa77be2c38f1d7594d1d7ceecc1012ab1ffdf8ddc9d0210596ee23e"
dependencies = [
"build-info-common",
"build-info-proc",
"once_cell",
]
[[package]]
name = "build-info-build"
version = "0.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562a39027834eae2be534ab5079440cfaa4ce9ca769646e72b1c734236205f16"
dependencies = [
"anyhow",
"base64 0.20.0",
"bincode",
"build-info-common",
"cargo_metadata",
"chrono",
"git2",
"glob",
"once_cell",
"pretty_assertions",
"rustc_version",
"serde_json",
"xz2",
]
[[package]]
name = "build-info-common"
version = "0.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "401a9dafdaa6855a7f66fb38f11627f606bce40fb782d124d4d058aec51188de"
dependencies = [
"chrono",
"derive_more",
"semver",
"serde",
]
[[package]]
name = "build-info-proc"
version = "0.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2c40d327e9d58c805c3154cfc7ecc5cb917c1e214fb3d37367540b4b7a9fcf"
dependencies = [
"anyhow",
"base64 0.20.0",
"bincode",
"build-info-common",
"chrono",
"num-bigint",
"num-traits",
"proc-macro-error",
"proc-macro2",
"quote",
"serde_json",
"syn 1.0.109",
"xz2",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
@ -488,11 +559,46 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "camino"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2"
dependencies = [
"serde",
]
[[package]]
name = "cargo-platform"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
dependencies = [
"jobserver",
]
[[package]]
name = "cfg-if"
@ -601,6 +707,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "cookie"
version = "0.17.0"
@ -681,6 +793,16 @@ dependencies = [
"subtle",
]
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]]
name = "cxx"
version = "1.0.94"
@ -731,6 +853,25 @@ version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn 1.0.109",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "difflib"
version = "0.4.0"
@ -1146,6 +1287,25 @@ dependencies = [
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "git2"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
dependencies = [
"bitflags",
"libc",
"libgit2-sys",
"log",
"url",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "h2"
version = "0.3.16"
@ -1442,6 +1602,15 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "jobserver"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
dependencies = [
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.61"
@ -1479,6 +1648,18 @@ version = "0.2.141"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
[[package]]
name = "libgit2-sys"
version = "0.14.2+1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4"
dependencies = [
"cc",
"libc",
"libz-sys",
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.7.4"
@ -1500,6 +1681,18 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "link-cplusplus"
version = "1.0.8"
@ -1534,6 +1727,17 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "mailin"
version = "0.6.1"
@ -1643,6 +1847,8 @@ dependencies = [
"axum-extra",
"axum-login",
"axum-sessions",
"build-info",
"build-info-build",
"chrono",
"dyn-clone",
"eyre",
@ -1658,6 +1864,7 @@ dependencies = [
"tokio",
"tower-http 0.3.5",
"tower-service",
"zstd",
]
[[package]]
@ -1893,6 +2100,17 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
@ -1978,6 +2196,15 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "parking"
version = "2.1.0"
@ -2104,6 +2331,42 @@ dependencies = [
"termtree",
]
[[package]]
name = "pretty_assertions"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
dependencies = [
"ctor",
"diff",
"output_vt100",
"yansi",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.56"
@ -2274,6 +2537,15 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.37.11"
@ -2411,6 +2683,15 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.160"
@ -3457,8 +3738,53 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db9fefe62d5969721e2cfc529e6a760901cc0da422b6d67e7bfd18e69490dba6"
[[package]]
name = "xz2"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
dependencies = [
"lzma-sys",
]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zeroize"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
[[package]]
name = "zstd"
version = "0.12.3+zstd.1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "6.0.5+zstd.1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b"
dependencies = [
"libc",
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.8+zstd.1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c"
dependencies = [
"cc",
"libc",
"pkg-config",
]

1
web/.gitignore vendored 100644
View File

@ -0,0 +1 @@
src/minijinja_utils/compressed.data

View File

@ -14,11 +14,16 @@ categories = ["email"]
name = "mpot-web"
path = "src/main.rs"
[features]
default = ["zstd"]
zstd = ["dep:zstd"]
[dependencies]
axum = { version = "^0.6" }
axum-extra = { version = "^0.7", features = ["typed-routing"] }
axum-login = { version = "^0.5" }
axum-sessions = { version = "^0.5" }
build-info = { version = "0.0.29" }
chrono = { version = "^0.4" }
dyn-clone = { version = "^1" }
eyre = { version = "0.6" }
@ -34,3 +39,8 @@ tempfile = { version = "^3.5" }
tokio = { version = "1", features = ["full"] }
tower-http = { version = "^0.3" }
tower-service = { version = "^0.3" }
zstd = { version = "0.12.3", optional = true, default-features = false }
[build-dependencies]
build-info-build = { version = "0.0.29" }
zstd = { version = "0.12.3", optional = true, default-features = false }

View File

@ -4,6 +4,10 @@
cargo run --bin mpot-web -- /path/to/conf.toml
```
By default on release builds templates are compressed with `zstd` and bundled in the binary.
You can disable this behavior by disabling the `zstd` feature: `cargo build --release --no-default-features`
## Configuration
By default, the server listens on `0.0.0.0:3000`.

109
web/build.rs 100644
View File

@ -0,0 +1,109 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
fn commit_sha() {
build_info_build::build_script();
if let Ok(s) = std::fs::read_to_string(".cargo_vcs_info.json") {
const KEY: &str = "\"sha1\":";
fn find_tail<'str>(str: &'str str, tok: &str) -> Option<&'str str> {
let i = str.find(tok)?;
Some(&str[(i + tok.len())..])
}
if let Some(mut tail) = find_tail(&s, KEY) {
while !tail.starts_with('"') && !tail.is_empty() {
tail = &tail[1..];
}
if !tail.is_empty() {
// skip "
tail = &tail[1..];
if let Some(end) = find_tail(tail, "\"") {
let end = tail.len() - end.len() - 1;
println!("cargo:rustc-env=PACKAGE_GIT_SHA={}", &tail[..end]);
}
}
}
}
}
#[cfg(feature = "zstd")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Embed HTML templates as zstd compressed byte slices into binary.
// [tag:embed_templates]
use std::{
fs::{create_dir_all, read_dir, OpenOptions},
io::{Read, Write},
path::PathBuf,
};
create_dir_all("./src/minijinja_utils")?;
let mut compressed = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open("./src/minijinja_utils/compressed.data")?;
println!("cargo:rerun-if-changed=./src/templates");
println!("cargo:rerun-if-changed=./src/minijinja_utils/compressed.rs");
let mut templates: Vec<(String, PathBuf)> = vec![];
let root_prefix: PathBuf = "./src/templates/".into();
let mut dirs: Vec<PathBuf> = vec!["./src/templates/".into()];
while let Some(dir) = dirs.pop() {
for entry in read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
dirs.push(path);
} else if path.extension().map(|s| s == "html").unwrap_or(false) {
templates.push((path.strip_prefix(&root_prefix)?.display().to_string(), path));
}
}
}
for (name, template_path) in templates {
let mut templ = OpenOptions::new()
.write(false)
.create(false)
.read(true)
.open(&template_path)?;
let mut templ_bytes = vec![];
let mut compressed_bytes = vec![];
let mut enc = zstd::stream::write::Encoder::new(&mut compressed_bytes, 21)?;
enc.include_checksum(true)?;
templ.read_to_end(&mut templ_bytes)?;
enc.write_all(&templ_bytes)?;
enc.finish()?;
compressed.write_all(b"(\"")?;
compressed.write_all(name.as_bytes())?;
compressed.write_all(b"\",&")?;
compressed.write_all(format!("{:?}", compressed_bytes).as_bytes())?;
compressed.write_all(b"),")?;
}
commit_sha();
Ok(())
}
#[cfg(not(feature = "zstd"))]
fn main() {
commit_sha();
}

View File

@ -77,7 +77,7 @@ pub use http::{Request, Response, StatusCode};
pub use mailpot::{models::DbVal, rusqlite::OptionalExtension, *};
use minijinja::{
value::{Object, Value},
Environment, Error, Source,
Environment, Error,
};
use tokio::sync::RwLock;
@ -199,3 +199,26 @@ mod auth_impls {
}
}
}
const fn _get_package_git_sha() -> Option<&'static str> {
option_env!("PACKAGE_GIT_SHA")
}
const _PACKAGE_COMMIT_SHA: Option<&str> = _get_package_git_sha();
pub fn get_git_sha() -> std::borrow::Cow<'static, str> {
if let Some(r) = _PACKAGE_COMMIT_SHA {
return r.into();
}
build_info::build_info!(fn build_info);
let info = build_info();
info.version_control
.as_ref()
.and_then(|v| v.git())
.map(|g| g.commit_short_id.clone())
.map_or_else(|| "<unknown>".into(), |v| v.into())
}
pub const VERSION_INFO: &str = build_info::format!("{}", $.crate_info.version);
pub const BUILD_INFO: &str = build_info::format!("{}\t{}\t{}\t{}", $.crate_info.version, $.compiler, $.timestamp, $.crate_info.enabled_features);
pub const CLI_INFO: &str = build_info::format!("{} Version: {}\nAuthors: {}\nLicense: AGPL version 3 or later\nCompiler: {}\nBuild-Date: {}\nEnabled-features: {}", $.crate_info.name, $.crate_info.version, $.crate_info.authors, $.compiler, $.timestamp, $.crate_info.enabled_features);

View File

@ -29,6 +29,12 @@ async fn main() {
let config_path = std::env::args()
.nth(1)
.expect("Expected configuration file path as first argument.");
if ["-v", "--version", "info"].contains(&config_path.as_str()) {
println!("{}", crate::get_git_sha());
println!("{CLI_INFO}");
return;
}
let conf = Configuration::from_file(config_path).unwrap();
let store = MemoryStore::new();

View File

@ -21,6 +21,10 @@
use super::*;
#[cfg(feature = "zstd")]
#[cfg(not(debug_assertions))]
mod compressed;
lazy_static::lazy_static! {
pub static ref TEMPLATES: Environment<'static> = {
let mut env = Environment::new();
@ -43,7 +47,28 @@ lazy_static::lazy_static! {
list_post_path
);
add!(filter pluralize);
env.set_source(Source::from_path("web/src/templates/"));
#[cfg(not(feature = "zstd"))]
#[cfg(debug_assertions)]
env.set_source(minijinja::Source::from_path("web/src/templates/"));
#[cfg(feature = "zstd")]
#[cfg(debug_assertions)]
env.set_source(minijinja::Source::from_path("web/src/templates/"));
#[cfg(not(feature = "zstd"))]
#[cfg(not(debug_assertions))]
env.set_source(minijinja::Source::from_path("web/src/templates/"));
#[cfg(feature = "zstd")]
#[cfg(not(debug_assertions))]
{
// Load compressed templates. They are constructed in build.rs. See
// [ref:embed_templates]
let mut source = minijinja::Source::new();
for (name, bytes) in compressed::COMPRESSED {
let mut de_bytes = vec![];
zstd::stream::copy_decode(*bytes,&mut de_bytes).unwrap();
source.add_template(*name, String::from_utf8(de_bytes).unwrap()).unwrap();
}
env.set_source(source);
}
env
};

View File

@ -0,0 +1,20 @@
/*
* This file is part of mailpot
*
* Copyright 2020 - Manos Pitsidianakis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
pub const COMPRESSED: &[(&str, &[u8])] = &[include!("compressed.data")];