From 8c523414f007d54e0145ad9335a8932043a19f16 Mon Sep 17 00:00:00 2001 From: Vika Date: Tue, 25 Oct 2022 22:47:35 +0300 Subject: kittybox-frontend-renderer: gzip static assets --- kittybox-rs/Cargo.lock | 54 ++++++++++++++++++++++++ kittybox-rs/templates/Cargo.toml | 4 ++ kittybox-rs/templates/build.rs | 88 ++++++++++++++++++++++++++++++++++------ kittybox-rs/templates/src/lib.rs | 21 +++++++--- 4 files changed, 149 insertions(+), 18 deletions(-) diff --git a/kittybox-rs/Cargo.lock b/kittybox-rs/Cargo.lock index e8e0bf3..9e7cba9 100644 --- a/kittybox-rs/Cargo.lock +++ b/kittybox-rs/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" version = "0.7.18" @@ -1297,10 +1303,12 @@ dependencies = [ "include_dir", "kittybox-indieauth", "kittybox-util", + "libflate", "markup", "microformats", "rand 0.8.5", "serde_json", + "walkdir", ] [[package]] @@ -1354,6 +1362,26 @@ version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +[[package]] +name = "libflate" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05605ab2bce11bcfc0e9c635ff29ef8b2ea83f29be257ee7d730cac3ee373093" +dependencies = [ + "adler32", + "crc32fast", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a734c0493409afcd49deee13c006a04e3586b9761a03543c6272c9c51f2f5a" +dependencies = [ + "rle-decode-fast", +] + [[package]] name = "listenfd" version = "0.5.0" @@ -2426,6 +2454,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "rustc_version" version = "0.4.0" @@ -2471,6 +2505,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.20" @@ -3341,6 +3384,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" diff --git a/kittybox-rs/templates/Cargo.toml b/kittybox-rs/templates/Cargo.toml index ffdfc25..7090046 100644 --- a/kittybox-rs/templates/Cargo.toml +++ b/kittybox-rs/templates/Cargo.toml @@ -5,6 +5,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[build-dependencies] +libflate = "^1.2.0" +walkdir = "^2.3.2" + [dev-dependencies] faker_rand = "^0.1.1" rand = "^0.8.5" diff --git a/kittybox-rs/templates/build.rs b/kittybox-rs/templates/build.rs index 1140060..ccd5b19 100644 --- a/kittybox-rs/templates/build.rs +++ b/kittybox-rs/templates/build.rs @@ -1,26 +1,90 @@ -fn main() { +use std::ffi::OsStr; + +use libflate::gzip::Encoder; +use walkdir::WalkDir; + +fn main() -> Result<(), std::io::Error> { use std::env; let out_dir = std::path::PathBuf::from(env::var("OUT_DIR").unwrap()); - println!("cargo:rerun-if-changed=assets/"); - let assets = std::fs::read_dir("assets").unwrap(); - for file in assets.map(|a| a.unwrap()) { - std::fs::copy( - file.path(), - out_dir.join(file.file_name()) - ) - .unwrap(); - } + println!("cargo::rerun-if-changed=javascript/"); if let Ok(exit) = std::process::Command::new("tsc") .arg("--outDir") .arg(&out_dir) .current_dir("javascript") - .spawn() - .unwrap() + .spawn()? .wait() { if !exit.success() { std::process::exit(exit.code().unwrap_or(1)) } } + + println!("cargo:rerun-if-changed=assets/"); + let assets_path = std::path::Path::new("assets"); + let mut assets = WalkDir::new(&assets_path) + .into_iter(); + while let Some(Ok(entry)) = assets.next() { + if entry.file_type().is_dir() { + if let Err(err) = std::fs::create_dir(&out_dir.join(entry.path())) { + if err.kind() != std::io::ErrorKind::AlreadyExists { + return Err(err) + } + } + } else { + std::fs::copy(entry.path(), &out_dir.join(entry.path().strip_prefix(assets_path).unwrap()))?; + } + } + + let walker = WalkDir::new(&out_dir) + .into_iter() + .map(Result::unwrap) + .filter(|e| { + e.file_type().is_file() && e.path().extension().unwrap() != "gz" + }); + for entry in walker { + let normal_path = entry.path(); + let gzip_path = normal_path.with_extension({ + let mut extension = normal_path + .extension() + .unwrap() + .to_owned(); + extension.push(OsStr::new(".gz")); + extension + }); + eprintln!( + "{} -> {}", + normal_path.strip_prefix(&out_dir).unwrap().display(), + gzip_path.strip_prefix(&out_dir).unwrap().display() + ); + { + let mut out_file = std::fs::OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(&gzip_path)?; + + let mut in_file = std::fs::File::open(&normal_path)?; + + let mut encoder = Encoder::new(&mut out_file)?; + std::io::copy(&mut in_file, &mut encoder)?; + encoder.finish().into_result()?; + } + + let normal_len: f64 = std::fs::metadata(&normal_path).unwrap().len() as f64; + let gzipped_len: f64 = std::fs::metadata(&gzip_path).unwrap().len() as f64; + let ratio = gzipped_len / normal_len; + eprintln!("Ratio: {}", ratio); + if ratio <= 0.9 { + std::fs::remove_file(&normal_path)? + } else { + println!( + "cargo:warning={} compression ratio is {} (> 0.9), leaving as is", + entry.path().display(), + ratio + ); + std::fs::remove_file(&gzip_path)? + } + } + Ok(()) } diff --git a/kittybox-rs/templates/src/lib.rs b/kittybox-rs/templates/src/lib.rs index 5b3a8df..8d5d5fa 100644 --- a/kittybox-rs/templates/src/lib.rs +++ b/kittybox-rs/templates/src/lib.rs @@ -13,13 +13,15 @@ pub mod assets { use axum::response::{IntoResponse, Response}; use axum::extract::Path; use axum::http::StatusCode; - use axum::http::header::{CONTENT_TYPE, CACHE_CONTROL}; + use axum::http::header::{CONTENT_TYPE, CONTENT_ENCODING, CACHE_CONTROL}; - const ASSETS: include_dir::Dir<'static> = include_dir::include_dir!("$OUT_DIR"); + const ASSETS: include_dir::Dir<'static> = include_dir::include_dir!("$OUT_DIR/"); const CACHE_FOR_A_DAY: &str = "max-age=86400"; + const GZIP: &str = "gzip"; - pub async fn statics(Path(path): Path) -> Response { - + pub async fn statics( + Path(path): Path + ) -> Response { let content_type: &'static str = if path.ends_with(".js") { "application/javascript" } else if path.ends_with(".css") { @@ -30,12 +32,19 @@ pub mod assets { "application/octet-stream" }; - match ASSETS.get_file(path) { + match ASSETS.get_file(path.clone() + ".gz") { Some(file) => (StatusCode::OK, [(CONTENT_TYPE, content_type), + (CONTENT_ENCODING, GZIP), (CACHE_CONTROL, CACHE_FOR_A_DAY)], file.contents()).into_response(), - None => StatusCode::NOT_FOUND.into_response() + None => match ASSETS.get_file(path) { + Some(file) => (StatusCode::OK, + [(CONTENT_TYPE, content_type), + (CACHE_CONTROL, CACHE_FOR_A_DAY)], + file.contents()).into_response(), + None => StatusCode::NOT_FOUND.into_response() + } } } } -- cgit 1.4.1