From 95379c9f669d69c78f8836d75f91b4b53c28a189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20=C3=9Cst=C3=BCn?= <41573243+omerbustun@users.noreply.github.com> Date: Sat, 26 Aug 2023 15:35:13 +0300 Subject: [PATCH] feat(server): support handling spaces in filenames (#107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(server): add url encoding - Introduced an option to enable encoding for filenames in the returned URL. - Modified the encoding approach to specifically target whitespaces, replacing them with `%20`. - Included unit tests to validate this encoding approach. - Added fixture tests for broader functional verification. - Removed the `urlencoding` dependency from `Cargo.toml`. * Refactor code and fixtures for better compliance and consistency - Fixed errors in the fixture configuration to ensure tests run as expected. - Reformatted line endings for consistency across the codebase. - Made necessary adjustments to adhere to Clippy's recommendations. * Enhance whitespace handling options for filenames - Implemented a configuration choice for space handling in filenames: encode, replace with underscores or none. - Added corresponding unit and fixture tests. * Remove redundant function call Fixed misplaced line as per @tessus's review feedback, preventing duplicate handling of spaces in filenames. * Delete test file with spaces.txt * Refactor filename space handling - Introduced `SpaceHandling` enum to manage filename spaces. - Updated server.rs and paste.rs to utilize the new method. - Added unit tests for the new filename handling method. - Adjusted fixture tests to reflect these changes. * Move filename processing into `SpaceHandling` enum - Relocated filename processing logic to reside within the `SpaceHandling` enum, enhancing encapsulation. - Updated calls in `server.rs` and `paste.rs` to use the new method structure. - Adjusted unit tests to align with the refactored function location and call pattern. * Refactor based on review suggestions - Incorporated the inline suggestions made by @tessus, removing the unnecessary one-line assignments. - Ensured the code adheres to Rust guidelines by running clippy and fmt. * chore(deps): revert changes in Cargo.lock * chore(config): replace spaces as default * refactor(config): move SpaceHandling to config module --------- Co-authored-by: Orhun Parmaksız --- config.toml | 1 + fixtures/test-filename-replace/config.toml | 10 +++++++ fixtures/test-filename-replace/test.sh | 21 +++++++++++++++ fixtures/test-url-encode/config.toml | 10 +++++++ fixtures/test-url-encode/test.sh | 21 +++++++++++++++ src/config.rs | 31 ++++++++++++++++++++++ src/paste.rs | 5 +++- src/server.rs | 8 +++++- 8 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 fixtures/test-filename-replace/config.toml create mode 100644 fixtures/test-filename-replace/test.sh create mode 100644 fixtures/test-url-encode/config.toml create mode 100644 fixtures/test-url-encode/test.sh diff --git a/config.toml b/config.toml index 61979cb..7d9fcdc 100644 --- a/config.toml +++ b/config.toml @@ -14,6 +14,7 @@ expose_list = false # "super_secret_token1", # "super_secret_token2", #] +handle_spaces = "replace" # or "replace" [landing_page] text = """ diff --git a/fixtures/test-filename-replace/config.toml b/fixtures/test-filename-replace/config.toml new file mode 100644 index 0000000..7ffc943 --- /dev/null +++ b/fixtures/test-filename-replace/config.toml @@ -0,0 +1,10 @@ +[server] +address="127.0.0.1:8000" +max_content_length="10MB" +upload_path="./upload" +handle_spaces = "replace" + +[paste] +#random_url = { enabled = false, type = "alphanumeric", length = 6, suffix_mode = true } +default_extension = "txt" +duplicate_files = true diff --git a/fixtures/test-filename-replace/test.sh b/fixtures/test-filename-replace/test.sh new file mode 100644 index 0000000..b44fd02 --- /dev/null +++ b/fixtures/test-filename-replace/test.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +content="test data for space replacement" + +setup() { + echo "$content" > "test file with spaces.txt" +} + +run_test() { + # Upload the file and get the URL. + replaced_url=$(curl -s -F "file=@test file with spaces.txt" localhost:8000) + + # Ensure the URL contains underscores instead of spaces. + expected_url="http://localhost:8000/test_file_with_spaces.txt" + test "$replaced_url" = "$expected_url" +} + +teardown() { + rm "test file with spaces.txt" + rm -r upload +} diff --git a/fixtures/test-url-encode/config.toml b/fixtures/test-url-encode/config.toml new file mode 100644 index 0000000..f1f66f5 --- /dev/null +++ b/fixtures/test-url-encode/config.toml @@ -0,0 +1,10 @@ +[server] +address="127.0.0.1:8000" +max_content_length="10MB" +upload_path="./upload" +handle_spaces = "encode" + +[paste] +#random_url = { enabled = false, type = "alphanumeric", length = 6, suffix_mode = true } +default_extension = "txt" +duplicate_files = true diff --git a/fixtures/test-url-encode/test.sh b/fixtures/test-url-encode/test.sh new file mode 100644 index 0000000..99c3458 --- /dev/null +++ b/fixtures/test-url-encode/test.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +content="test data for URL encoding" + +setup() { + echo "$content" > "test file with spaces.txt" +} + +run_test() { + # Upload the file and get the URL. + encoded_url=$(curl -s -F "file=@test file with spaces.txt" localhost:8000) + + # Ensure the URL is encoded correctly. + expected_url="http://localhost:8000/test%20file%20with%20spaces.txt" + test "$encoded_url" = "$expected_url" +} + +teardown() { + rm "test file with spaces.txt" + rm -r upload +} diff --git a/src/config.rs b/src/config.rs index 414da1d..6d55c2d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -58,10 +58,32 @@ pub struct ServerConfig { /// Landing page content-type. #[deprecated(note = "use the [landing_page] table")] pub landing_page_content_type: Option, + /// Handle spaces either via encoding or replacing. + pub handle_spaces: Option, /// Path of the JSON index. pub expose_list: Option, } +/// Enum representing different strategies for handling spaces in filenames. +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum SpaceHandlingConfig { + /// Represents encoding spaces (e.g., using "%20"). + Encode, + /// Represents replacing spaces with underscores. + Replace, +} + +impl SpaceHandlingConfig { + /// Processes the given filename based on the specified space handling strategy. + pub fn process_filename(&self, file_name: &str) -> String { + match self { + Self::Encode => file_name.replace(' ', "%20"), + Self::Replace => file_name.replace(' ', "_"), + } + } +} + /// Landing page configuration. #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] pub struct LandingPageConfig { @@ -180,4 +202,13 @@ mod tests { config.warn_deprecation(); Ok(()) } + + #[test] + fn test_space_handling() { + let processed_filename = + SpaceHandlingConfig::Replace.process_filename("file with spaces.txt"); + assert_eq!("file_with_spaces.txt", processed_filename); + let encoded_filename = SpaceHandlingConfig::Encode.process_filename("file with spaces.txt"); + assert_eq!("file%20with%20spaces.txt", encoded_filename); + } } diff --git a/src/paste.rs b/src/paste.rs index 8928c45..3163088 100644 --- a/src/paste.rs +++ b/src/paste.rs @@ -108,7 +108,7 @@ impl Paste { } } } - let file_name = match PathBuf::from(file_name) + let mut file_name = match PathBuf::from(file_name) .file_name() .and_then(|v| v.to_str()) { @@ -117,6 +117,9 @@ impl Paste { Some(v) => v.to_string(), None => String::from("file"), }; + if let Some(handle_spaces_config) = config.server.handle_spaces { + file_name = handle_spaces_config.process_filename(&file_name); + } let mut path = self .type_ .get_path(&config.server.upload_path) diff --git a/src/server.rs b/src/server.rs index 66a4db3..4c434a6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -252,7 +252,7 @@ async fn upload( data: bytes.to_vec(), type_: paste_type, }; - let file_name = match paste.type_ { + let mut file_name = match paste.type_ { PasteType::File | PasteType::Oneshot => { let config = config .read() @@ -277,6 +277,12 @@ async fn upload( Byte::from_bytes(paste.data.len() as u128).get_appropriate_unit(false), host ); + let config = config + .read() + .map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?; + if let Some(handle_spaces_config) = config.server.handle_spaces { + file_name = handle_spaces_config.process_filename(&file_name); + } urls.push(format!("{}/{}\n", server_url, file_name)); } else { log::warn!("{} sent an invalid form field", host);