mirror of
https://github.com/sigoden/dufs.git
synced 2026-04-09 09:09:03 +03:00
Compare commits
7 Commits
v0.45.0
...
v0.46.0-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26bdc4e298 | ||
|
|
a118c1348e | ||
|
|
db7a0530a2 | ||
|
|
bc27c8c479 | ||
|
|
2b2c7bd5f7 | ||
|
|
ca18df1a36 | ||
|
|
7cfb97dfdf |
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -38,14 +38,6 @@ jobs:
|
|||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
use-cross: true
|
use-cross: true
|
||||||
cargo-flags: ""
|
cargo-flags: ""
|
||||||
- target: i686-unknown-linux-musl
|
|
||||||
os: ubuntu-latest
|
|
||||||
use-cross: true
|
|
||||||
cargo-flags: ""
|
|
||||||
- target: i686-pc-windows-msvc
|
|
||||||
os: windows-latest
|
|
||||||
use-cross: true
|
|
||||||
cargo-flags: ""
|
|
||||||
- target: armv7-unknown-linux-musleabihf
|
- target: armv7-unknown-linux-musleabihf
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
use-cross: true
|
use-cross: true
|
||||||
|
|||||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -2,6 +2,23 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [0.46.0] - 2026-03-04
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Some search results missing due to broken symlinks ([#665](https://github.com/sigoden/dufs/issues/665))
|
||||||
|
- Escape filename in ?simple output ([#669](https://github.com/sigoden/dufs/issues/669))
|
||||||
|
- Ensure symlink inside serve root ([#670](https://github.com/sigoden/dufs/issues/670))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Add option --allow-hash to allow/disallow file hashing ([#657](https://github.com/sigoden/dufs/issues/657))
|
||||||
|
|
||||||
|
### Refactor
|
||||||
|
|
||||||
|
- Update deps ([#655](https://github.com/sigoden/dufs/issues/655))
|
||||||
|
- Improve UI button titles ([#656](https://github.com/sigoden/dufs/issues/656))
|
||||||
|
|
||||||
## [0.45.0] - 2025-09-03
|
## [0.45.0] - 2025-09-03
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
1309
Cargo.lock
generated
1309
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dufs"
|
name = "dufs"
|
||||||
version = "0.45.0"
|
version = "0.46.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["sigoden <sigoden@gmail.com>"]
|
authors = ["sigoden <sigoden@gmail.com>"]
|
||||||
description = "Dufs is a distinctive utility file server"
|
description = "Dufs is a distinctive utility file server"
|
||||||
@@ -24,9 +24,8 @@ futures-util = { version = "0.3", default-features = false, features = ["alloc"]
|
|||||||
async_zip = { version = "0.0.18", default-features = false, features = ["deflate", "bzip2", "xz", "chrono", "tokio"] }
|
async_zip = { version = "0.0.18", default-features = false, features = ["deflate", "bzip2", "xz", "chrono", "tokio"] }
|
||||||
headers = "0.4"
|
headers = "0.4"
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
if-addrs = "0.14"
|
if-addrs = "0.15"
|
||||||
rustls-pemfile = { version = "2.0", optional = true }
|
tokio-rustls = { version = "0.26", optional = true }
|
||||||
tokio-rustls = { version = "0.26", optional = true, default-features = false, features = ["ring", "tls12"]}
|
|
||||||
md5 = "0.8"
|
md5 = "0.8"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
uuid = { version = "1.7", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.7", features = ["v4", "fast-rng"] }
|
||||||
@@ -58,11 +57,11 @@ hex = "0.4.3"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["tls"]
|
default = ["tls"]
|
||||||
tls = ["rustls-pemfile", "tokio-rustls"]
|
tls = ["tokio-rustls"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2"
|
assert_cmd = "2"
|
||||||
reqwest = { version = "0.12", features = ["blocking", "multipart", "rustls-tls"], default-features = false }
|
reqwest = { version = "0.13", features = ["blocking", "multipart", "rustls"], default-features = false }
|
||||||
assert_fs = "1"
|
assert_fs = "1"
|
||||||
port_check = "0.3"
|
port_check = "0.3"
|
||||||
rstest = "0.26.1"
|
rstest = "0.26.1"
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ Options:
|
|||||||
--allow-search Allow search files/folders
|
--allow-search Allow search files/folders
|
||||||
--allow-symlink Allow symlink to files/folders outside root directory
|
--allow-symlink Allow symlink to files/folders outside root directory
|
||||||
--allow-archive Allow download folders as archive file
|
--allow-archive Allow download folders as archive file
|
||||||
|
--allow-hash Allow ?hash query to get file sha256 hash
|
||||||
--enable-cors Enable CORS, sets `Access-Control-Allow-Origin: *`
|
--enable-cors Enable CORS, sets `Access-Control-Allow-Origin: *`
|
||||||
--render-index Serve index.html when requesting a directory, returns 404 if not found index.html
|
--render-index Serve index.html when requesting a directory, returns 404 if not found index.html
|
||||||
--render-try-index Serve index.html when requesting a directory, returns directory listing if not found index.html
|
--render-try-index Serve index.html when requesting a directory, returns directory listing if not found index.html
|
||||||
@@ -346,6 +347,7 @@ All options can be set using environment variables prefixed with `DUFS_`.
|
|||||||
--allow-search DUFS_ALLOW_SEARCH=true
|
--allow-search DUFS_ALLOW_SEARCH=true
|
||||||
--allow-symlink DUFS_ALLOW_SYMLINK=true
|
--allow-symlink DUFS_ALLOW_SYMLINK=true
|
||||||
--allow-archive DUFS_ALLOW_ARCHIVE=true
|
--allow-archive DUFS_ALLOW_ARCHIVE=true
|
||||||
|
--allow-hash DUFS_ALLOW_HASH=true
|
||||||
--enable-cors DUFS_ENABLE_CORS=true
|
--enable-cors DUFS_ENABLE_CORS=true
|
||||||
--render-index DUFS_RENDER_INDEX=true
|
--render-index DUFS_RENDER_INDEX=true
|
||||||
--render-try-index DUFS_RENDER_TRY_INDEX=true
|
--render-try-index DUFS_RENDER_TRY_INDEX=true
|
||||||
@@ -383,6 +385,7 @@ allow-delete: true
|
|||||||
allow-search: true
|
allow-search: true
|
||||||
allow-symlink: true
|
allow-symlink: true
|
||||||
allow-archive: true
|
allow-archive: true
|
||||||
|
allow-hash: true
|
||||||
enable-cors: true
|
enable-cors: true
|
||||||
render-index: true
|
render-index: true
|
||||||
render-try-index: true
|
render-try-index: true
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" />
|
d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<div class="control move-file hidden" title="Move to new path">
|
<div class="control move-file hidden" title="Move & Rename">
|
||||||
<svg class="icon-move" width="16" height="16" viewBox="0 0 16 16">
|
<svg class="icon-move" width="16" height="16" viewBox="0 0 16 16">
|
||||||
<path fill-rule="evenodd"
|
<path fill-rule="evenodd"
|
||||||
d="M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z">
|
d="M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z">
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z" />
|
d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="control upload-file hidden" title="Upload files">
|
<div class="control upload-file hidden" title="Upload files/folders">
|
||||||
<label for="file">
|
<label for="file">
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16">
|
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||||
<path
|
<path
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
d="M7.646 1.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 2.707V11.5a.5.5 0 0 1-1 0V2.707L5.354 4.854a.5.5 0 1 1-.708-.708l3-3z" />
|
||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<input type="file" id="file" title="Upload files" name="file" multiple>
|
<input type="file" id="file" title="Upload files/folders" name="file" multiple>
|
||||||
</div>
|
</div>
|
||||||
<div class="control new-folder hidden" title="New folder">
|
<div class="control new-folder hidden" title="New folder">
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16">
|
<svg width="16" height="16" viewBox="0 0 16 16">
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ function addPath(file, index) {
|
|||||||
}
|
}
|
||||||
if (DATA.allow_delete) {
|
if (DATA.allow_delete) {
|
||||||
if (DATA.allow_upload) {
|
if (DATA.allow_upload) {
|
||||||
actionMove = `<div onclick="movePath(${index})" class="action-btn" id="moveBtn${index}" title="Move to new path">${ICONS.move}</div>`;
|
actionMove = `<div onclick="movePath(${index})" class="action-btn" id="moveBtn${index}" title="Move & Rename">${ICONS.move}</div>`;
|
||||||
if (!isDir) {
|
if (!isDir) {
|
||||||
actionEdit = `<a class="action-btn" title="Edit file" target="_blank" href="${url}?edit">${ICONS.edit}</a>`;
|
actionEdit = `<a class="action-btn" title="Edit file" target="_blank" href="${url}?edit">${ICONS.edit}</a>`;
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/args.rs
21
src/args.rs
@@ -148,6 +148,14 @@ pub fn build_cli() -> Command {
|
|||||||
.action(ArgAction::SetTrue)
|
.action(ArgAction::SetTrue)
|
||||||
.help("Allow download folders as archive file"),
|
.help("Allow download folders as archive file"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("allow-hash")
|
||||||
|
.env("DUFS_ALLOW_HASH")
|
||||||
|
.hide_env(true)
|
||||||
|
.long("allow-hash")
|
||||||
|
.action(ArgAction::SetTrue)
|
||||||
|
.help("Allow ?hash query to get file sha256 hash"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("enable-cors")
|
Arg::new("enable-cors")
|
||||||
.env("DUFS_ENABLE_CORS")
|
.env("DUFS_ENABLE_CORS")
|
||||||
@@ -281,6 +289,7 @@ pub struct Args {
|
|||||||
pub allow_search: bool,
|
pub allow_search: bool,
|
||||||
pub allow_symlink: bool,
|
pub allow_symlink: bool,
|
||||||
pub allow_archive: bool,
|
pub allow_archive: bool,
|
||||||
|
pub allow_hash: bool,
|
||||||
pub render_index: bool,
|
pub render_index: bool,
|
||||||
pub render_spa: bool,
|
pub render_spa: bool,
|
||||||
pub render_try_index: bool,
|
pub render_try_index: bool,
|
||||||
@@ -375,6 +384,9 @@ impl Args {
|
|||||||
if !args.allow_symlink {
|
if !args.allow_symlink {
|
||||||
args.allow_symlink = allow_all || matches.get_flag("allow-symlink");
|
args.allow_symlink = allow_all || matches.get_flag("allow-symlink");
|
||||||
}
|
}
|
||||||
|
if !args.allow_hash {
|
||||||
|
args.allow_hash = allow_all || matches.get_flag("allow-hash");
|
||||||
|
}
|
||||||
if !args.allow_archive {
|
if !args.allow_archive {
|
||||||
args.allow_archive = allow_all || matches.get_flag("allow-archive");
|
args.allow_archive = allow_all || matches.get_flag("allow-archive");
|
||||||
}
|
}
|
||||||
@@ -492,21 +504,16 @@ impl BindAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Default)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Compress {
|
pub enum Compress {
|
||||||
None,
|
None,
|
||||||
|
#[default]
|
||||||
Low,
|
Low,
|
||||||
Medium,
|
Medium,
|
||||||
High,
|
High,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Compress {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Low
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueEnum for Compress {
|
impl ValueEnum for Compress {
|
||||||
fn value_variants<'a>() -> &'a [Self] {
|
fn value_variants<'a>() -> &'a [Self] {
|
||||||
&[Self::None, Self::Low, Self::Medium, Self::High]
|
&[Self::None, Self::Low, Self::Medium, Self::High]
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ impl Server {
|
|||||||
let render_spa = self.args.render_spa;
|
let render_spa = self.args.render_spa;
|
||||||
let render_try_index = self.args.render_try_index;
|
let render_try_index = self.args.render_try_index;
|
||||||
|
|
||||||
if !self.args.allow_symlink && !is_miss && !self.is_root_contained(path).await {
|
if self.guard_root_contained(path).await {
|
||||||
status_not_found(&mut res);
|
status_not_found(&mut res);
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
@@ -358,7 +358,11 @@ impl Server {
|
|||||||
self.handle_edit_file(path, DataKind::View, head_only, user, &mut res)
|
self.handle_edit_file(path, DataKind::View, head_only, user, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
} else if has_query_flag(&query_params, "hash") {
|
} else if has_query_flag(&query_params, "hash") {
|
||||||
self.handle_hash_file(path, head_only, &mut res).await?;
|
if self.args.allow_hash {
|
||||||
|
self.handle_hash_file(path, head_only, &mut res).await?;
|
||||||
|
} else {
|
||||||
|
status_forbid(&mut res);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.handle_send_file(path, headers, head_only, &mut res)
|
self.handle_send_file(path, headers, head_only, &mut res)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -1110,6 +1114,11 @@ impl Server {
|
|||||||
|
|
||||||
ensure_path_parent(&dest).await?;
|
ensure_path_parent(&dest).await?;
|
||||||
|
|
||||||
|
if self.guard_root_contained(&dest).await {
|
||||||
|
status_bad_request(res, "Invalid Destination");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
fs::copy(path, &dest).await?;
|
fs::copy(path, &dest).await?;
|
||||||
|
|
||||||
status_no_content(res);
|
status_no_content(res);
|
||||||
@@ -1126,6 +1135,11 @@ impl Server {
|
|||||||
|
|
||||||
ensure_path_parent(&dest).await?;
|
ensure_path_parent(&dest).await?;
|
||||||
|
|
||||||
|
if self.guard_root_contained(&dest).await {
|
||||||
|
status_bad_request(res, "Invalid Destination");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
fs::rename(path, &dest).await?;
|
fs::rename(path, &dest).await?;
|
||||||
|
|
||||||
status_no_content(res);
|
status_no_content(res);
|
||||||
@@ -1205,10 +1219,11 @@ impl Server {
|
|||||||
let output = paths
|
let output = paths
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
|
let displayname = escape_str_pcdata(&v.name);
|
||||||
if v.is_dir() {
|
if v.is_dir() {
|
||||||
format!("{}/\n", v.name)
|
format!("{}/\n", displayname)
|
||||||
} else {
|
} else {
|
||||||
format!("{}\n", v.name)
|
format!("{}\n", displayname)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
@@ -1284,6 +1299,21 @@ impl Server {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn guard_root_contained(&self, path: &Path) -> bool {
|
||||||
|
if self.args.allow_symlink {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let path = if !fs::try_exists(path).await.unwrap_or_default() {
|
||||||
|
match path.parent() {
|
||||||
|
Some(parent) => parent.to_path_buf(),
|
||||||
|
None => return true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path.to_path_buf()
|
||||||
|
};
|
||||||
|
!self.is_root_contained(path.as_path()).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn is_root_contained(&self, path: &Path) -> bool {
|
async fn is_root_contained(&self, path: &Path) -> bool {
|
||||||
fs::canonicalize(path)
|
fs::canonicalize(path)
|
||||||
.await
|
.await
|
||||||
@@ -1877,10 +1907,14 @@ where
|
|||||||
for dir in access_paths.entry_paths(&path) {
|
for dir in access_paths.entry_paths(&path) {
|
||||||
let mut it = WalkDir::new(&dir).follow_links(true).into_iter();
|
let mut it = WalkDir::new(&dir).follow_links(true).into_iter();
|
||||||
it.next();
|
it.next();
|
||||||
while let Some(Ok(entry)) = it.next() {
|
while let Some(entry) = it.next() {
|
||||||
if !running.load(atomic::Ordering::SeqCst) {
|
if !running.load(atomic::Ordering::SeqCst) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
let entry = match entry {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
let entry_path = entry.path();
|
let entry_path = entry.path();
|
||||||
let base_name = get_file_name(entry_path);
|
let base_name = get_file_name(entry_path);
|
||||||
let is_dir = entry.file_type().is_dir();
|
let is_dir = entry.file_type().is_dir();
|
||||||
|
|||||||
52
src/utils.rs
52
src/utils.rs
@@ -1,7 +1,7 @@
|
|||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
use rustls_pki_types::{CertificateDer, PrivateKeyDer};
|
use rustls_pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
path::Path,
|
path::Path,
|
||||||
@@ -62,42 +62,40 @@ pub fn glob(pattern: &str, target: &str) -> bool {
|
|||||||
|
|
||||||
// Load public certificate from file.
|
// Load public certificate from file.
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
pub fn load_certs<T: AsRef<Path>>(filename: T) -> Result<Vec<CertificateDer<'static>>> {
|
pub fn load_certs<T: AsRef<Path>>(file_name: T) -> Result<Vec<CertificateDer<'static>>> {
|
||||||
// Open certificate file.
|
|
||||||
let cert_file = std::fs::File::open(filename.as_ref())
|
|
||||||
.with_context(|| format!("Failed to access `{}`", filename.as_ref().display()))?;
|
|
||||||
let mut reader = std::io::BufReader::new(cert_file);
|
|
||||||
|
|
||||||
// Load and return certificate.
|
|
||||||
let mut certs = vec![];
|
let mut certs = vec![];
|
||||||
for cert in rustls_pemfile::certs(&mut reader) {
|
for cert in CertificateDer::pem_file_iter(file_name.as_ref()).with_context(|| {
|
||||||
let cert = cert.with_context(|| "Failed to load certificate")?;
|
format!(
|
||||||
|
"Failed to load cert file at `{}`",
|
||||||
|
file_name.as_ref().display()
|
||||||
|
)
|
||||||
|
})? {
|
||||||
|
let cert = cert.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Invalid certificate data in file `{}`",
|
||||||
|
file_name.as_ref().display()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
certs.push(cert)
|
certs.push(cert)
|
||||||
}
|
}
|
||||||
if certs.is_empty() {
|
if certs.is_empty() {
|
||||||
anyhow::bail!("No supported certificate in file");
|
anyhow::bail!(
|
||||||
|
"No supported certificate in file `{}`",
|
||||||
|
file_name.as_ref().display()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(certs)
|
Ok(certs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load private key from file.
|
// Load private key from file.
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
pub fn load_private_key<T: AsRef<Path>>(filename: T) -> Result<PrivateKeyDer<'static>> {
|
pub fn load_private_key<T: AsRef<Path>>(file_name: T) -> Result<PrivateKeyDer<'static>> {
|
||||||
let key_file = std::fs::File::open(filename.as_ref())
|
PrivateKeyDer::from_pem_file(file_name.as_ref()).with_context(|| {
|
||||||
.with_context(|| format!("Failed to access `{}`", filename.as_ref().display()))?;
|
format!(
|
||||||
let mut reader = std::io::BufReader::new(key_file);
|
"Failed to load key file at `{}`",
|
||||||
|
file_name.as_ref().display()
|
||||||
// Load and return a single private key.
|
)
|
||||||
for key in rustls_pemfile::read_all(&mut reader) {
|
})
|
||||||
let key = key.with_context(|| "There was a problem with reading private key")?;
|
|
||||||
match key {
|
|
||||||
rustls_pemfile::Item::Pkcs1Key(key) => return Ok(PrivateKeyDer::Pkcs1(key)),
|
|
||||||
rustls_pemfile::Item::Pkcs8Key(key) => return Ok(PrivateKeyDer::Pkcs8(key)),
|
|
||||||
rustls_pemfile::Item::Sec1Key(key) => return Ok(PrivateKeyDer::Sec1(key)),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anyhow::bail!("No supported private key in file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_range(range: &str, size: u64) -> Option<Vec<(u64, u64)>> {
|
pub fn parse_range(range: &str, size: u64) -> Option<Vec<(u64, u64)>> {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
mod fixtures;
|
mod fixtures;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use assert_cmd::prelude::*;
|
|
||||||
use assert_fs::fixture::TempDir;
|
use assert_fs::fixture::TempDir;
|
||||||
use fixtures::{port, server, tmpdir, wait_for_port, Error, TestServer, DIR_ASSETS};
|
use fixtures::{port, server, tmpdir, wait_for_port, Error, TestServer, DIR_ASSETS};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
@@ -101,7 +100,7 @@ fn asset_js_with_prefix(
|
|||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn assets_override(tmpdir: TempDir, port: u16) -> Result<(), Error> {
|
fn assets_override(tmpdir: TempDir, port: u16) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use std::process::{Command, Stdio};
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(&["-b", "20.205.243.166"])]
|
#[case(&["-b", "20.205.243.166"])]
|
||||||
fn bind_fails(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
fn bind_fails(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
||||||
Command::cargo_bin("dufs")?
|
Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
@@ -49,7 +49,7 @@ fn bind_ipv4_ipv6(
|
|||||||
#[case(&[] as &[&str])]
|
#[case(&[] as &[&str])]
|
||||||
#[case(&["--path-prefix", "/prefix"])]
|
#[case(&["--path-prefix", "/prefix"])]
|
||||||
fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
fn validate_printed_urls(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ use std::process::Command;
|
|||||||
#[test]
|
#[test]
|
||||||
/// Show help and exit.
|
/// Show help and exit.
|
||||||
fn help_shows() -> Result<(), Error> {
|
fn help_shows() -> Result<(), Error> {
|
||||||
Command::cargo_bin("dufs")?.arg("-h").assert().success();
|
Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
|
.arg("-h")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -21,7 +24,7 @@ fn help_shows() -> Result<(), Error> {
|
|||||||
fn print_completions() -> Result<(), Error> {
|
fn print_completions() -> Result<(), Error> {
|
||||||
// let shell_enums = EnumValueParser::<Shell>::new();
|
// let shell_enums = EnumValueParser::<Shell>::new();
|
||||||
for shell in Shell::value_variants() {
|
for shell in Shell::value_variants() {
|
||||||
Command::cargo_bin("dufs")?
|
Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg("--completions")
|
.arg("--completions")
|
||||||
.arg(shell.to_string())
|
.arg(shell.to_string())
|
||||||
.assert()
|
.assert()
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ mod digest_auth_util;
|
|||||||
mod fixtures;
|
mod fixtures;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use assert_cmd::prelude::*;
|
|
||||||
use assert_fs::TempDir;
|
use assert_fs::TempDir;
|
||||||
use digest_auth_util::send_with_digest_auth;
|
use digest_auth_util::send_with_digest_auth;
|
||||||
use fixtures::{port, tmpdir, wait_for_port, Error};
|
use fixtures::{port, tmpdir, wait_for_port, Error};
|
||||||
@@ -13,7 +12,7 @@ use std::process::{Command, Stdio};
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
fn use_config_file(tmpdir: TempDir, port: u16) -> Result<(), Error> {
|
fn use_config_file(tmpdir: TempDir, port: u16) -> Result<(), Error> {
|
||||||
let config_path = get_config_path().display().to_string();
|
let config_path = get_config_path().display().to_string();
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use assert_cmd::prelude::*;
|
|
||||||
use assert_fs::fixture::TempDir;
|
use assert_fs::fixture::TempDir;
|
||||||
use assert_fs::prelude::*;
|
use assert_fs::prelude::*;
|
||||||
use port_check::free_local_port;
|
use port_check::free_local_port;
|
||||||
@@ -129,8 +128,7 @@ where
|
|||||||
{
|
{
|
||||||
let port = port();
|
let port = port();
|
||||||
let tmpdir = tmpdir();
|
let tmpdir = tmpdir();
|
||||||
let child = Command::cargo_bin("dufs")
|
let child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.expect("Couldn't find test binary")
|
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ fn head_file(server: TestServer) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn hash_file(server: TestServer) -> Result<(), Error> {
|
fn hash_file(#[with(&["--allow-hash"])] server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}index.html?hash", server.url()))?;
|
let resp = reqwest::blocking::get(format!("{}index.html?hash", server.url()))?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get("content-type").unwrap(),
|
resp.headers().get("content-type").unwrap(),
|
||||||
@@ -217,6 +217,13 @@ fn hash_file(server: TestServer) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn no_hash_file(server: TestServer) -> Result<(), Error> {
|
||||||
|
let resp = reqwest::blocking::get(format!("{}index.html?hash", server.url()))?;
|
||||||
|
assert_eq!(resp.status(), 403);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn get_file_404(server: TestServer) -> Result<(), Error> {
|
fn get_file_404(server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}404", server.url()))?;
|
let resp = reqwest::blocking::get(format!("{}404", server.url()))?;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ mod utils;
|
|||||||
use digest_auth_util::send_with_digest_auth;
|
use digest_auth_util::send_with_digest_auth;
|
||||||
use fixtures::{port, tmpdir, wait_for_port, Error};
|
use fixtures::{port, tmpdir, wait_for_port, Error};
|
||||||
|
|
||||||
use assert_cmd::prelude::*;
|
|
||||||
use assert_fs::fixture::TempDir;
|
use assert_fs::fixture::TempDir;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@@ -20,7 +19,7 @@ fn log_remote_user(
|
|||||||
#[case] args: &[&str],
|
#[case] args: &[&str],
|
||||||
#[case] is_basic: bool,
|
#[case] is_basic: bool,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
@@ -55,7 +54,7 @@ fn log_remote_user(
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(&["--log-format", ""])]
|
#[case(&["--log-format", ""])]
|
||||||
fn no_log(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
fn no_log(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path())
|
.arg(tmpdir.path())
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
mod fixtures;
|
mod fixtures;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use assert_cmd::prelude::*;
|
|
||||||
use assert_fs::fixture::TempDir;
|
use assert_fs::fixture::TempDir;
|
||||||
use fixtures::{port, tmpdir, wait_for_port, Error};
|
use fixtures::{port, tmpdir, wait_for_port, Error};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
@@ -12,7 +11,7 @@ use std::process::{Command, Stdio};
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case("index.html")]
|
#[case("index.html")]
|
||||||
fn single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> {
|
fn single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path().join(file))
|
.arg(tmpdir.path().join(file))
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
@@ -35,7 +34,7 @@ fn single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Err
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
#[case("index.html")]
|
#[case("index.html")]
|
||||||
fn path_prefix_single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> {
|
fn path_prefix_single_file(tmpdir: TempDir, port: u16, #[case] file: &str) -> Result<(), Error> {
|
||||||
let mut child = Command::cargo_bin("dufs")?
|
let mut child = Command::new(assert_cmd::cargo::cargo_bin!())
|
||||||
.arg(tmpdir.path().join(file))
|
.arg(tmpdir.path().join(file))
|
||||||
.arg("-p")
|
.arg("-p")
|
||||||
.arg(port.to_string())
|
.arg(port.to_string())
|
||||||
|
|||||||
11
tests/tls.rs
11
tests/tls.rs
@@ -1,7 +1,6 @@
|
|||||||
mod fixtures;
|
mod fixtures;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use assert_cmd::Command;
|
|
||||||
use fixtures::{server, Error, TestServer};
|
use fixtures::{server, Error, TestServer};
|
||||||
use predicates::str::contains;
|
use predicates::str::contains;
|
||||||
use reqwest::blocking::ClientBuilder;
|
use reqwest::blocking::ClientBuilder;
|
||||||
@@ -25,7 +24,7 @@ use crate::fixtures::port;
|
|||||||
]))]
|
]))]
|
||||||
fn tls_works(#[case] server: TestServer) -> Result<(), Error> {
|
fn tls_works(#[case] server: TestServer) -> Result<(), Error> {
|
||||||
let client = ClientBuilder::new()
|
let client = ClientBuilder::new()
|
||||||
.danger_accept_invalid_certs(true)
|
.tls_danger_accept_invalid_certs(true)
|
||||||
.build()?;
|
.build()?;
|
||||||
let resp = client.get(server.url()).send()?.error_for_status()?;
|
let resp = client.get(server.url()).send()?.error_for_status()?;
|
||||||
assert_resp_paths!(resp);
|
assert_resp_paths!(resp);
|
||||||
@@ -36,7 +35,7 @@ fn tls_works(#[case] server: TestServer) -> Result<(), Error> {
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
fn wrong_path_cert() -> Result<(), Error> {
|
fn wrong_path_cert() -> Result<(), Error> {
|
||||||
let port = port().to_string();
|
let port = port().to_string();
|
||||||
Command::cargo_bin("dufs")?
|
assert_cmd::cargo::cargo_bin_cmd!()
|
||||||
.args([
|
.args([
|
||||||
"--tls-cert",
|
"--tls-cert",
|
||||||
"wrong",
|
"wrong",
|
||||||
@@ -47,7 +46,7 @@ fn wrong_path_cert() -> Result<(), Error> {
|
|||||||
])
|
])
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(contains("Failed to access `wrong`"));
|
.stderr(contains("Failed to load cert file at `wrong`"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -56,7 +55,7 @@ fn wrong_path_cert() -> Result<(), Error> {
|
|||||||
#[rstest]
|
#[rstest]
|
||||||
fn wrong_path_key() -> Result<(), Error> {
|
fn wrong_path_key() -> Result<(), Error> {
|
||||||
let port = port().to_string();
|
let port = port().to_string();
|
||||||
Command::cargo_bin("dufs")?
|
assert_cmd::cargo::cargo_bin_cmd!()
|
||||||
.args([
|
.args([
|
||||||
"--tls-cert",
|
"--tls-cert",
|
||||||
"tests/data/cert.pem",
|
"tests/data/cert.pem",
|
||||||
@@ -67,7 +66,7 @@ fn wrong_path_key() -> Result<(), Error> {
|
|||||||
])
|
])
|
||||||
.assert()
|
.assert()
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(contains("Failed to access `wrong`"));
|
.stderr(contains("Failed to load key file at `wrong`"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user