From 10aabcb2f2167c550417a74ff9996e7d4d591555 Mon Sep 17 00:00:00 2001 From: sigoden Date: Sun, 29 May 2022 17:33:21 +0800 Subject: [PATCH] feat: add cors --- Cargo.lock | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 2 +- src/args.rs | 18 ++++++--- src/server.rs | 42 +++++++++++++------- 5 files changed, 146 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2bcbb4..58846d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + [[package]] name = "blocking" version = "1.2.0" @@ -247,6 +256,15 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -256,6 +274,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "duf" version = "0.3.0" @@ -265,6 +303,7 @@ dependencies = [ "base64", "clap", "futures", + "headers", "hyper", "log", "percent-encoding", @@ -412,12 +451,47 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "headers" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha-1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -556,6 +630,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "miniz_oxide" version = "0.5.1" @@ -712,6 +792,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "simple_logger" version = "2.1.0" @@ -894,12 +985,24 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + [[package]] name = "unicode-ident" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "waker-fn" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2ef2d4b..dd755ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ log = "0.4" simple_logger = "2.1.0" async_zip = "0.0.7" async-walkdir = "0.2.0" +headers = "0.3.7" [profile.release] lto = true diff --git a/README.md b/README.md index 3452cd4..47a16fe 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ duf folder_name Only serve static files, disable editing operations such as update or delete ``` -duf --no-edit +duf --no-change ``` Finally, run this command to see a list of all available option diff --git a/src/args.rs b/src/args.rs index 12ba5ca..f50f83b 100644 --- a/src/args.rs +++ b/src/args.rs @@ -35,10 +35,10 @@ fn app() -> clap::Command<'static> { .help("Path to a directory for serving files"), ) .arg( - Arg::new("no-edit") - .short('E') - .long("no-edit") - .help("Disable editing operations such as update or delete"), + Arg::new("no-change") + .short('C') + .long("no-change") + .help("Disable change operations such as update or delete"), ) .arg( Arg::new("auth") @@ -47,6 +47,11 @@ fn app() -> clap::Command<'static> { .help("Authenticate with user and pass") .value_name("user:pass"), ) + .arg( + Arg::new("cors") + .long("cors") + .help("Enable CORS, sets `Access-Control-Allow-Origin: *`"), + ) } pub fn matches() -> ArgMatches { @@ -60,6 +65,7 @@ pub struct Args { pub path: PathBuf, pub readonly: bool, pub auth: Option, + pub cors: bool, } impl Args { @@ -72,7 +78,8 @@ impl Args { let port = matches.value_of_t::("port")?; let path = matches.value_of_os("path").unwrap_or_default(); let path = Args::parse_path(path)?; - let readonly = matches.is_present("no-edit"); + let readonly = matches.is_present("no-change"); + let cors = matches.is_present("cors"); let auth = matches.value_of("auth").map(|v| v.to_owned()); Ok(Args { @@ -81,6 +88,7 @@ impl Args { path, readonly, auth, + cors, }) } diff --git a/src/server.rs b/src/server.rs index 59ed997..e88da23 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,7 +5,8 @@ use async_zip::write::{EntryOptions, ZipFileWriter}; use async_zip::Compression; use futures::stream::StreamExt; use futures::TryStreamExt; -use hyper::header::HeaderValue; +use headers::{AccessControlAllowHeaders, AccessControlAllowOrigin, HeaderMapExt}; +use hyper::header::{HeaderValue, ACCEPT, CONTENT_TYPE, ORIGIN, RANGE, WWW_AUTHENTICATE}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, StatusCode}; use percent_encoding::percent_decode; @@ -69,11 +70,15 @@ impl InnerService { pub async fn call(self: Arc, req: Request) -> Result { let method = req.method().clone(); let uri = req.uri().clone(); - let res = self + let cors = self.args.cors; + let mut res = self .handle(req) .await .unwrap_or_else(|_| status_code!(StatusCode::INTERNAL_SERVER_ERROR)); info!(r#""{} {}" - {}"#, method, uri, res.status()); + if cors { + add_cors(&mut res); + } Ok(res) } @@ -81,21 +86,20 @@ impl InnerService { if !self.auth_guard(&req).unwrap_or_default() { let mut res = status_code!(StatusCode::UNAUTHORIZED); res.headers_mut() - .insert("WWW-Authenticate", HeaderValue::from_static("Basic")); + .insert(WWW_AUTHENTICATE, HeaderValue::from_static("Basic")); return Ok(res); } - - if req.method() == Method::GET { - self.handle_static(req).await - } else if req.method() == Method::PUT { - if self.args.readonly { - return Ok(status_code!(StatusCode::FORBIDDEN)); + match *req.method() { + Method::GET => self.handle_static(req).await, + Method::PUT => { + if self.args.readonly { + return Ok(status_code!(StatusCode::FORBIDDEN)); + } + self.handle_upload(req).await } - self.handle_upload(req).await - } else if req.method() == Method::DELETE { - self.handle_delete(req).await - } else { - return Ok(status_code!(StatusCode::NOT_FOUND)); + Method::OPTIONS => Ok(status_code!(StatusCode::NO_CONTENT)), + Method::DELETE => self.handle_delete(req).await, + _ => Ok(status_code!(StatusCode::NOT_FOUND)), } } @@ -359,6 +363,16 @@ fn normalize_path>(path: P) -> String { } } +fn add_cors(res: &mut Response) { + res.headers_mut() + .typed_insert(AccessControlAllowOrigin::ANY); + res.headers_mut().typed_insert( + vec![RANGE, CONTENT_TYPE, ACCEPT, ORIGIN, WWW_AUTHENTICATE] + .into_iter() + .collect::(), + ); +} + async fn dir_zip(writer: &mut W, dir: &Path) -> BoxResult<()> { let mut writer = ZipFileWriter::new(writer); let mut walkdir = WalkDir::new(dir);