aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--data/style.scss25
-rw-r--r--src/listing.rs38
-rw-r--r--src/renderer.rs10
-rw-r--r--tests/serve_request.rs74
4 files changed, 102 insertions, 45 deletions
diff --git a/data/style.scss b/data/style.scss
index b901ef6..0b87265 100644
--- a/data/style.scss
+++ b/data/style.scss
@@ -82,11 +82,6 @@ a.file:visited,
color: var(--file_link_color);
}
-a.symlink,
-a.symlink:visited {
- color: var(--symlink_link_color);
-}
-
a.directory:hover {
color: var(--directory_link_color);
}
@@ -95,13 +90,10 @@ a.file:hover {
color: var(--file_link_color);
}
-a.symlink:hover {
- color: var(--symlink_link_color);
-}
-
-.symlink-symbol {
+.symlink-symbol::after {
+ content: "⇢";
display: inline-block;
- border: 1px solid var(--symlink_link_color);
+ border: 1px solid;
margin-left: 0.5rem;
border-radius: 0.2rem;
padding: 0 0.1rem;
@@ -488,17 +480,12 @@ th span.active span {
}
a.root,
- a.file,
- a.symlink {
+ a.file {
display: inline-block;
flex: 1;
padding: 0.5625rem 0;
}
- a.symlink {
- width: 100%;
- }
-
.back {
display: flex;
}
@@ -539,7 +526,6 @@ th span.active span {
--text_color: #323232;
--directory_link_color: #d02474;
--file_link_color: #0086b3;
- --symlink_link_color: #ed6a43;
--table_background: #ffffff;
--table_text_color: #323232;
--table_header_background: #323232;
@@ -584,7 +570,6 @@ th span.active span {
--text_color: #fefefe;
--directory_link_color: #03a9f4;
--file_link_color: #ea95ff;
- --symlink_link_color: #ff9800;
--table_background: #353946;
--table_text_color: #eeeeee;
--table_header_background: #5294e2;
@@ -629,7 +614,6 @@ th span.active span {
--text_color: #efefef;
--directory_link_color: #f0dfaf;
--file_link_color: #87d6d5;
- --symlink_link_color: #ffccee;
--table_background: #4a4949;
--table_text_color: #efefef;
--table_header_background: #7f9f7f;
@@ -674,7 +658,6 @@ th span.active span {
--text_color: #f8f8f2;
--directory_link_color: #f92672;
--file_link_color: #a6e22e;
- --symlink_link_color: #fd971f;
--table_background: #3b3a32;
--table_text_color: #f8f8f0;
--table_header_background: #75715e;
diff --git a/src/listing.rs b/src/listing.rs
index 013265c..43cfb0e 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -74,9 +74,6 @@ pub enum EntryType {
/// Entry is a file
File,
-
- /// Entry is a symlink
- Symlink,
}
/// Entry
@@ -87,6 +84,9 @@ pub struct Entry {
/// Type of the entry
pub entry_type: EntryType,
+ /// Entry is symlink. Not mutually exclusive with entry_type
+ pub is_symlink: bool,
+
/// URL of the entry
pub link: String,
@@ -101,6 +101,7 @@ impl Entry {
fn new(
name: String,
entry_type: EntryType,
+ is_symlink: bool,
link: String,
size: Option<bytesize::ByteSize>,
last_modification_date: Option<SystemTime>,
@@ -108,6 +109,7 @@ impl Entry {
Entry {
name,
entry_type,
+ is_symlink,
link,
size,
last_modification_date,
@@ -123,11 +125,6 @@ impl Entry {
pub fn is_file(&self) -> bool {
self.entry_type == EntryType::File
}
-
- /// Returns wether the entry is a symlink
- pub fn is_symlink(&self) -> bool {
- self.entry_type == EntryType::Symlink
- }
}
/// One entry in the path to the listed directory
@@ -263,14 +260,21 @@ pub fn directory_listing(
let entry = entry?;
// show file url as relative to static path
let file_name = entry.file_name().to_string_lossy().to_string();
+ let (is_symlink, metadata) = match entry.metadata() {
+ Ok(metadata) if metadata.file_type().is_symlink() => {
+ // for symlinks, get the metadata of the original file
+ (true, std::fs::metadata(entry.path()))
+ }
+ res => (false, res),
+ };
let file_url = base
.join(&utf8_percent_encode(&file_name, PATH_SEGMENT).to_string())
.to_string_lossy()
.to_string();
// if file is a directory, add '/' to the end of the name
- if let Ok(metadata) = entry.metadata() {
- if skip_symlinks && metadata.file_type().is_symlink() {
+ if let Ok(metadata) = metadata {
+ if skip_symlinks && is_symlink {
continue;
}
let last_modification_date = match metadata.modified() {
@@ -278,26 +282,20 @@ pub fn directory_listing(
Err(_) => None,
};
- if metadata.file_type().is_symlink() {
- entries.push(Entry::new(
- file_name,
- EntryType::Symlink,
- file_url,
- None,
- last_modification_date,
- ));
- } else if metadata.is_dir() {
+ if metadata.is_dir() {
entries.push(Entry::new(
file_name,
EntryType::Directory,
+ is_symlink,
file_url,
None,
last_modification_date,
));
- } else {
+ } else if metadata.is_file() {
entries.push(Entry::new(
file_name,
EntryType::File,
+ is_symlink,
file_url,
Some(ByteSize::b(metadata.len())),
last_modification_date,
diff --git a/src/renderer.rs b/src/renderer.rs
index fc6897c..d2beda3 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -330,11 +330,17 @@ fn entry_row(
@if entry.is_dir() {
a.directory href=(parametrized_link(&entry.link, sort_method, sort_order)) {
(entry.name) "/"
+ @if entry.is_symlink {
+ span.symlink-symbol { }
+ }
}
} @else if entry.is_file() {
div.file-entry {
a.file href=(&entry.link) {
(entry.name)
+ @if entry.is_symlink {
+ span.symlink-symbol { }
+ }
}
@if let Some(size) = entry.size {
span.mobile-info.size {
@@ -342,10 +348,6 @@ fn entry_row(
}
}
}
- } @else if entry.is_symlink() {
- a.symlink href=(parametrized_link(&entry.link, sort_method, sort_order)) {
- (entry.name) span.symlink-symbol { "⇢" }
- }
}
}
}
diff --git a/tests/serve_request.rs b/tests/serve_request.rs
index 25c5574..e259b9e 100644
--- a/tests/serve_request.rs
+++ b/tests/serve_request.rs
@@ -8,10 +8,16 @@ use regex::Regex;
use rstest::rstest;
use select::document::Document;
use select::node::Node;
+use std::path::Path;
use std::process::{Command, Stdio};
use std::thread::sleep;
use std::time::Duration;
+#[cfg(unix)]
+use std::os::unix::fs::{symlink as symlink_dir, symlink as symlink_file};
+#[cfg(windows)]
+use std::os::windows::fs::{symlink_dir, symlink_file};
+
#[rstest]
fn serves_requests_with_no_options(tmpdir: TempDir) -> Result<(), Error> {
let mut child = Command::cargo_bin("miniserve")?
@@ -159,6 +165,74 @@ fn serves_requests_no_hidden_files_without_flag(tmpdir: TempDir, port: u16) -> R
Ok(())
}
+#[rstest(no_symlinks, case(true), case(false))]
+fn serves_requests_symlinks(tmpdir: TempDir, port: u16, no_symlinks: bool) -> Result<(), Error> {
+ let mut comm = Command::cargo_bin("miniserve")?;
+ comm.arg(tmpdir.path())
+ .arg("-p")
+ .arg(port.to_string())
+ .stdout(Stdio::null());
+ if no_symlinks {
+ comm.arg("--no-symlinks");
+ }
+
+ let mut child = comm.spawn()?;
+ sleep(Duration::from_secs(1));
+
+ let files = &["symlink-file.html"];
+ let dirs = &["symlink-dir/"];
+ let broken = &["symlink broken"];
+
+ for &directory in dirs {
+ let orig = Path::new(DIRECTORIES[0].strip_suffix("/").unwrap());
+ let link = tmpdir
+ .path()
+ .join(Path::new(directory.strip_suffix("/").unwrap()));
+ symlink_dir(orig, link).expect("Couldn't create symlink");
+ }
+ for &file in files {
+ let orig = Path::new(FILES[0]);
+ let link = tmpdir.path().join(Path::new(file));
+ symlink_file(orig, link).expect("Couldn't create symlink");
+ }
+ for &file in broken {
+ let orig = Path::new("should-not-exist.xxx");
+ let link = tmpdir.path().join(Path::new(file));
+ symlink_file(orig, link).expect("Couldn't create symlink");
+ }
+
+ let body = reqwest::blocking::get(format!("http://localhost:{}", port).as_str())?
+ .error_for_status()?;
+ let parsed = Document::from_read(body)?;
+
+ for &entry in files.into_iter().chain(dirs) {
+ let node = parsed
+ .find(|x: &Node| x.name().unwrap_or_default() == "a" && x.text() == entry)
+ .next();
+ assert_eq!(node.is_none(), no_symlinks);
+ if no_symlinks {
+ continue;
+ }
+
+ let node = node.unwrap();
+ assert_eq!(node.attr("href").unwrap().strip_prefix("/").unwrap(), entry);
+ reqwest::blocking::get(format!("http://localhost:{}/{}", port, entry))?
+ .error_for_status()?;
+ if entry.ends_with("/") {
+ assert_eq!(node.attr("class").unwrap(), "directory");
+ } else {
+ assert_eq!(node.attr("class").unwrap(), "file");
+ }
+ }
+ for &entry in broken {
+ assert!(parsed.find(|x: &Node| x.text() == entry).next().is_none());
+ }
+
+ child.kill()?;
+
+ Ok(())
+}
+
#[rstest]
fn serves_requests_with_randomly_assigned_port(tmpdir: TempDir) -> Result<(), Error> {
let mut child = Command::cargo_bin("miniserve")?