Compare commits

..

3 Commits

Author SHA1 Message Date
sigoden
7c4c264206 chore(release): version v0.13.1 2022-06-06 07:15:48 +08:00
sigoden
c1e0c6bb2f refactor: use logger (#22) 2022-06-06 07:13:22 +08:00
sigoden
f138915f20 fix: escape filename (#21)
close #19
2022-06-06 06:51:35 +08:00
6 changed files with 79 additions and 17 deletions

View File

@@ -2,6 +2,16 @@
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.13.1] - 2022-06-05
### Bug Fixes
- Escape filename ([#21](https://github.com/sigoden/duf/issues/21))
### Refactor
- Use logger ([#22](https://github.com/sigoden/duf/issues/22))
## [0.13.0] - 2022-06-05 ## [0.13.0] - 2022-06-05
### Bug Fixes ### Bug Fixes

27
Cargo.lock generated
View File

@@ -286,18 +286,20 @@ dependencies = [
[[package]] [[package]]
name = "duf" name = "duf"
version = "0.13.0" version = "0.13.1"
dependencies = [ dependencies = [
"async-walkdir", "async-walkdir",
"async_zip", "async_zip",
"base64", "base64",
"chrono", "chrono",
"clap", "clap",
"env_logger",
"futures", "futures",
"get_if_addrs", "get_if_addrs",
"headers", "headers",
"hyper", "hyper",
"lazy_static", "lazy_static",
"log",
"md5", "md5",
"mime_guess", "mime_guess",
"percent-encoding", "percent-encoding",
@@ -309,9 +311,20 @@ dependencies = [
"tokio-rustls", "tokio-rustls",
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util",
"urlencoding",
"uuid", "uuid",
] ]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"humantime",
"log",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "2.5.2" version = "2.5.2"
@@ -572,6 +585,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.19" version = "0.14.19"
@@ -1154,6 +1173,12 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "urlencoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.1.1" version = "1.1.1"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "duf" name = "duf"
version = "0.13.0" version = "0.13.1"
edition = "2021" edition = "2021"
authors = ["sigoden <sigoden@gmail.com>"] authors = ["sigoden <sigoden@gmail.com>"]
description = "Duf is a simple file server." description = "Duf is a simple file server."
@@ -34,6 +34,9 @@ rustls-pemfile = "1"
md5 = "0.7.0" md5 = "0.7.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
uuid = { version = "1.1.1", features = ["v4", "fast-rng"] } uuid = { version = "1.1.1", features = ["v4", "fast-rng"] }
urlencoding = "2.1.0"
env_logger = { version = "0.9.0", default-features = false, features = ["humantime"] }
log = "0.4.17"
[profile.release] [profile.release]
lto = true lto = true

View File

@@ -143,7 +143,7 @@ impl Args {
let uri_prefix = if path_prefix.is_empty() { let uri_prefix = if path_prefix.is_empty() {
"/".to_owned() "/".to_owned()
} else { } else {
format!("/{}/", &path_prefix) format!("/{}/", encode_uri(&path_prefix))
}; };
let cors = matches.is_present("cors"); let cors = matches.is_present("cors");
let auth = match matches.value_of("auth") { let auth = match matches.value_of("auth") {
@@ -237,3 +237,8 @@ pub fn load_private_key(filename: &str) -> BoxResult<PrivateKey> {
} }
Ok(PrivateKey(keys[0].to_owned())) Ok(PrivateKey(keys[0].to_owned()))
} }
pub fn encode_uri(v: &str) -> String {
let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect();
parts.join("/")
}

View File

@@ -2,9 +2,15 @@ mod args;
mod auth; mod auth;
mod server; mod server;
#[macro_use]
extern crate log;
pub type BoxResult<T> = Result<T, Box<dyn std::error::Error>>; pub type BoxResult<T> = Result<T, Box<dyn std::error::Error>>;
use crate::args::{matches, Args}; use std::env;
use std::io::Write;
use crate::args::{encode_uri, matches, Args};
use crate::server::serve; use crate::server::serve;
#[tokio::main] #[tokio::main]
@@ -13,6 +19,16 @@ async fn main() {
} }
async fn run() -> BoxResult<()> { async fn run() -> BoxResult<()> {
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "info")
}
env_logger::builder()
.format(|buf, record| {
let timestamp = buf.timestamp();
writeln!(buf, "[{} {}] {}", timestamp, record.level(), record.args())
})
.init();
let args = Args::parse(matches())?; let args = Args::parse(matches())?;
tokio::select! { tokio::select! {
ret = serve(args) => { ret = serve(args) => {

View File

@@ -1,10 +1,10 @@
use crate::auth::{generate_www_auth, valid_digest}; use crate::auth::{generate_www_auth, valid_digest};
use crate::{Args, BoxResult}; use crate::{encode_uri, Args, BoxResult};
use async_walkdir::WalkDir; use async_walkdir::WalkDir;
use async_zip::write::{EntryOptions, ZipFileWriter}; use async_zip::write::{EntryOptions, ZipFileWriter};
use async_zip::Compression; use async_zip::Compression;
use chrono::{Local, TimeZone, Utc}; use chrono::{TimeZone, Utc};
use futures::stream::StreamExt; use futures::stream::StreamExt;
use futures::TryStreamExt; use futures::TryStreamExt;
use get_if_addrs::get_if_addrs; use get_if_addrs::get_if_addrs;
@@ -120,20 +120,16 @@ impl InnerService {
let uri = req.uri().clone(); let uri = req.uri().clone();
let cors = self.args.cors; let cors = self.args.cors;
let timestamp = Local::now().format("%d/%b/%Y %H:%M:%S");
let mut res = match self.handle(req).await { let mut res = match self.handle(req).await {
Ok(res) => { Ok(res) => {
println!(r#"[{}] "{} {}" - {}"#, timestamp, method, uri, res.status()); info!(r#""{} {}" - {}"#, method, uri, res.status());
res res
} }
Err(err) => { Err(err) => {
let mut res = Response::default(); let mut res = Response::default();
let status = StatusCode::INTERNAL_SERVER_ERROR; let status = StatusCode::INTERNAL_SERVER_ERROR;
status!(res, status); status!(res, status);
eprintln!( error!(r#""{} {}" - {} {}"#, method, uri, status, err);
r#"[{}] "{} {}" - {} {}"#,
timestamp, method, uri, status, err
);
res res
} }
}; };
@@ -363,14 +359,18 @@ impl InnerService {
let path = path.to_owned(); let path = path.to_owned();
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = zip_dir(&mut writer, &path).await { if let Err(e) = zip_dir(&mut writer, &path).await {
eprintln!("Failed to zip {}, {}", path.display(), e); error!("Failed to zip {}, {}", path.display(), e);
} }
}); });
let stream = ReaderStream::new(reader); let stream = ReaderStream::new(reader);
*res.body_mut() = Body::wrap_stream(stream); *res.body_mut() = Body::wrap_stream(stream);
res.headers_mut().insert( res.headers_mut().insert(
CONTENT_DISPOSITION, CONTENT_DISPOSITION,
HeaderValue::from_str(&format!("attachment; filename=\"{}.zip\"", filename,)).unwrap(), HeaderValue::from_str(&format!(
"attachment; filename=\"{}.zip\"",
encode_uri(filename),
))
.unwrap(),
); );
Ok(()) Ok(())
} }
@@ -821,7 +821,10 @@ impl PathItem {
<D:status>HTTP/1.1 200 OK</D:status> <D:status>HTTP/1.1 200 OK</D:status>
</D:propstat> </D:propstat>
</D:response>"#, </D:response>"#,
prefix, self.name, self.base_name, mtime prefix,
encode_uri(&self.name),
urlencoding::encode(&self.base_name),
mtime
), ),
PathType::File | PathType::SymlinkFile => format!( PathType::File | PathType::SymlinkFile => format!(
r#"<D:response> r#"<D:response>
@@ -837,8 +840,8 @@ impl PathItem {
</D:propstat> </D:propstat>
</D:response>"#, </D:response>"#,
prefix, prefix,
self.name, encode_uri(&self.name),
self.base_name, urlencoding::encode(&self.base_name),
self.size.unwrap_or_default(), self.size.unwrap_or_default(),
mtime mtime
), ),