feat: add cors

This commit is contained in:
sigoden 2022-05-29 17:33:21 +08:00
parent 06ce7b0175
commit 10aabcb2f2
5 changed files with 146 additions and 20 deletions

103
Cargo.lock generated
View file

@ -130,6 +130,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 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]] [[package]]
name = "blocking" name = "blocking"
version = "1.2.0" version = "1.2.0"
@ -247,6 +256,15 @@ dependencies = [
"cache-padded", "cache-padded",
] ]
[[package]]
name = "cpufeatures"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.3.2"
@ -256,6 +274,26 @@ dependencies = [
"cfg-if", "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]] [[package]]
name = "duf" name = "duf"
version = "0.3.0" version = "0.3.0"
@ -265,6 +303,7 @@ dependencies = [
"base64", "base64",
"clap", "clap",
"futures", "futures",
"headers",
"hyper", "hyper",
"log", "log",
"percent-encoding", "percent-encoding",
@ -412,12 +451,47 @@ dependencies = [
"slab", "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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 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]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.19" version = "0.1.19"
@ -556,6 +630,12 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.5.1" version = "0.5.1"
@ -712,6 +792,17 @@ dependencies = [
"serde", "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]] [[package]]
name = "simple_logger" name = "simple_logger"
version = "2.1.0" version = "2.1.0"
@ -894,12 +985,24 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "waker-fn" name = "waker-fn"
version = "1.1.0" version = "1.1.0"

View file

@ -25,6 +25,7 @@ log = "0.4"
simple_logger = "2.1.0" simple_logger = "2.1.0"
async_zip = "0.0.7" async_zip = "0.0.7"
async-walkdir = "0.2.0" async-walkdir = "0.2.0"
headers = "0.3.7"
[profile.release] [profile.release]
lto = true lto = true

View file

@ -45,7 +45,7 @@ duf folder_name
Only serve static files, disable editing operations such as update or delete 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 Finally, run this command to see a list of all available option

View file

@ -35,10 +35,10 @@ fn app() -> clap::Command<'static> {
.help("Path to a directory for serving files"), .help("Path to a directory for serving files"),
) )
.arg( .arg(
Arg::new("no-edit") Arg::new("no-change")
.short('E') .short('C')
.long("no-edit") .long("no-change")
.help("Disable editing operations such as update or delete"), .help("Disable change operations such as update or delete"),
) )
.arg( .arg(
Arg::new("auth") Arg::new("auth")
@ -47,6 +47,11 @@ fn app() -> clap::Command<'static> {
.help("Authenticate with user and pass") .help("Authenticate with user and pass")
.value_name("user:pass"), .value_name("user:pass"),
) )
.arg(
Arg::new("cors")
.long("cors")
.help("Enable CORS, sets `Access-Control-Allow-Origin: *`"),
)
} }
pub fn matches() -> ArgMatches { pub fn matches() -> ArgMatches {
@ -60,6 +65,7 @@ pub struct Args {
pub path: PathBuf, pub path: PathBuf,
pub readonly: bool, pub readonly: bool,
pub auth: Option<String>, pub auth: Option<String>,
pub cors: bool,
} }
impl Args { impl Args {
@ -72,7 +78,8 @@ impl Args {
let port = matches.value_of_t::<u16>("port")?; let port = matches.value_of_t::<u16>("port")?;
let path = matches.value_of_os("path").unwrap_or_default(); let path = matches.value_of_os("path").unwrap_or_default();
let path = Args::parse_path(path)?; 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()); let auth = matches.value_of("auth").map(|v| v.to_owned());
Ok(Args { Ok(Args {
@ -81,6 +88,7 @@ impl Args {
path, path,
readonly, readonly,
auth, auth,
cors,
}) })
} }

View file

@ -5,7 +5,8 @@ use async_zip::write::{EntryOptions, ZipFileWriter};
use async_zip::Compression; use async_zip::Compression;
use futures::stream::StreamExt; use futures::stream::StreamExt;
use futures::TryStreamExt; 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::service::{make_service_fn, service_fn};
use hyper::{Body, Method, StatusCode}; use hyper::{Body, Method, StatusCode};
use percent_encoding::percent_decode; use percent_encoding::percent_decode;
@ -69,11 +70,15 @@ impl InnerService {
pub async fn call(self: Arc<Self>, req: Request) -> Result<Response, hyper::Error> { pub async fn call(self: Arc<Self>, req: Request) -> Result<Response, hyper::Error> {
let method = req.method().clone(); let method = req.method().clone();
let uri = req.uri().clone(); let uri = req.uri().clone();
let res = self let cors = self.args.cors;
let mut res = self
.handle(req) .handle(req)
.await .await
.unwrap_or_else(|_| status_code!(StatusCode::INTERNAL_SERVER_ERROR)); .unwrap_or_else(|_| status_code!(StatusCode::INTERNAL_SERVER_ERROR));
info!(r#""{} {}" - {}"#, method, uri, res.status()); info!(r#""{} {}" - {}"#, method, uri, res.status());
if cors {
add_cors(&mut res);
}
Ok(res) Ok(res)
} }
@ -81,21 +86,20 @@ impl InnerService {
if !self.auth_guard(&req).unwrap_or_default() { if !self.auth_guard(&req).unwrap_or_default() {
let mut res = status_code!(StatusCode::UNAUTHORIZED); let mut res = status_code!(StatusCode::UNAUTHORIZED);
res.headers_mut() res.headers_mut()
.insert("WWW-Authenticate", HeaderValue::from_static("Basic")); .insert(WWW_AUTHENTICATE, HeaderValue::from_static("Basic"));
return Ok(res); return Ok(res);
} }
match *req.method() {
if req.method() == Method::GET { Method::GET => self.handle_static(req).await,
self.handle_static(req).await Method::PUT => {
} else if req.method() == Method::PUT {
if self.args.readonly { if self.args.readonly {
return Ok(status_code!(StatusCode::FORBIDDEN)); 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 Method::OPTIONS => Ok(status_code!(StatusCode::NO_CONTENT)),
} else { Method::DELETE => self.handle_delete(req).await,
return Ok(status_code!(StatusCode::NOT_FOUND)); _ => Ok(status_code!(StatusCode::NOT_FOUND)),
} }
} }
@ -359,6 +363,16 @@ fn normalize_path<P: AsRef<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::<AccessControlAllowHeaders>(),
);
}
async fn dir_zip<W: AsyncWrite + Unpin>(writer: &mut W, dir: &Path) -> BoxResult<()> { async fn dir_zip<W: AsyncWrite + Unpin>(writer: &mut W, dir: &Path) -> BoxResult<()> {
let mut writer = ZipFileWriter::new(writer); let mut writer = ZipFileWriter::new(writer);
let mut walkdir = WalkDir::new(dir); let mut walkdir = WalkDir::new(dir);