From 00094be0f950d3d577a1b323a5f84e3f82345bbd Mon Sep 17 00:00:00 2001 From: Taylor Vann Date: Tue, 23 Sep 2025 16:08:56 -0700 Subject: [PATCH] use tokio filesystem fs to get meta data and ping the hard drive --- .gitignore | 2 +- README.md | 16 ++++----- ...e.json => file_server_config_example.json} | 2 +- file_server/src/config.rs | 34 +++++++++++-------- response/src/response_paths.rs | 7 ++-- response/src/type_flyweight.rs | 6 ++-- 6 files changed, 36 insertions(+), 31 deletions(-) rename demo/{demo.example.json => file_server_config_example.json} (70%) diff --git a/.gitignore b/.gitignore index 07572f6..a742f6a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ Cargo.lock # FILE SERVER *.json -!*.example.json +!file_server_config_example.json diff --git a/README.md b/README.md index 8c488b8..b9d50d3 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Bash the following command: file_server ``` -This will start `file_server` with it's default configuration in the `cwd`. +This will start `file_server` with using the `cwd` as a base directory. Now files can be requested from the `cwd` at `localhost:3000`: @@ -39,18 +39,18 @@ curl localhost:3000 ### Configuration -A valid [JSON configuration file](./file_server.example.json) matches the following schema. +A valid [JSON configuration file](./demo/file_server_config_example.json) matches the following schema. ```JSON { - "directory": "./demo", + "directory": "./", "host_and_port": "127.0.0.1:4000", "content_encodings": ["gzip", "deflate", "br", "zstd"], - "filepath_404": "./demo/404.html" + "filepath_404": "./404.html" } ``` -Filepaths can be relative or absolute. Relative paths are "relative from" the filepath of the JSON configuration. +Filepaths can be relative or absolute. Relative paths are "relative from" the filepath of the JSON configuration file. The `content_encodings` and `filepath_404` properties are optional. @@ -66,7 +66,7 @@ Open a browser and visit `http://localhost:3000` and an encoded version of `inde ### Accept-Encoding -When an `accept-encoding` header is found in a request, `file_server` will return a corresponding `zip`-ed version of file if available. +When a request has an `accept-encoding` header, `file_server` will return a corresponding `zip`-ed version of file if available. So if a request has the following header: @@ -74,11 +74,11 @@ So if a request has the following header: Accept-Encoding: gzip; ``` -And the source file has a correspponding gziped file: +And the target file has a correspponding gziped file: ```sh index.html # source file -index.html.gz # gzipped file +index.html.gz # gzipped file ``` `File_server` will send the encoded file, if available. Otherwise, it serves the source file. diff --git a/demo/demo.example.json b/demo/file_server_config_example.json similarity index 70% rename from demo/demo.example.json rename to demo/file_server_config_example.json index c8f9f21..c1d631b 100644 --- a/demo/demo.example.json +++ b/demo/file_server_config_example.json @@ -1,6 +1,6 @@ { "directory": "./", - "host_and_port": "127.0.0.1:3000", + "host_and_port": "127.0.0.1:4000", "content_encodings": ["zstd"], "filepath_404": "./404.html" } diff --git a/file_server/src/config.rs b/file_server/src/config.rs index 27f0823..6ea9908 100644 --- a/file_server/src/config.rs +++ b/file_server/src/config.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use serde_json; use std::env; use std::path; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use tokio::fs; #[derive(Clone, Serialize, Deserialize, Debug)] @@ -28,9 +28,9 @@ impl Config { }) } - pub async fn try_from(source_path: &PathBuf) -> Result { + pub async fn try_from(filepath: &PathBuf) -> Result { // see if config exists - let config_json = match fs::read_to_string(source_path).await { + let config_json = match fs::read_to_string(filepath).await { Ok(r) => r, Err(e) => return Err(e.to_string()), }; @@ -41,7 +41,7 @@ impl Config { }; // get target directory - let config_path = match path::absolute(&source_path) { + let config_path = match path::absolute(&filepath) { Ok(pb) => pb, Err(e) => return Err(e.to_string()), }; @@ -53,29 +53,33 @@ impl Config { } }; - // get target directory relative to config path - let target_directory = parent_dir.join(config.directory); - let target_directory_abs = match path::absolute(target_directory) { + // https://doc.rust-lang.org/std/path/struct.Path.html#method.normalize_lexically + // normalize lexically + let target_directory = match fs::canonicalize(parent_dir.join(config.directory)).await { Ok(pb) => pb, Err(e) => return Err(e.to_string()), }; - config.directory = target_directory_abs; - if let Some(origin_404s) = config.filepath_404 { - config.filepath_404 = match get_path_relative_to_origin(parent_dir, &origin_404s) { - Ok(pb) => Some(pb), - Err(e) => return Err(e.to_string()), - }; + config.filepath_404 = + match get_path_relative_to_origin(&target_directory, &origin_404s).await { + Ok(pb) => Some(pb), + Err(e) => return Err(e.to_string()), + }; } + config.directory = target_directory; + Ok(config) } } -fn get_path_relative_to_origin(source_dir: &Path, filepath: &PathBuf) -> Result { +async fn get_path_relative_to_origin( + source_dir: &PathBuf, + filepath: &PathBuf, +) -> Result { let target_path = source_dir.join(filepath); - let target_path_abs = match path::absolute(target_path) { + let target_path_abs = match fs::canonicalize(target_path).await { Ok(pb) => pb, Err(e) => return Err(e.to_string()), }; diff --git a/response/src/response_paths.rs b/response/src/response_paths.rs index 1b62bb3..d2d8b7b 100644 --- a/response/src/response_paths.rs +++ b/response/src/response_paths.rs @@ -2,7 +2,6 @@ use hyper::body::Incoming; use hyper::header::ACCEPT_ENCODING; use hyper::http::Request; use std::ffi::OsString; -use std::path; use std::path::PathBuf; use tokio::fs; @@ -23,7 +22,9 @@ pub async fn get_path_from_request_url( } pub async fn get_path(directory: &PathBuf, filepath: &PathBuf) -> Option { - let mut target_path = match path::absolute(directory.join(&filepath)) { + // https://doc.rust-lang.org/std/path/struct.Path.html#method.normalize_lexically + // normalize lexically in nightly + let mut target_path = match fs::canonicalize(directory.join(&filepath)).await { Ok(pb) => pb, _ => return None, }; @@ -38,7 +39,7 @@ pub async fn get_path(directory: &PathBuf, filepath: &PathBuf) -> Option return None, }; - // if file bail early + // if file return early if metadata.is_file() { return Some(target_path); } diff --git a/response/src/type_flyweight.rs b/response/src/type_flyweight.rs index 3c96087..56faed3 100644 --- a/response/src/type_flyweight.rs +++ b/response/src/type_flyweight.rs @@ -29,9 +29,9 @@ impl ResponseParams { let available_encodings = AvailableEncodings::from(content_encodings); ResponseParams { - directory: directory, - available_encodings: available_encodings, - filepath_404: filepath_404, + directory, + available_encodings, + filepath_404, } } }