diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a067c07 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,91 @@ +use flate2::{write::GzEncoder, Compression}; +use std::{ + env, + fs, + io::Write, + path::{Component, Path, PathBuf}, +}; +use tiny_http::{Header, Response, Server, StatusCode}; + +const ROOT: &str = "/data"; + +fn mime(path: &Path) -> &'static str { + match path.extension().and_then(|s| s.to_str()).unwrap_or("") { + "html" => "text/html; charset=utf-8", + "css" => "text/css", + "js" => "application/javascript", + "json" => "application/json", + "svg" => "image/svg+xml", + "png" => "image/png", + "jpg" | "jpeg" => "image/jpeg", + "gif" => "image/gif", + "wasm" => "application/wasm", + "ico" => "image/x-icon", + "txt" => "text/plain; charset=utf-8", + _ => "application/octet-stream", + } +} + +fn sanitize(url: &str) -> PathBuf { + let mut out = PathBuf::from(ROOT); + + for c in Path::new(url.trim_start_matches('/')).components() { + if let Component::Normal(p) = c { + out.push(p); + } + } + + out +} + +fn main() { + let spa = env::args().any(|a| a == "-s"); + + let server = Server::http("0.0.0.0:3000").unwrap(); + + for req in server.incoming_requests() { + let mut path = sanitize(req.url()); + + if path.is_dir() { + path.push("index.html"); + } + + if !path.exists() && spa { + path = PathBuf::from(ROOT).join("index.html"); + } + + let Ok(data) = fs::read(&path) else { + let _ = req.respond(Response::empty(StatusCode(404))); + continue; + }; + + let accepts_gzip = req + .headers() + .iter() + .find(|h| h.field.equiv("Accept-Encoding")) + .map(|h| h.value.as_str().contains("gzip")) + .unwrap_or(false); + + let (body, gzip) = if accepts_gzip { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + enc.write_all(&data).unwrap(); + (enc.finish().unwrap(), true) + } else { + (data, false) + }; + + let mut resp = Response::from_data(body); + + resp.add_header( + Header::from_bytes("Content-Type", mime(&path)).unwrap(), + ); + + if gzip { + resp.add_header( + Header::from_bytes("Content-Encoding", "gzip").unwrap(), + ); + } + + let _ = req.respond(resp); + } +} \ No newline at end of file