mirror of
https://github.com/sigoden/dufs.git
synced 2026-04-09 09:09:03 +03:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dff489398e | ||
|
|
64e397d18a | ||
|
|
cc0014c183 | ||
|
|
a489c5647a | ||
|
|
0918fb3fe4 | ||
|
|
14efeb6360 | ||
|
|
30b8f75bba | ||
|
|
a39065beff | ||
|
|
a493c13734 | ||
|
|
ae2f878e62 | ||
|
|
277d9d22d4 | ||
|
|
c62926d19c | ||
|
|
cccbbe9ea4 |
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -2,7 +2,28 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.27.0] - 2022-07-21
|
||||
## [0.29.0] - 2022-08-03
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Table row hover highlighting in dark mode ([#122](https://github.com/sigoden/dufs/issues/122))
|
||||
|
||||
### Features
|
||||
|
||||
- Support ecdsa tls cert ([#119](https://github.com/sigoden/dufs/issues/119))
|
||||
|
||||
## [0.28.0] - 2022-08-01
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- File path contains special charactors ([#114](https://github.com/sigoden/dufs/issues/114))
|
||||
|
||||
### Features
|
||||
|
||||
- Add table row hover ([#115](https://github.com/sigoden/dufs/issues/115))
|
||||
- Support customize http log format ([#116](https://github.com/sigoden/dufs/issues/116))
|
||||
|
||||
## [0.27.0] - 2022-07-25
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
471
Cargo.lock
generated
471
Cargo.lock
generated
@@ -17,15 +17,6 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.4"
|
||||
@@ -90,9 +81,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.56"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
|
||||
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -134,21 +125,6 @@ version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -192,9 +168,9 @@ checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e"
|
||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
@@ -223,9 +199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.14"
|
||||
version = "3.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54635806b078b7925d6e36810b1755f2a4b5b4d57560432c1ecf60bcbe10602b"
|
||||
checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
@@ -306,22 +282,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
@@ -356,7 +316,7 @@ dependencies = [
|
||||
"digest 0.9.0",
|
||||
"hex",
|
||||
"md-5",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
@@ -379,7 +339,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dufs"
|
||||
version = "0.27.0"
|
||||
version = "0.29.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"assert_fs",
|
||||
@@ -401,13 +361,11 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"port_check",
|
||||
"predicates",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"rstest",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"select",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"socket2",
|
||||
@@ -495,16 +453,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futf"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
|
||||
dependencies = [
|
||||
"mac",
|
||||
"new_debug_unreachable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.21"
|
||||
@@ -602,25 +550,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
version = "0.14.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
@@ -721,20 +658,6 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "html5ever"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
@@ -900,9 +823,9 @@ checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.58"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -919,16 +842,6 @@ version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
@@ -938,38 +851,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever_rcdom"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
|
||||
dependencies = [
|
||||
"html5ever",
|
||||
"markup5ever",
|
||||
"tendril",
|
||||
"xml5ever",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
@@ -1054,12 +935,6 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.3.0"
|
||||
@@ -1158,101 +1033,12 @@ version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
|
||||
dependencies = [
|
||||
"phf_generator 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
"rand 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
@@ -1283,12 +1069,6 @@ version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "2.1.1"
|
||||
@@ -1319,23 +1099,11 @@ dependencies = [
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
|
||||
checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1349,20 +1117,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -1370,18 +1124,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1391,16 +1135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1409,32 +1144,14 @@ version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534cfe58d6a18cc17120fbf4635d53d14691c1fe4d951064df9bd326178d7d5a"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -1577,9 +1294,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
|
||||
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
@@ -1609,12 +1326,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
@@ -1648,17 +1359,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "select"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee061f90afcc8678bef7a78d0d121683f0ba753f740ff7005f833ec445876b7"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"html5ever",
|
||||
"markup5ever_rcdom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.12"
|
||||
@@ -1667,18 +1367,18 @@ checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.140"
|
||||
version = "1.0.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
|
||||
checksum = "7af873f2c95b99fcb0bd0fe622a43e29514658873c8ceba88c4cb88833a22500"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.140"
|
||||
version = "1.0.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
|
||||
checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1741,12 +1441,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
@@ -1756,12 +1450,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.4"
|
||||
@@ -1778,32 +1466,6 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "string_cache_codegen"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.98"
|
||||
@@ -1829,17 +1491,6 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tendril"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
|
||||
dependencies = [
|
||||
"futf",
|
||||
"mac",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
@@ -1922,9 +1573,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.20.0"
|
||||
version = "1.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e"
|
||||
checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -1994,9 +1645,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.35"
|
||||
version = "0.1.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
|
||||
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"pin-project-lite",
|
||||
@@ -2005,9 +1656,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.28"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
|
||||
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
@@ -2078,20 +1729,14 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
dependencies = [
|
||||
"getrandom 0.2.7",
|
||||
"rand 0.8.5",
|
||||
"getrandom",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2136,12 +1781,6 @@ dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
@@ -2156,9 +1795,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -2166,13 +1805,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -2181,9 +1820,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.31"
|
||||
version = "0.4.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
|
||||
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -2193,9 +1832,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -2203,9 +1842,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2216,15 +1855,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.58"
|
||||
version = "0.3.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
|
||||
checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -2337,15 +1976,3 @@ name = "xml-rs"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||
|
||||
[[package]]
|
||||
name = "xml5ever"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9234163818fd8e2418fcde330655e757900d4236acd8cc70fef345ef91f6d865"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac",
|
||||
"markup5ever",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dufs"
|
||||
version = "0.27.0"
|
||||
version = "0.29.0"
|
||||
edition = "2021"
|
||||
authors = ["sigoden <sigoden@gmail.com>"]
|
||||
description = "Dufs is a distinctive utility file server"
|
||||
@@ -47,11 +47,9 @@ tls = ["rustls", "rustls-pemfile", "tokio-rustls"]
|
||||
assert_cmd = "2"
|
||||
reqwest = { version = "0.11", features = ["blocking", "multipart", "rustls-tls"], default-features = false }
|
||||
assert_fs = "1"
|
||||
select = "0.5"
|
||||
port_check = "0.1"
|
||||
rstest = "0.15"
|
||||
regex = "1"
|
||||
pretty_assertions = "1.2"
|
||||
url = "2"
|
||||
diqwest = { version = "1", features = ["blocking"] }
|
||||
predicates = "2"
|
||||
|
||||
55
README.md
55
README.md
@@ -52,7 +52,7 @@ OPTIONS:
|
||||
-b, --bind <addr>... Specify bind address
|
||||
-p, --port <port> Specify port to listen on [default: 5000]
|
||||
--path-prefix <path> Specify a path prefix
|
||||
--hidden <value> Hide directories from directory listings, separated by `,`
|
||||
--hidden <value> Hide paths from directory listings, separated by `,`
|
||||
-a, --auth <rule>... Add auth for path
|
||||
--auth-method <value> Select auth method [default: digest] [possible values: basic, digest]
|
||||
-A, --allow-all Allow all operations
|
||||
@@ -64,9 +64,10 @@ OPTIONS:
|
||||
--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-spa Serve SPA(Single Page Application)
|
||||
--completions <shell> Print shell completion script for <shell> [possible values: bash, elvish, fish, powershell, zsh]
|
||||
--tls-cert <path> Path to an SSL/TLS certificate to serve with HTTPS
|
||||
--tls-key <path> Path to the SSL/TLS certificate's private key
|
||||
--log-format <format> Customize http log format
|
||||
--completions <shell> Print shell completion script for <shell> [possible values: bash, elvish, fish, powershell, zsh]
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
```
|
||||
@@ -127,12 +128,6 @@ Listen on a specific port
|
||||
dufs -p 80
|
||||
```
|
||||
|
||||
Hide directories from directory listings
|
||||
|
||||
```
|
||||
dufs --hidden .git,.DS_Store
|
||||
```
|
||||
|
||||
Use https
|
||||
|
||||
```
|
||||
@@ -164,7 +159,10 @@ Delete a file/folder
|
||||
curl -X DELETE http://127.0.0.1:5000/path-to-file-or-folder
|
||||
```
|
||||
|
||||
## Access Control
|
||||
<details>
|
||||
<summary><h2>Advance topics</h2></summary>
|
||||
|
||||
### Access Control
|
||||
|
||||
Dufs supports path level access control. You can control who can do what on which path with `--auth`/`-a`.
|
||||
|
||||
@@ -187,6 +185,45 @@ dufs -a /@admin:pass1@* -a /ui@designer:pass2 -A
|
||||
- Account `admin:pass1` can upload/delete/view/download any files/folders.
|
||||
- Account `designer:pass2` can upload/delete/view/download any files/folders in the `ui` folder.
|
||||
|
||||
|
||||
### Hidden Paths
|
||||
|
||||
Dufs supports hiding paths from directory listings via option `--hidden`.
|
||||
|
||||
```
|
||||
dufs --hidden .git,.DS_Store
|
||||
```
|
||||
|
||||
`--hidden` supports a variant glob:
|
||||
|
||||
- `?` matches any single character
|
||||
- `*` matches any (possibly empty) sequence of characters
|
||||
- no `**`, `[..]`, `[!..]`
|
||||
|
||||
Hide all hidden directories/files
|
||||
|
||||
```
|
||||
dufs --hidden '.*'
|
||||
```
|
||||
|
||||
### Log Format
|
||||
|
||||
Dufs supports customize http log format via option `--log-format`.
|
||||
|
||||
The default format is `'$remote_addr "$request" $status'`.
|
||||
|
||||
| variable | description |
|
||||
| ------------ | ------------------------------------------------------------------------- |
|
||||
| $remote_addr | client address |
|
||||
| $remote_user | user name supplied with authentication |
|
||||
| $request | full original request line |
|
||||
| $status | response status |
|
||||
| $http_ | arbitrary request header field. examples: $http_user_agent, $http_referer |
|
||||
|
||||
> use `dufs --log-format=''` to disable http log
|
||||
|
||||
</details>
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2022 dufs-developers.
|
||||
|
||||
@@ -131,6 +131,10 @@ body {
|
||||
padding-left: 0.6em;
|
||||
}
|
||||
|
||||
.paths-table tr:hover {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.paths-table .cell-actions {
|
||||
width: 75px;
|
||||
display: flex;
|
||||
@@ -227,4 +231,8 @@ body {
|
||||
.path a {
|
||||
color: #3191ff;
|
||||
}
|
||||
|
||||
.paths-table tr:hover {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ class Uploader {
|
||||
upload() {
|
||||
const { idx, name } = this;
|
||||
const url = getUrl(name);
|
||||
const encodedUrl = encodedStr(url);
|
||||
const encodedName = encodedStr(name);
|
||||
$uploadersTable.insertAdjacentHTML("beforeend", `
|
||||
<tr id="upload${idx}" class="uploader">
|
||||
@@ -63,7 +62,7 @@ class Uploader {
|
||||
${getSvg()}
|
||||
</td>
|
||||
<td class="path cell-name">
|
||||
<a href="${encodedUrl}">${encodedName}</a>
|
||||
<a href="${url}">${encodedName}</a>
|
||||
</td>
|
||||
<td class="cell-status upload-status" id="uploadStatus${idx}"></td>
|
||||
</tr>`);
|
||||
@@ -76,7 +75,7 @@ class Uploader {
|
||||
}
|
||||
|
||||
ajax() {
|
||||
Uploader.runings += 1;
|
||||
Uploader.runnings += 1;
|
||||
const url = getUrl(this.name);
|
||||
this.lastUptime = Date.now();
|
||||
const ajax = new XMLHttpRequest();
|
||||
@@ -111,20 +110,20 @@ class Uploader {
|
||||
|
||||
complete() {
|
||||
this.$uploadStatus.innerHTML = `✓`;
|
||||
Uploader.runings -= 1;
|
||||
Uploader.runnings -= 1;
|
||||
Uploader.runQueue();
|
||||
}
|
||||
|
||||
fail() {
|
||||
this.$uploadStatus.innerHTML = `✗`;
|
||||
Uploader.runings -= 1;
|
||||
Uploader.runnings -= 1;
|
||||
Uploader.runQueue();
|
||||
}
|
||||
}
|
||||
|
||||
Uploader.globalIdx = 0;
|
||||
|
||||
Uploader.runings = 0;
|
||||
Uploader.runnings = 0;
|
||||
|
||||
/**
|
||||
* @type Uploader[]
|
||||
@@ -133,7 +132,7 @@ Uploader.queues = [];
|
||||
|
||||
|
||||
Uploader.runQueue = () => {
|
||||
if (Uploader.runings > 2) return;
|
||||
if (Uploader.runnings > 2) return;
|
||||
let uploader = Uploader.queues.shift();
|
||||
if (!uploader) return;
|
||||
uploader.ajax();
|
||||
@@ -160,16 +159,15 @@ function addBreadcrumb(href, uri_prefix) {
|
||||
if (!path.endsWith("/")) {
|
||||
path += "/";
|
||||
}
|
||||
path += encodeURI(name);
|
||||
path += encodeURIComponent(name);
|
||||
}
|
||||
const encodedPath = encodedStr(path);
|
||||
const encodedName = encodedStr(name);
|
||||
if (i === 0) {
|
||||
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${encodedPath}"><svg width="16" height="16" viewBox="0 0 16 16"><path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5z"/></svg></a>`);
|
||||
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${path}"><svg width="16" height="16" viewBox="0 0 16 16"><path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5z"/></svg></a>`);
|
||||
} else if (i === len - 1) {
|
||||
$breadcrumb.insertAdjacentHTML("beforeend", `<b>${encodedName}</b>`);
|
||||
} else {
|
||||
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${encodedPath}">${encodedName}</a>`);
|
||||
$breadcrumb.insertAdjacentHTML("beforeend", `<a href="${path}">${encodedName}</a>`);
|
||||
}
|
||||
if (i !== len - 1) {
|
||||
$breadcrumb.insertAdjacentHTML("beforeend", `<span class="separator">/</span>`);
|
||||
@@ -185,23 +183,21 @@ function addBreadcrumb(href, uri_prefix) {
|
||||
function addPath(file, index) {
|
||||
const encodedName = encodedStr(file.name);
|
||||
let url = getUrl(file.name)
|
||||
let encodedUrl = encodedStr(url);
|
||||
let actionDelete = "";
|
||||
let actionDownload = "";
|
||||
let actionMove = "";
|
||||
if (file.path_type.endsWith("Dir")) {
|
||||
url += "/";
|
||||
encodedUrl += "/";
|
||||
actionDownload = `
|
||||
<div class="action-btn">
|
||||
<a href="${encodedUrl}?zip" title="Download folder as a .zip file">
|
||||
<a href="${url}?zip" title="Download folder as a .zip file">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path 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>
|
||||
</a>
|
||||
</div>`;
|
||||
} else {
|
||||
actionDownload = `
|
||||
<div class="action-btn" >
|
||||
<a href="${encodedUrl}" title="Download file" download>
|
||||
<a href="${url}" title="Download file" download>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16"><path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/><path 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>
|
||||
</a>
|
||||
</div>`;
|
||||
@@ -231,7 +227,7 @@ function addPath(file, index) {
|
||||
${getSvg(file.path_type)}
|
||||
</td>
|
||||
<td class="path cell-name">
|
||||
<a href="${encodedUrl}">${encodedName}</a>
|
||||
<a href="${url}">${encodedName}</a>
|
||||
</td>
|
||||
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
||||
<td class="cell-size">${formatSize(file.size).join(" ")}</td>
|
||||
@@ -287,12 +283,14 @@ async function movePath(index) {
|
||||
|
||||
const filePath = decodeURIComponent(fileUrlObj.pathname.slice(prefix.length));
|
||||
|
||||
const newPath = prompt("Enter new path", filePath)
|
||||
if (!newPath || filePath === newPath) return;
|
||||
const newFileUrl = fileUrlObj.origin + prefix + encodeURI(newPath);
|
||||
let newPath = prompt("Enter new path", filePath)
|
||||
if (!newPath) return;
|
||||
if (!newPath.startsWith("/")) newPath = "/" + newPath;
|
||||
if (filePath === newPath) return;
|
||||
const newFileUrl = fileUrlObj.origin + prefix + newPath.split("/").map(encodeURIComponent).join("/");
|
||||
|
||||
try {
|
||||
const res = await fetch(getUrl(file.name), {
|
||||
const res = await fetch(fileUrl, {
|
||||
method: "MOVE",
|
||||
headers: {
|
||||
"Destination": newFileUrl,
|
||||
@@ -367,7 +365,7 @@ async function addFileEntries(entries, dirs) {
|
||||
function getUrl(name) {
|
||||
let url = location.href.split('?')[0];
|
||||
if (!url.endsWith("/")) url += "/";
|
||||
url += encodeURI(name);
|
||||
url += name.split("/").map(encodeURIComponent).join("/");
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
30
src/args.rs
30
src/args.rs
@@ -8,6 +8,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::auth::AccessControl;
|
||||
use crate::auth::AuthMethod;
|
||||
use crate::log_http::{LogHttp, DEFAULT_LOG_FORMAT};
|
||||
#[cfg(feature = "tls")]
|
||||
use crate::tls::{load_certs, load_private_key};
|
||||
use crate::utils::encode_uri;
|
||||
@@ -55,7 +56,7 @@ pub fn build_cli() -> Command<'static> {
|
||||
.arg(
|
||||
Arg::new("hidden")
|
||||
.long("hidden")
|
||||
.help("Hide directories from directory listings, separated by `,`")
|
||||
.help("Hide paths from directory listings, separated by `,`")
|
||||
.value_name("value"),
|
||||
)
|
||||
.arg(
|
||||
@@ -120,13 +121,6 @@ pub fn build_cli() -> Command<'static> {
|
||||
Arg::new("render-spa")
|
||||
.long("render-spa")
|
||||
.help("Serve SPA(Single Page Application)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("completions")
|
||||
.long("completions")
|
||||
.value_name("shell")
|
||||
.value_parser(value_parser!(Shell))
|
||||
.help("Print shell completion script for <shell>"),
|
||||
);
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
@@ -144,7 +138,19 @@ pub fn build_cli() -> Command<'static> {
|
||||
.help("Path to the SSL/TLS certificate's private key"),
|
||||
);
|
||||
|
||||
app
|
||||
app.arg(
|
||||
Arg::new("log-format")
|
||||
.long("log-format")
|
||||
.value_name("format")
|
||||
.help("Customize http log format"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("completions")
|
||||
.long("completions")
|
||||
.value_name("shell")
|
||||
.value_parser(value_parser!(Shell))
|
||||
.help("Print shell completion script for <shell>"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
|
||||
@@ -170,6 +176,7 @@ pub struct Args {
|
||||
pub render_spa: bool,
|
||||
pub render_try_index: bool,
|
||||
pub enable_cors: bool,
|
||||
pub log_http: LogHttp,
|
||||
#[cfg(feature = "tls")]
|
||||
pub tls: Option<(Vec<Certificate>, PrivateKey)>,
|
||||
#[cfg(not(feature = "tls"))]
|
||||
@@ -231,6 +238,10 @@ impl Args {
|
||||
};
|
||||
#[cfg(not(feature = "tls"))]
|
||||
let tls = None;
|
||||
let log_http: LogHttp = matches
|
||||
.value_of("log-format")
|
||||
.unwrap_or(DEFAULT_LOG_FORMAT)
|
||||
.parse()?;
|
||||
|
||||
Ok(Args {
|
||||
addrs,
|
||||
@@ -251,6 +262,7 @@ impl Args {
|
||||
render_try_index,
|
||||
render_spa,
|
||||
tls,
|
||||
log_http,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
47
src/auth.rs
47
src/auth.rs
@@ -198,6 +198,24 @@ impl AuthMethod {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn get_user(&self, authorization: &HeaderValue) -> Option<String> {
|
||||
match self {
|
||||
AuthMethod::Basic => {
|
||||
let value: Vec<u8> =
|
||||
base64::decode(strip_prefix(authorization.as_bytes(), b"Basic ")?).ok()?;
|
||||
let parts: Vec<&str> = std::str::from_utf8(&value).ok()?.split(':').collect();
|
||||
Some(parts[0].to_string())
|
||||
}
|
||||
AuthMethod::Digest => {
|
||||
let digest_value = strip_prefix(authorization.as_bytes(), b"Digest ")?;
|
||||
let digest_vals = to_headermap(digest_value).ok()?;
|
||||
digest_vals
|
||||
.get(b"username".as_ref())
|
||||
.and_then(|b| std::str::from_utf8(*b).ok())
|
||||
.map(|v| v.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn validate(
|
||||
&self,
|
||||
authorization: &HeaderValue,
|
||||
@@ -207,10 +225,9 @@ impl AuthMethod {
|
||||
) -> Option<()> {
|
||||
match self {
|
||||
AuthMethod::Basic => {
|
||||
let value: Vec<u8> =
|
||||
base64::decode(strip_prefix(authorization.as_bytes(), b"Basic ").unwrap())
|
||||
.unwrap();
|
||||
let parts: Vec<&str> = std::str::from_utf8(&value).unwrap().split(':').collect();
|
||||
let basic_value: Vec<u8> =
|
||||
base64::decode(strip_prefix(authorization.as_bytes(), b"Basic ")?).ok()?;
|
||||
let parts: Vec<&str> = std::str::from_utf8(&basic_value).ok()?.split(':').collect();
|
||||
|
||||
if parts[0] != auth_user {
|
||||
return None;
|
||||
@@ -229,13 +246,13 @@ impl AuthMethod {
|
||||
}
|
||||
AuthMethod::Digest => {
|
||||
let digest_value = strip_prefix(authorization.as_bytes(), b"Digest ")?;
|
||||
let user_vals = to_headermap(digest_value).ok()?;
|
||||
let digest_vals = to_headermap(digest_value).ok()?;
|
||||
if let (Some(username), Some(nonce), Some(user_response)) = (
|
||||
user_vals
|
||||
digest_vals
|
||||
.get(b"username".as_ref())
|
||||
.and_then(|b| std::str::from_utf8(*b).ok()),
|
||||
user_vals.get(b"nonce".as_ref()),
|
||||
user_vals.get(b"response".as_ref()),
|
||||
digest_vals.get(b"nonce".as_ref()),
|
||||
digest_vals.get(b"response".as_ref()),
|
||||
) {
|
||||
match validate_nonce(nonce) {
|
||||
Ok(true) => {}
|
||||
@@ -247,12 +264,12 @@ impl AuthMethod {
|
||||
let mut ha = Context::new();
|
||||
ha.consume(method);
|
||||
ha.consume(b":");
|
||||
if let Some(uri) = user_vals.get(b"uri".as_ref()) {
|
||||
if let Some(uri) = digest_vals.get(b"uri".as_ref()) {
|
||||
ha.consume(uri);
|
||||
}
|
||||
let ha = format!("{:x}", ha.compute());
|
||||
let mut correct_response = None;
|
||||
if let Some(qop) = user_vals.get(b"qop".as_ref()) {
|
||||
if let Some(qop) = digest_vals.get(b"qop".as_ref()) {
|
||||
if qop == &b"auth".as_ref() || qop == &b"auth-int".as_ref() {
|
||||
correct_response = Some({
|
||||
let mut c = Context::new();
|
||||
@@ -260,11 +277,11 @@ impl AuthMethod {
|
||||
c.consume(b":");
|
||||
c.consume(nonce);
|
||||
c.consume(b":");
|
||||
if let Some(nc) = user_vals.get(b"nc".as_ref()) {
|
||||
if let Some(nc) = digest_vals.get(b"nc".as_ref()) {
|
||||
c.consume(nc);
|
||||
}
|
||||
c.consume(b":");
|
||||
if let Some(cnonce) = user_vals.get(b"cnonce".as_ref()) {
|
||||
if let Some(cnonce) = digest_vals.get(b"cnonce".as_ref()) {
|
||||
c.consume(cnonce);
|
||||
}
|
||||
c.consume(b":");
|
||||
@@ -340,12 +357,12 @@ fn strip_prefix<'a>(search: &'a [u8], prefix: &[u8]) -> Option<&'a [u8]> {
|
||||
|
||||
fn to_headermap(header: &[u8]) -> Result<HashMap<&[u8], &[u8]>, ()> {
|
||||
let mut sep = Vec::new();
|
||||
let mut asign = Vec::new();
|
||||
let mut assign = Vec::new();
|
||||
let mut i: usize = 0;
|
||||
let mut esc = false;
|
||||
for c in header {
|
||||
match (c, esc) {
|
||||
(b'=', false) => asign.push(i),
|
||||
(b'=', false) => assign.push(i),
|
||||
(b',', false) => sep.push(i),
|
||||
(b'"', false) => esc = true,
|
||||
(b'"', true) => esc = false,
|
||||
@@ -357,7 +374,7 @@ fn to_headermap(header: &[u8]) -> Result<HashMap<&[u8], &[u8]>, ()> {
|
||||
|
||||
i = 0;
|
||||
let mut ret = HashMap::new();
|
||||
for (&k, &a) in sep.iter().zip(asign.iter()) {
|
||||
for (&k, &a) in sep.iter().zip(assign.iter()) {
|
||||
while header[i] == b' ' {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
99
src/log_http.rs
Normal file
99
src/log_http.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
use std::{collections::HashMap, str::FromStr, sync::Arc};
|
||||
|
||||
use crate::{args::Args, server::Request};
|
||||
|
||||
pub const DEFAULT_LOG_FORMAT: &str = r#"$remote_addr "$request" $status"#;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LogHttp {
|
||||
elems: Vec<LogElement>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum LogElement {
|
||||
Variable(String),
|
||||
Header(String),
|
||||
Literal(String),
|
||||
}
|
||||
|
||||
impl LogHttp {
|
||||
pub fn data(&self, req: &Request, args: &Arc<Args>) -> HashMap<String, String> {
|
||||
let mut data = HashMap::default();
|
||||
for elem in self.elems.iter() {
|
||||
match elem {
|
||||
LogElement::Variable(name) => match name.as_str() {
|
||||
"request" => {
|
||||
data.insert(name.to_string(), format!("{} {}", req.method(), req.uri()));
|
||||
}
|
||||
"remote_user" => {
|
||||
if let Some(user) = req
|
||||
.headers()
|
||||
.get("authorization")
|
||||
.and_then(|v| args.auth_method.get_user(v))
|
||||
{
|
||||
data.insert(name.to_string(), user);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
LogElement::Header(name) => {
|
||||
if let Some(value) = req.headers().get(name).and_then(|v| v.to_str().ok()) {
|
||||
data.insert(name.to_string(), value.to_string());
|
||||
}
|
||||
}
|
||||
LogElement::Literal(_) => {}
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
pub fn log(&self, data: &HashMap<String, String>, err: Option<String>) {
|
||||
if self.elems.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut output = String::new();
|
||||
for elem in self.elems.iter() {
|
||||
match elem {
|
||||
LogElement::Literal(value) => output.push_str(value.as_str()),
|
||||
LogElement::Header(name) | LogElement::Variable(name) => {
|
||||
output.push_str(data.get(name).map(|v| v.as_str()).unwrap_or("-"))
|
||||
}
|
||||
}
|
||||
}
|
||||
match err {
|
||||
Some(err) => error!("{} {}", output, err),
|
||||
None => info!("{}", output),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for LogHttp {
|
||||
type Err = Box<dyn std::error::Error>;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut elems = vec![];
|
||||
let mut is_var = false;
|
||||
let mut cache = String::new();
|
||||
for c in format!("{} ", s).chars() {
|
||||
if c == '$' {
|
||||
if !cache.is_empty() {
|
||||
elems.push(LogElement::Literal(cache.to_string()));
|
||||
}
|
||||
cache.clear();
|
||||
is_var = true;
|
||||
} else if is_var && !(c.is_alphanumeric() || c == '_') {
|
||||
if let Some(value) = cache.strip_prefix("$http_") {
|
||||
elems.push(LogElement::Header(value.replace('_', "-").to_string()));
|
||||
} else if let Some(value) = cache.strip_prefix('$') {
|
||||
elems.push(LogElement::Variable(value.to_string()));
|
||||
}
|
||||
cache.clear();
|
||||
is_var = false;
|
||||
}
|
||||
cache.push(c);
|
||||
}
|
||||
let cache = cache.trim();
|
||||
if !cache.is_empty() {
|
||||
elems.push(LogElement::Literal(cache.to_string()));
|
||||
}
|
||||
Ok(Self { elems })
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod args;
|
||||
mod auth;
|
||||
mod log_http;
|
||||
mod logger;
|
||||
mod server;
|
||||
mod streamer;
|
||||
@@ -78,7 +79,7 @@ fn serve(
|
||||
let inner = inner.clone();
|
||||
let incoming = create_addr_incoming(SocketAddr::new(*ip, port))
|
||||
.map_err(|e| format!("Failed to bind `{}:{}`, {}", ip, port, e))?;
|
||||
let serv_func = move |remote_addr: SocketAddr| {
|
||||
let serve_func = move |remote_addr: SocketAddr| {
|
||||
let inner = inner.clone();
|
||||
async move {
|
||||
Ok::<_, hyper::Error>(service_fn(move |req: Request| {
|
||||
@@ -98,7 +99,7 @@ fn serve(
|
||||
let accepter = TlsAcceptor::new(config.clone(), incoming);
|
||||
let new_service = make_service_fn(move |socket: &TlsStream| {
|
||||
let remote_addr = socket.remote_addr();
|
||||
serv_func(remote_addr)
|
||||
serve_func(remote_addr)
|
||||
});
|
||||
let server = tokio::spawn(hyper::Server::builder(accepter).serve(new_service));
|
||||
handles.push(server);
|
||||
@@ -110,7 +111,7 @@ fn serve(
|
||||
None => {
|
||||
let new_service = make_service_fn(move |socket: &AddrStream| {
|
||||
let remote_addr = socket.remote_addr();
|
||||
serv_func(remote_addr)
|
||||
serve_func(remote_addr)
|
||||
});
|
||||
let server = tokio::spawn(hyper::Server::builder(incoming).serve(new_service));
|
||||
handles.push(server);
|
||||
|
||||
@@ -78,16 +78,17 @@ impl Server {
|
||||
req: Request,
|
||||
addr: SocketAddr,
|
||||
) -> Result<Response, hyper::Error> {
|
||||
let method = req.method().clone();
|
||||
let uri = req.uri().clone();
|
||||
let assets_prefix = self.assets_prefix.clone();
|
||||
let enable_cors = self.args.enable_cors;
|
||||
let mut http_log_data = self.args.log_http.data(&req, &self.args);
|
||||
http_log_data.insert("remote_addr".to_string(), addr.ip().to_string());
|
||||
|
||||
let mut res = match self.handle(req).await {
|
||||
let mut res = match self.clone().handle(req).await {
|
||||
Ok(res) => {
|
||||
let status = res.status().as_u16();
|
||||
http_log_data.insert("status".to_string(), res.status().as_u16().to_string());
|
||||
if !uri.path().starts_with(&assets_prefix) {
|
||||
info!(r#"{} "{} {}" - {}"#, addr.ip(), method, uri, status,);
|
||||
self.args.log_http.log(&http_log_data, None);
|
||||
}
|
||||
res
|
||||
}
|
||||
@@ -95,8 +96,10 @@ impl Server {
|
||||
let mut res = Response::default();
|
||||
let status = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
*res.status_mut() = status;
|
||||
let status = status.as_u16();
|
||||
error!(r#"{} "{} {}" - {} {}"#, addr.ip(), method, uri, status, err);
|
||||
http_log_data.insert("status".to_string(), status.as_u16().to_string());
|
||||
self.args
|
||||
.log_http
|
||||
.log(&http_log_data, Some(err.to_string()));
|
||||
res
|
||||
}
|
||||
};
|
||||
|
||||
13
src/tls.rs
13
src/tls.rs
@@ -125,9 +125,9 @@ impl Accept for TlsAcceptor {
|
||||
// Load public certificate from file.
|
||||
pub fn load_certs(filename: &str) -> Result<Vec<Certificate>, Box<dyn std::error::Error>> {
|
||||
// Open certificate file.
|
||||
let certfile = fs::File::open(&filename)
|
||||
let cert_file = fs::File::open(&filename)
|
||||
.map_err(|e| format!("Failed to access `{}`, {}", &filename, e))?;
|
||||
let mut reader = io::BufReader::new(certfile);
|
||||
let mut reader = io::BufReader::new(cert_file);
|
||||
|
||||
// Load and return certificate.
|
||||
let certs = rustls_pemfile::certs(&mut reader).map_err(|_| "Failed to load certificate")?;
|
||||
@@ -139,17 +139,18 @@ pub fn load_certs(filename: &str) -> Result<Vec<Certificate>, Box<dyn std::error
|
||||
|
||||
// Load private key from file.
|
||||
pub fn load_private_key(filename: &str) -> Result<PrivateKey, Box<dyn std::error::Error>> {
|
||||
// Open keyfile.
|
||||
let keyfile = fs::File::open(&filename)
|
||||
let key_file = fs::File::open(&filename)
|
||||
.map_err(|e| format!("Failed to access `{}`, {}", &filename, e))?;
|
||||
let mut reader = io::BufReader::new(keyfile);
|
||||
let mut reader = io::BufReader::new(key_file);
|
||||
|
||||
// Load and return a single private key.
|
||||
let keys = rustls_pemfile::read_all(&mut reader)
|
||||
.map_err(|e| format!("There was a problem with reading private key: {:?}", e))?
|
||||
.into_iter()
|
||||
.find_map(|item| match item {
|
||||
rustls_pemfile::Item::RSAKey(key) | rustls_pemfile::Item::PKCS8Key(key) => Some(key),
|
||||
rustls_pemfile::Item::RSAKey(key)
|
||||
| rustls_pemfile::Item::PKCS8Key(key)
|
||||
| rustls_pemfile::Item::ECKey(key) => Some(key),
|
||||
_ => None,
|
||||
})
|
||||
.ok_or("No supported private key in file")?;
|
||||
|
||||
@@ -64,7 +64,7 @@ fn allow_upload_delete_can_override(#[with(&["-A"])] server: TestServer) -> Resu
|
||||
fn allow_search(#[with(&["--allow-search"])] server: TestServer) -> Result<(), Error> {
|
||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert!(!paths.is_empty());
|
||||
for p in paths {
|
||||
assert!(p.contains("test.html"));
|
||||
|
||||
11
tests/data/cert_ecdsa.pem
Normal file
11
tests/data/cert_ecdsa.pem
Normal file
@@ -0,0 +1,11 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBfTCCASOgAwIBAgIUfrAUHXIfeM54OLnTIUD9xT6FIwkwCgYIKoZIzj0EAwIw
|
||||
FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDgwMjAxMjQ1NFoXDTMyMDczMDAx
|
||||
MjQ1NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
||||
AQcDQgAEW4tBe0jF2wYSLCvdreb0izR/8sgKNKkbe4xPyA9uNEbtTk58eoO3944R
|
||||
JPT6S5wRTHFpF0BJhQRfiuW4K2EUcaNTMFEwHQYDVR0OBBYEFEebUDkiMJoV2d5W
|
||||
8o+6p4DauHFFMB8GA1UdIwQYMBaAFEebUDkiMJoV2d5W8o+6p4DauHFFMA8GA1Ud
|
||||
EwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAPJvmzqaq/S5yYxeB4se8k2z
|
||||
6pnVNxrTT2CqdPD8Z+7rAiBZAyU+5+KbQq3aZsmuNUx+YOqTDMkaUR/nd/tjnnOX
|
||||
gA==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,3 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
openssl req -subj '/CN=localhost' -x509 -newkey rsa:4096 -keyout key_pkcs8.pem -out cert.pem -nodes -days 3650
|
||||
openssl rsa -in key_pkcs8.pem -out key_pkcs1.pem
|
||||
openssl ecparam -name prime256v1 -genkey -noout -out key_ecdsa.pem
|
||||
openssl req -subj '/CN=localhost' -x509 -key key_ecdsa.pem -out cert_ecdsa.pem -nodes -days 3650
|
||||
5
tests/data/key_ecdsa.pem
Normal file
5
tests/data/key_ecdsa.pem
Normal file
@@ -0,0 +1,5 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEILOQ44lHqD4w12HJKlZJ+Y3u91eUKjabu3UKPSahhC89oAoGCCqGSM49
|
||||
AwEHoUQDQgAEW4tBe0jF2wYSLCvdreb0izR/8sgKNKkbe4xPyA9uNEbtTk58eoO3
|
||||
944RJPT6S5wRTHFpF0BJhQRfiuW4K2EUcQ==
|
||||
-----END EC PRIVATE KEY-----
|
||||
@@ -15,11 +15,11 @@ pub type Error = Box<dyn std::error::Error>;
|
||||
#[allow(dead_code)]
|
||||
pub static FILES: &[&str] = &["test.txt", "test.html", "index.html", "😀.bin"];
|
||||
|
||||
/// Directory names for testing diretory don't exist
|
||||
/// Directory names for testing directory don't exist
|
||||
#[allow(dead_code)]
|
||||
pub static DIR_NO_FOUND: &str = "dir-no-found/";
|
||||
|
||||
/// Directory names for testing diretory don't have index.html
|
||||
/// Directory names for testing directory don't have index.html
|
||||
#[allow(dead_code)]
|
||||
pub static DIR_NO_INDEX: &str = "dir-no-index/";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use rstest::rstest;
|
||||
fn hidden_get_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
|
||||
let resp = reqwest::blocking::get(server.url())?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert_eq!(paths.contains(".git/"), exist);
|
||||
assert_eq!(paths.contains("index.html"), exist);
|
||||
Ok(())
|
||||
@@ -34,7 +34,7 @@ fn hidden_propfind_dir(#[case] server: TestServer, #[case] exist: bool) -> Resul
|
||||
fn hidden_search_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
|
||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
for p in paths {
|
||||
assert_eq!(p.contains("test.html"), exist);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ fn head_dir_zip(server: TestServer) -> Result<(), Error> {
|
||||
fn get_dir_search(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert!(!paths.is_empty());
|
||||
for p in paths {
|
||||
assert!(p.contains("test.html"));
|
||||
@@ -78,7 +78,7 @@ fn get_dir_search(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
||||
fn get_dir_search2(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "😀.bin"))?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert!(!paths.is_empty());
|
||||
for p in paths {
|
||||
assert!(p.contains("😀.bin"));
|
||||
|
||||
78
tests/log_http.rs
Normal file
78
tests/log_http.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
mod fixtures;
|
||||
mod utils;
|
||||
|
||||
use diqwest::blocking::WithDigestAuth;
|
||||
use fixtures::{port, tmpdir, wait_for_port, Error};
|
||||
|
||||
use assert_cmd::prelude::*;
|
||||
use assert_fs::fixture::TempDir;
|
||||
use rstest::rstest;
|
||||
use std::io::Read;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
#[rstest]
|
||||
#[case(&["-a", "/@user:pass", "--log-format", "$remote_user"], false)]
|
||||
#[case(&["-a", "/@user:pass", "--log-format", "$remote_user", "--auth-method", "basic"], true)]
|
||||
fn log_remote_user(
|
||||
tmpdir: TempDir,
|
||||
port: u16,
|
||||
#[case] args: &[&str],
|
||||
#[case] is_basic: bool,
|
||||
) -> Result<(), Error> {
|
||||
let mut child = Command::cargo_bin("dufs")?
|
||||
.arg(tmpdir.path())
|
||||
.arg("-p")
|
||||
.arg(port.to_string())
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
wait_for_port(port);
|
||||
|
||||
let stdout = child.stdout.as_mut().expect("Failed to get stdout");
|
||||
|
||||
let req = fetch!(b"GET", &format!("http://localhost:{}", port));
|
||||
|
||||
let resp = if is_basic {
|
||||
req.basic_auth("user", Some("pass")).send()?
|
||||
} else {
|
||||
req.send_with_digest_auth("user", "pass")?
|
||||
};
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let mut buf = [0; 1000];
|
||||
let buf_len = stdout.read(&mut buf)?;
|
||||
let output = std::str::from_utf8(&buf[0..buf_len])?;
|
||||
|
||||
assert!(output.lines().last().unwrap().ends_with("user"));
|
||||
|
||||
child.kill()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(&["--log-format", ""])]
|
||||
fn no_log(tmpdir: TempDir, port: u16, #[case] args: &[&str]) -> Result<(), Error> {
|
||||
let mut child = Command::cargo_bin("dufs")?
|
||||
.arg(tmpdir.path())
|
||||
.arg("-p")
|
||||
.arg(port.to_string())
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
wait_for_port(port);
|
||||
|
||||
let stdout = child.stdout.as_mut().expect("Failed to get stdout");
|
||||
|
||||
let resp = fetch!(b"GET", &format!("http://localhost:{}", port)).send()?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let mut buf = [0; 1000];
|
||||
let buf_len = stdout.read(&mut buf)?;
|
||||
let output = std::str::from_utf8(&buf[0..buf_len])?;
|
||||
|
||||
assert_eq!(output.lines().last().unwrap(), "");
|
||||
Ok(())
|
||||
}
|
||||
@@ -56,7 +56,7 @@ fn render_try_index3(#[with(&["--render-try-index"])] server: TestServer) -> Res
|
||||
fn render_try_index4(#[case] server: TestServer, #[case] searched: bool) -> Result<(), Error> {
|
||||
let resp = reqwest::blocking::get(format!("{}{}?q={}", server.url(), DIR_NO_INDEX, "😀.bin"))?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert!(!paths.is_empty());
|
||||
assert_eq!(paths.iter().all(|v| v.contains("😀.bin")), searched);
|
||||
Ok(())
|
||||
|
||||
@@ -20,7 +20,7 @@ fn default_not_allow_symlink(server: TestServer, tmpdir: TempDir) -> Result<(),
|
||||
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?;
|
||||
assert_eq!(resp.status(), 404);
|
||||
let resp = reqwest::blocking::get(server.url())?;
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert!(!paths.is_empty());
|
||||
assert!(!paths.contains(&format!("{}/", dir)));
|
||||
Ok(())
|
||||
@@ -39,7 +39,7 @@ fn allow_symlink(
|
||||
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?;
|
||||
assert_eq!(resp.status(), 200);
|
||||
let resp = reqwest::blocking::get(server.url())?;
|
||||
let paths = utils::retrive_index_paths(&resp.text()?);
|
||||
let paths = utils::retrieve_index_paths(&resp.text()?);
|
||||
assert!(!paths.is_empty());
|
||||
assert!(paths.contains(&format!("{}/", dir)));
|
||||
Ok(())
|
||||
|
||||
@@ -17,6 +17,10 @@ use rstest::rstest;
|
||||
"--tls-cert", "tests/data/cert.pem",
|
||||
"--tls-key", "tests/data/key_pkcs1.pem",
|
||||
]))]
|
||||
#[case(server(&[
|
||||
"--tls-cert", "tests/data/cert_ecdsa.pem",
|
||||
"--tls-key", "tests/data/key_ecdsa.pem",
|
||||
]))]
|
||||
fn tls_works(#[case] server: TestServer) -> Result<(), Error> {
|
||||
let client = ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
|
||||
@@ -9,7 +9,7 @@ macro_rules! assert_resp_paths {
|
||||
($resp:ident, $files:expr) => {
|
||||
assert_eq!($resp.status(), 200);
|
||||
let body = $resp.text()?;
|
||||
let paths = self::utils::retrive_index_paths(&body);
|
||||
let paths = self::utils::retrieve_index_paths(&body);
|
||||
assert!(!paths.is_empty());
|
||||
for file in $files {
|
||||
assert!(paths.contains(&file.to_string()));
|
||||
@@ -25,8 +25,8 @@ macro_rules! fetch {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn retrive_index_paths(index: &str) -> HashSet<String> {
|
||||
retrive_index_paths_impl(index).unwrap_or_default()
|
||||
pub fn retrieve_index_paths(index: &str) -> HashSet<String> {
|
||||
retrieve_index_paths_impl(index).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -35,7 +35,7 @@ pub fn encode_uri(v: &str) -> String {
|
||||
parts.join("/")
|
||||
}
|
||||
|
||||
fn retrive_index_paths_impl(index: &str) -> Option<HashSet<String>> {
|
||||
fn retrieve_index_paths_impl(index: &str) -> Option<HashSet<String>> {
|
||||
let lines: Vec<&str> = index.lines().collect();
|
||||
let line = lines.iter().find(|v| v.contains("DATA ="))?;
|
||||
let value: Value = line[7..].parse().ok()?;
|
||||
|
||||
Reference in New Issue
Block a user