Skip to content

Commit

Permalink
fix: don't percent-encode unreserved characters for directory listing…
Browse files Browse the repository at this point in the history
… links (#371)

* fix: don't percent-encode unreserved chars in the directory listing links

those chars (a.k.a unreserved marks) are now not percent-encoded:
https://www.ietf.org/rfc/rfc3986.txt

* chore: revert partially previous functionality
  • Loading branch information
joseluisq committed Apr 28, 2024
1 parent 5b5ea98 commit 5d66301
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 8 deletions.
20 changes: 14 additions & 6 deletions src/directory_listing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use headers::{ContentLength, ContentType, HeaderMapExt};
use humansize::FormatSize;
use hyper::{Body, Method, Response, StatusCode};
use mime_guess::mime;
use percent_encoding::{percent_decode_str, utf8_percent_encode, NON_ALPHANUMERIC};
use percent_encoding::{percent_decode_str, utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};
use serde::{Serialize, Serializer};
use std::future::Future;
use std::io;
Expand All @@ -22,6 +22,15 @@ use std::time::{SystemTime, UNIX_EPOCH};

use crate::{http_ext::MethodExt, Context, Result};

/// Non-alphanumeric characters to be percent-encoded
/// excluding the "unreserved characters" because allowed in a URI.
/// See 2.3. Unreserved Characters - https://www.ietf.org/rfc/rfc3986.txt
const PERCENT_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC
.remove(b'_')
.remove(b'-')
.remove(b'.')
.remove(b'~');

#[derive(Debug, Serialize, Deserialize, Clone, ValueEnum)]
#[serde(rename_all = "lowercase")]
/// Directory listing output format for file entries.
Expand Down Expand Up @@ -75,9 +84,7 @@ pub fn auto_index(
opts.dir_listing_order,
opts.dir_listing_format,
opts.ignore_hidden_files,
)
.await
{
) {
Ok(resp) => Ok(resp),
Err(err) => {
tracing::error!("error after try to read directory entries: {:?}", err);
Expand Down Expand Up @@ -157,7 +164,7 @@ struct SortingAttr<'a> {

/// It reads a list of directory entries and create an index page content.
/// Otherwise it returns a status error.
async fn read_dir_entries(
fn read_dir_entries(
dir_reader: std::fs::ReadDir,
base_path: &str,
uri_query: Option<&str>,
Expand All @@ -183,6 +190,7 @@ async fn read_dir_entries(
}
};

// FIXME: handle non-Unicode file names properly via OsString
let name = match dir_entry
.file_name()
.into_string()
Expand All @@ -203,7 +211,7 @@ async fn read_dir_entries(
continue;
}

let mut name_encoded = utf8_percent_encode(&name, NON_ALPHANUMERIC).to_string();
let mut name_encoded = utf8_percent_encode(&name, PERCENT_ENCODE_SET).to_string();
let mut size = None;

if meta.is_dir() {
Expand Down
4 changes: 2 additions & 2 deletions tests/dir_listing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ mod tests {

assert_eq!(
body_str.contains(
r#"<a href="sp%C3%A9cial%20direct%C3%B6ry/">spécial directöry/</a>"#
r#"<a href="sp%C3%A9cial-direct%C3%B6ry.net/">spécial-directöry.net/</a>"#
),
method == Method::GET
);
Expand Down Expand Up @@ -269,7 +269,7 @@ mod tests {
assert_eq!(entries.len(), 3);

let first_entry = entries.first().unwrap();
assert_eq!(first_entry.name, "spécial directöry");
assert_eq!(first_entry.name, "spécial-directöry.net");
assert_eq!(first_entry.typed, "directory");
assert!(!first_entry.mtime.is_empty());
assert!(first_entry.size.is_none());
Expand Down

0 comments on commit 5d66301

Please sign in to comment.