diff --git a/src/server.rs b/src/server.rs index 9417b43..6c8f384 100644 --- a/src/server.rs +++ b/src/server.rs @@ -44,11 +44,16 @@ const BUF_SIZE: usize = 65536; pub struct Server { args: Arc, + assets_prefix: String, } impl Server { pub fn new(args: Arc) -> Self { - Self { args } + let assets_prefix = format!("{}__dufs_v{}_", args.uri_prefix, env!("CARGO_PKG_VERSION")); + Self { + args, + assets_prefix, + } } pub async fn call( @@ -58,12 +63,15 @@ impl Server { ) -> Result { let method = req.method().clone(); let uri = req.uri().clone(); + let assets_prefix = self.assets_prefix.clone(); let enable_cors = self.args.enable_cors; let mut res = match self.handle(req).await { Ok(res) => { let status = res.status().as_u16(); - info!(r#"{} "{} {}" - {}"#, addr.ip(), method, uri, status,); + if !uri.path().starts_with(&assets_prefix) { + info!(r#"{} "{} {}" - {}"#, addr.ip(), method, uri, status,); + } res } Err(err) => { @@ -89,8 +97,7 @@ impl Server { let headers = req.headers(); let method = req.method().clone(); - if req_path == "/favicon.ico" && method == Method::GET { - self.handle_send_favicon(headers, &mut res).await?; + if method == Method::GET && self.handle_embed_assets(req_path, &mut res).await? { return Ok(res); } @@ -418,23 +425,38 @@ impl Server { Ok(()) } - async fn handle_send_favicon( - &self, - headers: &HeaderMap, - res: &mut Response, - ) -> BoxResult<()> { - let path = self.args.path.join("favicon.ico"); - let meta = fs::metadata(&path).await.ok(); - let is_file = meta.map(|v| v.is_file()).unwrap_or_default(); - if is_file { - self.handle_send_file(path.as_path(), headers, false, res) - .await?; + async fn handle_embed_assets(&self, req_path: &str, res: &mut Response) -> BoxResult { + if let Some(name) = req_path.strip_prefix(&self.assets_prefix) { + match name { + "index.js" => { + *res.body_mut() = Body::from(INDEX_JS); + res.headers_mut().insert( + "content-type", + HeaderValue::from_static("application/javascript"), + ); + } + "index.css" => { + *res.body_mut() = Body::from(INDEX_CSS); + res.headers_mut() + .insert("content-type", HeaderValue::from_static("text/css")); + } + "favicon.ico" => { + *res.body_mut() = Body::from(FAVICON_ICO); + res.headers_mut() + .insert("content-type", HeaderValue::from_static("image/x-icon")); + } + _ => { + return Ok(false); + } + } + res.headers_mut().insert( + "cache-control", + HeaderValue::from_static("max-age=2592000, public"), + ); + Ok(true) } else { - *res.body_mut() = Body::from(FAVICON_ICO); - res.headers_mut() - .insert("content-type", HeaderValue::from_static("image/x-icon")); + Ok(false) } - Ok(()) } async fn handle_send_file( @@ -701,17 +723,21 @@ impl Server { dir_exists: exist, }; let data = serde_json::to_string(&data).unwrap(); + let asset_js = format!("{}index.js", self.assets_prefix); + let asset_css = format!("{}index.css", self.assets_prefix); + let asset_ico = format!("{}favicon.ico", self.assets_prefix); let output = INDEX_HTML.replace( "__SLOT__", &format!( r#" - + + +DATA = {} + + "#, - INDEX_CSS, data, INDEX_JS + asset_ico, asset_css, data, asset_js ), ); res.headers_mut() diff --git a/tests/assets.rs b/tests/assets.rs new file mode 100644 index 0000000..b5a1e95 --- /dev/null +++ b/tests/assets.rs @@ -0,0 +1,61 @@ +mod fixtures; +mod utils; + +use fixtures::{server, Error, TestServer}; +use rstest::rstest; + +#[rstest] +fn assets(server: TestServer) -> Result<(), Error> { + let ver = env!("CARGO_PKG_VERSION"); + let resp = reqwest::blocking::get(server.url())?; + let index_js = format!("/__dufs_v{}_index.js", ver); + let index_css = format!("/__dufs_v{}_index.css", ver); + let favicon_ico = format!("/__dufs_v{}_favicon.ico", ver); + let text = resp.text()?; + assert!(text.contains(&format!(r#"href="{}""#, index_css))); + assert!(text.contains(&format!(r#"href="{}""#, favicon_ico))); + assert!(text.contains(&format!(r#"src="{}""#, index_js))); + Ok(()) +} + +#[rstest] +fn asset_js(server: TestServer) -> Result<(), Error> { + let url = format!( + "{}__dufs_v{}_index.js", + server.url(), + env!("CARGO_PKG_VERSION") + ); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 200); + assert_eq!( + resp.headers().get("content-type").unwrap(), + "application/javascript" + ); + Ok(()) +} + +#[rstest] +fn asset_css(server: TestServer) -> Result<(), Error> { + let url = format!( + "{}__dufs_v{}_index.css", + server.url(), + env!("CARGO_PKG_VERSION") + ); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.headers().get("content-type").unwrap(), "text/css"); + Ok(()) +} + +#[rstest] +fn asset_ico(server: TestServer) -> Result<(), Error> { + let url = format!( + "{}__dufs_v{}_favicon.ico", + server.url(), + env!("CARGO_PKG_VERSION") + ); + let resp = reqwest::blocking::get(url)?; + assert_eq!(resp.status(), 200); + assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon"); + Ok(()) +} diff --git a/tests/favicon.rs b/tests/favicon.rs deleted file mode 100644 index 12feb47..0000000 --- a/tests/favicon.rs +++ /dev/null @@ -1,25 +0,0 @@ -mod fixtures; -mod utils; - -use fixtures::{server, Error, TestServer}; -use rstest::rstest; - -#[rstest] -fn default_favicon(server: TestServer) -> Result<(), Error> { - let resp = reqwest::blocking::get(format!("{}favicon.ico", server.url()))?; - assert_eq!(resp.status(), 200); - assert_eq!(resp.headers().get("content-type").unwrap(), "image/x-icon"); - Ok(()) -} - -#[rstest] -fn exist_favicon(#[with(&["-A"])] server: TestServer) -> Result<(), Error> { - let url = format!("{}favicon.ico", server.url()); - let data = b"abc"; - let resp = fetch!(b"PUT", &url).body(data.to_vec()).send()?; - assert_eq!(resp.status(), 201); - let resp = reqwest::blocking::get(url)?; - assert_eq!(resp.status(), 200); - assert_eq!(resp.bytes()?, data.to_vec()); - Ok(()) -} diff --git a/tests/utils.rs b/tests/utils.rs index a0f7eda..d33a473 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -37,12 +37,8 @@ pub fn encode_uri(v: &str) -> String { fn retrive_index_paths_impl(index: &str) -> Option> { let lines: Vec<&str> = index.lines().collect(); - let (i, _) = lines - .iter() - .enumerate() - .find(|(_, v)| v.contains("const DATA"))?; - let line = lines.get(i + 1)?; - let value: Value = line.parse().ok()?; + let line = lines.iter().find(|v| v.contains("DATA ="))?; + let value: Value = line[7..].parse().ok()?; let paths = value .get("paths")? .as_array()?