mirror of
https://github.com/sigoden/dufs.git
synced 2026-04-09 17:13:02 +03:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dff489398e | ||
|
|
64e397d18a | ||
|
|
cc0014c183 | ||
|
|
a489c5647a | ||
|
|
0918fb3fe4 | ||
|
|
14efeb6360 | ||
|
|
30b8f75bba | ||
|
|
a39065beff |
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"
|
|
||||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -2,7 +2,17 @@
|
|||||||
|
|
||||||
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.28.0] - 2022-07-31
|
## [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
|
### Bug Fixes
|
||||||
|
|
||||||
|
|||||||
22
Cargo.lock
generated
22
Cargo.lock
generated
@@ -81,9 +81,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.56"
|
version = "0.1.57"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
|
checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -339,7 +339,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dufs"
|
name = "dufs"
|
||||||
version = "0.28.0"
|
version = "0.29.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"assert_fs",
|
"assert_fs",
|
||||||
@@ -550,9 +550,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.5"
|
version = "0.14.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
@@ -1294,9 +1294,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
|
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
]
|
]
|
||||||
@@ -1367,18 +1367,18 @@ checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.140"
|
version = "1.0.141"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
|
checksum = "7af873f2c95b99fcb0bd0fe622a43e29514658873c8ceba88c4cb88833a22500"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.140"
|
version = "1.0.141"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
|
checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dufs"
|
name = "dufs"
|
||||||
version = "0.28.0"
|
version = "0.29.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"
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -52,7 +52,7 @@ OPTIONS:
|
|||||||
-b, --bind <addr>... Specify bind address
|
-b, --bind <addr>... Specify bind address
|
||||||
-p, --port <port> Specify port to listen on [default: 5000]
|
-p, --port <port> Specify port to listen on [default: 5000]
|
||||||
--path-prefix <path> Specify a path prefix
|
--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
|
-a, --auth <rule>... Add auth for path
|
||||||
--auth-method <value> Select auth method [default: digest] [possible values: basic, digest]
|
--auth-method <value> Select auth method [default: digest] [possible values: basic, digest]
|
||||||
-A, --allow-all Allow all operations
|
-A, --allow-all Allow all operations
|
||||||
@@ -128,12 +128,6 @@ Listen on a specific port
|
|||||||
dufs -p 80
|
dufs -p 80
|
||||||
```
|
```
|
||||||
|
|
||||||
Hide directories from directory listings
|
|
||||||
|
|
||||||
```
|
|
||||||
dufs --hidden .git,.DS_Store
|
|
||||||
```
|
|
||||||
|
|
||||||
Use https
|
Use https
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -165,7 +159,10 @@ Delete a file/folder
|
|||||||
curl -X DELETE http://127.0.0.1:5000/path-to-file-or-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`.
|
Dufs supports path level access control. You can control who can do what on which path with `--auth`/`-a`.
|
||||||
|
|
||||||
@@ -188,15 +185,34 @@ dufs -a /@admin:pass1@* -a /ui@designer:pass2 -A
|
|||||||
- Account `admin:pass1` can upload/delete/view/download any files/folders.
|
- 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.
|
- Account `designer:pass2` can upload/delete/view/download any files/folders in the `ui` folder.
|
||||||
|
|
||||||
## Log format
|
|
||||||
|
|
||||||
dufs supports customize http log format via option `--log-format`.
|
### Hidden Paths
|
||||||
|
|
||||||
The default format is `$remote_addr "$request" $status`.
|
Dufs supports hiding paths from directory listings via option `--hidden`.
|
||||||
|
|
||||||
All variables list below:
|
```
|
||||||
|
dufs --hidden .git,.DS_Store
|
||||||
|
```
|
||||||
|
|
||||||
| name | description |
|
`--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_addr | client address |
|
||||||
| $remote_user | user name supplied with authentication |
|
| $remote_user | user name supplied with authentication |
|
||||||
@@ -205,6 +221,9 @@ All variables list below:
|
|||||||
| $http_ | arbitrary request header field. examples: $http_user_agent, $http_referer |
|
| $http_ | arbitrary request header field. examples: $http_user_agent, $http_referer |
|
||||||
|
|
||||||
> use `dufs --log-format=''` to disable http log
|
> use `dufs --log-format=''` to disable http log
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (c) 2022 dufs-developers.
|
Copyright (c) 2022 dufs-developers.
|
||||||
|
|||||||
@@ -231,4 +231,8 @@ body {
|
|||||||
.path a {
|
.path a {
|
||||||
color: #3191ff;
|
color: #3191ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.paths-table tr:hover {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class Uploader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ajax() {
|
ajax() {
|
||||||
Uploader.runings += 1;
|
Uploader.runnings += 1;
|
||||||
const url = getUrl(this.name);
|
const url = getUrl(this.name);
|
||||||
this.lastUptime = Date.now();
|
this.lastUptime = Date.now();
|
||||||
const ajax = new XMLHttpRequest();
|
const ajax = new XMLHttpRequest();
|
||||||
@@ -110,20 +110,20 @@ class Uploader {
|
|||||||
|
|
||||||
complete() {
|
complete() {
|
||||||
this.$uploadStatus.innerHTML = `✓`;
|
this.$uploadStatus.innerHTML = `✓`;
|
||||||
Uploader.runings -= 1;
|
Uploader.runnings -= 1;
|
||||||
Uploader.runQueue();
|
Uploader.runQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
this.$uploadStatus.innerHTML = `✗`;
|
this.$uploadStatus.innerHTML = `✗`;
|
||||||
Uploader.runings -= 1;
|
Uploader.runnings -= 1;
|
||||||
Uploader.runQueue();
|
Uploader.runQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Uploader.globalIdx = 0;
|
Uploader.globalIdx = 0;
|
||||||
|
|
||||||
Uploader.runings = 0;
|
Uploader.runnings = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type Uploader[]
|
* @type Uploader[]
|
||||||
@@ -132,7 +132,7 @@ Uploader.queues = [];
|
|||||||
|
|
||||||
|
|
||||||
Uploader.runQueue = () => {
|
Uploader.runQueue = () => {
|
||||||
if (Uploader.runings > 2) return;
|
if (Uploader.runnings > 2) return;
|
||||||
let uploader = Uploader.queues.shift();
|
let uploader = Uploader.queues.shift();
|
||||||
if (!uploader) return;
|
if (!uploader) return;
|
||||||
uploader.ajax();
|
uploader.ajax();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ pub fn build_cli() -> Command<'static> {
|
|||||||
.arg(
|
.arg(
|
||||||
Arg::new("hidden")
|
Arg::new("hidden")
|
||||||
.long("hidden")
|
.long("hidden")
|
||||||
.help("Hide directories from directory listings, separated by `,`")
|
.help("Hide paths from directory listings, separated by `,`")
|
||||||
.value_name("value"),
|
.value_name("value"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
|||||||
@@ -357,12 +357,12 @@ fn strip_prefix<'a>(search: &'a [u8], prefix: &[u8]) -> Option<&'a [u8]> {
|
|||||||
|
|
||||||
fn to_headermap(header: &[u8]) -> Result<HashMap<&[u8], &[u8]>, ()> {
|
fn to_headermap(header: &[u8]) -> Result<HashMap<&[u8], &[u8]>, ()> {
|
||||||
let mut sep = Vec::new();
|
let mut sep = Vec::new();
|
||||||
let mut asign = Vec::new();
|
let mut assign = Vec::new();
|
||||||
let mut i: usize = 0;
|
let mut i: usize = 0;
|
||||||
let mut esc = false;
|
let mut esc = false;
|
||||||
for c in header {
|
for c in header {
|
||||||
match (c, esc) {
|
match (c, esc) {
|
||||||
(b'=', false) => asign.push(i),
|
(b'=', false) => assign.push(i),
|
||||||
(b',', false) => sep.push(i),
|
(b',', false) => sep.push(i),
|
||||||
(b'"', false) => esc = true,
|
(b'"', false) => esc = true,
|
||||||
(b'"', true) => esc = false,
|
(b'"', true) => esc = false,
|
||||||
@@ -374,7 +374,7 @@ fn to_headermap(header: &[u8]) -> Result<HashMap<&[u8], &[u8]>, ()> {
|
|||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
let mut ret = HashMap::new();
|
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' ' {
|
while header[i] == b' ' {
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ fn serve(
|
|||||||
let inner = inner.clone();
|
let inner = inner.clone();
|
||||||
let incoming = create_addr_incoming(SocketAddr::new(*ip, port))
|
let incoming = create_addr_incoming(SocketAddr::new(*ip, port))
|
||||||
.map_err(|e| format!("Failed to bind `{}:{}`, {}", ip, port, e))?;
|
.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();
|
let inner = inner.clone();
|
||||||
async move {
|
async move {
|
||||||
Ok::<_, hyper::Error>(service_fn(move |req: Request| {
|
Ok::<_, hyper::Error>(service_fn(move |req: Request| {
|
||||||
@@ -99,7 +99,7 @@ fn serve(
|
|||||||
let accepter = TlsAcceptor::new(config.clone(), incoming);
|
let accepter = TlsAcceptor::new(config.clone(), incoming);
|
||||||
let new_service = make_service_fn(move |socket: &TlsStream| {
|
let new_service = make_service_fn(move |socket: &TlsStream| {
|
||||||
let remote_addr = socket.remote_addr();
|
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));
|
let server = tokio::spawn(hyper::Server::builder(accepter).serve(new_service));
|
||||||
handles.push(server);
|
handles.push(server);
|
||||||
@@ -111,7 +111,7 @@ fn serve(
|
|||||||
None => {
|
None => {
|
||||||
let new_service = make_service_fn(move |socket: &AddrStream| {
|
let new_service = make_service_fn(move |socket: &AddrStream| {
|
||||||
let remote_addr = socket.remote_addr();
|
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));
|
let server = tokio::spawn(hyper::Server::builder(incoming).serve(new_service));
|
||||||
handles.push(server);
|
handles.push(server);
|
||||||
|
|||||||
13
src/tls.rs
13
src/tls.rs
@@ -125,9 +125,9 @@ impl Accept for TlsAcceptor {
|
|||||||
// Load public certificate from file.
|
// Load public certificate from file.
|
||||||
pub fn load_certs(filename: &str) -> Result<Vec<Certificate>, Box<dyn std::error::Error>> {
|
pub fn load_certs(filename: &str) -> Result<Vec<Certificate>, Box<dyn std::error::Error>> {
|
||||||
// Open certificate file.
|
// 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))?;
|
.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.
|
// Load and return certificate.
|
||||||
let certs = rustls_pemfile::certs(&mut reader).map_err(|_| "Failed to load 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.
|
// Load private key from file.
|
||||||
pub fn load_private_key(filename: &str) -> Result<PrivateKey, Box<dyn std::error::Error>> {
|
pub fn load_private_key(filename: &str) -> Result<PrivateKey, Box<dyn std::error::Error>> {
|
||||||
// Open keyfile.
|
let key_file = fs::File::open(&filename)
|
||||||
let keyfile = fs::File::open(&filename)
|
|
||||||
.map_err(|e| format!("Failed to access `{}`, {}", &filename, e))?;
|
.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.
|
// Load and return a single private key.
|
||||||
let keys = rustls_pemfile::read_all(&mut reader)
|
let keys = rustls_pemfile::read_all(&mut reader)
|
||||||
.map_err(|e| format!("There was a problem with reading private key: {:?}", e))?
|
.map_err(|e| format!("There was a problem with reading private key: {:?}", e))?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find_map(|item| match item {
|
.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,
|
_ => None,
|
||||||
})
|
})
|
||||||
.ok_or("No supported private key in file")?;
|
.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> {
|
fn allow_search(#[with(&["--allow-search"])] server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
||||||
assert_eq!(resp.status(), 200);
|
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!(!paths.is_empty());
|
||||||
for p in paths {
|
for p in paths {
|
||||||
assert!(p.contains("test.html"));
|
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
|
#!/usr/bin/env bash
|
||||||
openssl req -subj '/CN=localhost' -x509 -newkey rsa:4096 -keyout key_pkcs8.pem -out cert.pem -nodes -days 3650
|
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 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)]
|
#[allow(dead_code)]
|
||||||
pub static FILES: &[&str] = &["test.txt", "test.html", "index.html", "😀.bin"];
|
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)]
|
#[allow(dead_code)]
|
||||||
pub static DIR_NO_FOUND: &str = "dir-no-found/";
|
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)]
|
#[allow(dead_code)]
|
||||||
pub static DIR_NO_INDEX: &str = "dir-no-index/";
|
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> {
|
fn hidden_get_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(server.url())?;
|
let resp = reqwest::blocking::get(server.url())?;
|
||||||
assert_eq!(resp.status(), 200);
|
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(".git/"), exist);
|
||||||
assert_eq!(paths.contains("index.html"), exist);
|
assert_eq!(paths.contains("index.html"), exist);
|
||||||
Ok(())
|
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> {
|
fn hidden_search_dir(#[case] server: TestServer, #[case] exist: bool) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
||||||
assert_eq!(resp.status(), 200);
|
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 {
|
for p in paths {
|
||||||
assert_eq!(p.contains("test.html"), exist);
|
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> {
|
fn get_dir_search(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "test.html"))?;
|
||||||
assert_eq!(resp.status(), 200);
|
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!(!paths.is_empty());
|
||||||
for p in paths {
|
for p in paths {
|
||||||
assert!(p.contains("test.html"));
|
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> {
|
fn get_dir_search2(#[with(&["-A"])] server: TestServer) -> Result<(), Error> {
|
||||||
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "😀.bin"))?;
|
let resp = reqwest::blocking::get(format!("{}?q={}", server.url(), "😀.bin"))?;
|
||||||
assert_eq!(resp.status(), 200);
|
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!(!paths.is_empty());
|
||||||
for p in paths {
|
for p in paths {
|
||||||
assert!(p.contains("😀.bin"));
|
assert!(p.contains("😀.bin"));
|
||||||
|
|||||||
@@ -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> {
|
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"))?;
|
let resp = reqwest::blocking::get(format!("{}{}?q={}", server.url(), DIR_NO_INDEX, "😀.bin"))?;
|
||||||
assert_eq!(resp.status(), 200);
|
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!(!paths.is_empty());
|
||||||
assert_eq!(paths.iter().all(|v| v.contains("😀.bin")), searched);
|
assert_eq!(paths.iter().all(|v| v.contains("😀.bin")), searched);
|
||||||
Ok(())
|
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))?;
|
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?;
|
||||||
assert_eq!(resp.status(), 404);
|
assert_eq!(resp.status(), 404);
|
||||||
let resp = reqwest::blocking::get(server.url())?;
|
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.is_empty());
|
||||||
assert!(!paths.contains(&format!("{}/", dir)));
|
assert!(!paths.contains(&format!("{}/", dir)));
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -39,7 +39,7 @@ fn allow_symlink(
|
|||||||
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?;
|
let resp = reqwest::blocking::get(format!("{}{}/index.html", server.url(), dir))?;
|
||||||
assert_eq!(resp.status(), 200);
|
assert_eq!(resp.status(), 200);
|
||||||
let resp = reqwest::blocking::get(server.url())?;
|
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.is_empty());
|
||||||
assert!(paths.contains(&format!("{}/", dir)));
|
assert!(paths.contains(&format!("{}/", dir)));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ use rstest::rstest;
|
|||||||
"--tls-cert", "tests/data/cert.pem",
|
"--tls-cert", "tests/data/cert.pem",
|
||||||
"--tls-key", "tests/data/key_pkcs1.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> {
|
fn tls_works(#[case] server: TestServer) -> Result<(), Error> {
|
||||||
let client = ClientBuilder::new()
|
let client = ClientBuilder::new()
|
||||||
.danger_accept_invalid_certs(true)
|
.danger_accept_invalid_certs(true)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ macro_rules! assert_resp_paths {
|
|||||||
($resp:ident, $files:expr) => {
|
($resp:ident, $files:expr) => {
|
||||||
assert_eq!($resp.status(), 200);
|
assert_eq!($resp.status(), 200);
|
||||||
let body = $resp.text()?;
|
let body = $resp.text()?;
|
||||||
let paths = self::utils::retrive_index_paths(&body);
|
let paths = self::utils::retrieve_index_paths(&body);
|
||||||
assert!(!paths.is_empty());
|
assert!(!paths.is_empty());
|
||||||
for file in $files {
|
for file in $files {
|
||||||
assert!(paths.contains(&file.to_string()));
|
assert!(paths.contains(&file.to_string()));
|
||||||
@@ -25,8 +25,8 @@ macro_rules! fetch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn retrive_index_paths(index: &str) -> HashSet<String> {
|
pub fn retrieve_index_paths(index: &str) -> HashSet<String> {
|
||||||
retrive_index_paths_impl(index).unwrap_or_default()
|
retrieve_index_paths_impl(index).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -35,7 +35,7 @@ pub fn encode_uri(v: &str) -> String {
|
|||||||
parts.join("/")
|
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 lines: Vec<&str> = index.lines().collect();
|
||||||
let line = lines.iter().find(|v| v.contains("DATA ="))?;
|
let line = lines.iter().find(|v| v.contains("DATA ="))?;
|
||||||
let value: Value = line[7..].parse().ok()?;
|
let value: Value = line[7..].parse().ok()?;
|
||||||
|
|||||||
Reference in New Issue
Block a user