feat(server): support handling spaces in filenames (#107)

* 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 <orhunparmaksiz@gmail.com>
This commit is contained in:
Ömer Üstün 2023-08-26 15:35:13 +03:00 committed by GitHub
parent 38d76fcb0f
commit 95379c9f66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 2 deletions

View File

@ -14,6 +14,7 @@ expose_list = false
# "super_secret_token1",
# "super_secret_token2",
#]
handle_spaces = "replace" # or "replace"
[landing_page]
text = """

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -58,10 +58,32 @@ pub struct ServerConfig {
/// Landing page content-type.
#[deprecated(note = "use the [landing_page] table")]
pub landing_page_content_type: Option<String>,
/// Handle spaces either via encoding or replacing.
pub handle_spaces: Option<SpaceHandlingConfig>,
/// Path of the JSON index.
pub expose_list: Option<bool>,
}
/// 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);
}
}

View File

@ -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)

View File

@ -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);