use futures::stream::StreamExt;
use futures::TryStreamExt;
use headers::{
- AcceptRanges, AccessControlAllowHeaders, AccessControlAllowOrigin, ContentLength, ContentRange,
- ContentType, ETag, HeaderMap, HeaderMapExt, IfModifiedSince, IfNoneMatch, IfRange,
- LastModified, Range,
+ AcceptRanges, AccessControlAllowCredentials, AccessControlAllowHeaders,
+ AccessControlAllowOrigin, Connection, ContentLength, ContentRange, ContentType, ETag,
+ HeaderMap, HeaderMapExt, IfModifiedSince, IfNoneMatch, IfRange, LastModified, Range,
};
use hyper::header::{
HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_DISPOSITION, CONTENT_TYPE, ORIGIN, RANGE,
const INDEX_NAME: &str = "index.html";
const BUF_SIZE: usize = 1024 * 16;
-macro_rules! status {
- ($res:ident, $status:expr) => {
- *$res.status_mut() = $status;
- *$res.body_mut() = Body::from($status.canonical_reason().unwrap_or_default());
- };
-}
-
pub struct Server {
args: Arc<Args>,
}
let mut res = match self.handle(req).await {
Ok(res) => {
- info!(r#"{} "{} {}" - {}"#, addr, method, uri, res.status());
+ let status = res.status().as_u16();
+ info!(r#"{} "{} {}" - {}"#, addr.ip(), method, uri, status,);
res
}
Err(err) => {
let mut res = Response::default();
let status = StatusCode::INTERNAL_SERVER_ERROR;
- status!(res, status);
- error!(r#"{} "{} {}" - {} {}"#, addr, method, uri, status, err);
+ *res.status_mut() = status;
+ let status = status.as_u16();
+ error!(r#"{} "{} {}" - {} {}"#, addr.ip(), method, uri, status, err);
res
}
};
let path = match self.extract_path(req_path) {
Some(v) => v,
None => {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(&mut res);
return Ok(res);
}
};
let render_spa = self.args.render_spa;
if !self.args.allow_symlink && !is_miss && !self.is_root_contained(path).await {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
return Ok(res);
}
} else if allow_upload && req_path.ends_with('/') {
self.handle_ls_dir(path, false, head_only, &mut res).await?;
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
}
}
Method::OPTIONS => {
- self.handle_options(&mut res);
+ set_webdav_headers(&mut res);
}
Method::PUT => {
if !allow_upload || (!allow_delete && is_file && size > 0) {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(&mut res);
} else {
self.handle_upload(path, req, &mut res).await?;
}
}
Method::DELETE => {
if !allow_delete {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(&mut res);
} else if !is_miss {
self.handle_delete(path, is_dir, &mut res).await?
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
}
}
method => match method.as_str() {
} else if is_file {
self.handle_propfind_file(path, &mut res).await?;
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
}
}
"PROPPATCH" => {
if is_file {
self.handle_proppatch(req_path, &mut res).await?;
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
}
}
"MKCOL" => {
if !allow_upload || !is_miss {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(&mut res);
} else {
self.handle_mkcol(path, &mut res).await?;
}
}
"COPY" => {
if !allow_upload {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(&mut res);
} else if is_miss {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
} else {
self.handle_copy(path, headers, &mut res).await?
}
}
"MOVE" => {
if !allow_upload || !allow_delete {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(&mut res);
} else if is_miss {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
} else {
self.handle_move(path, headers, &mut res).await?
}
if is_file {
self.handle_lock(req_path, &mut res).await?;
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(&mut res);
}
}
"UNLOCK" => {
// Fake unlock
if is_miss {
- status!(res, StatusCode::NOT_FOUND);
- } else {
- status!(res, StatusCode::OK);
+ status_not_found(&mut res);
}
}
_ => {
- status!(res, StatusCode::METHOD_NOT_ALLOWED);
+ *res.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
}
},
}
let mut file = match fs::File::create(&path).await {
Ok(v) => v,
Err(_) => {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(res);
return Ok(());
}
};
io::copy(&mut body_reader, &mut file).await?;
- status!(res, StatusCode::CREATED);
+ *res.status_mut() = StatusCode::CREATED;
Ok(())
}
false => fs::remove_file(path).await?,
}
- status!(res, StatusCode::NO_CONTENT);
+ status_no_content(res);
Ok(())
}
paths = match self.list_dir(path, path).await {
Ok(paths) => paths,
Err(_) => {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(res);
return Ok(());
}
}
.unwrap(),
);
res.headers_mut()
- .insert("content-type", "application/zip".parse().unwrap());
+ .insert("content-type", HeaderValue::from_static("application/zip"));
if head_only {
return Ok(());
}
self.handle_send_file(&path, headers, head_only, res)
.await?;
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(res)
}
Ok(())
}
self.handle_send_file(&path, headers, head_only, res)
.await?;
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(res)
}
Ok(())
}
} else {
*res.body_mut() = Body::from(FAVICON_ICO);
res.headers_mut()
- .insert("content-type", "image/x-icon".parse().unwrap());
+ .insert("content-type", HeaderValue::from_static("image/x-icon"));
}
Ok(())
}
res.headers_mut().typed_insert(last_modified);
res.headers_mut().typed_insert(etag.clone());
if cached {
- status!(res, StatusCode::NOT_MODIFIED);
+ *res.status_mut() = StatusCode::NOT_MODIFIED;
return Ok(());
}
if headers.typed_get::<Range>().is_some() {
Ok(())
}
- fn handle_options(&self, res: &mut Response) {
- res.headers_mut().insert(
- "Allow",
- "GET,HEAD,PUT,OPTIONS,DELETE,PROPFIND,COPY,MOVE"
- .parse()
- .unwrap(),
- );
- res.headers_mut().insert("DAV", "1".parse().unwrap());
- }
-
async fn handle_propfind_dir(
&self,
path: &Path,
Some(v) => match v.to_str().ok().and_then(|v| v.parse().ok()) {
Some(v) => v,
None => {
- status!(res, StatusCode::BAD_REQUEST);
+ *res.status_mut() = StatusCode::BAD_REQUEST;
return Ok(());
}
},
match self.list_dir(path, &self.args.path).await {
Ok(child) => paths.extend(child),
Err(_) => {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(res);
return Ok(());
}
}
if let Some(pathitem) = self.to_pathitem(path, &self.args.path).await? {
res_multistatus(res, &pathitem.to_dav_xml(self.args.uri_prefix.as_str()));
} else {
- status!(res, StatusCode::NOT_FOUND);
+ status_not_found(res);
}
Ok(())
}
async fn handle_mkcol(&self, path: &Path, res: &mut Response) -> BoxResult<()> {
fs::create_dir_all(path).await?;
- status!(res, StatusCode::CREATED);
+ *res.status_mut() = StatusCode::CREATED;
Ok(())
}
let dest = match self.extract_dest(headers) {
Some(dest) => dest,
None => {
- status!(res, StatusCode::BAD_REQUEST);
+ *res.status_mut() = StatusCode::BAD_REQUEST;
return Ok(());
}
};
let meta = fs::symlink_metadata(path).await?;
if meta.is_dir() {
- status!(res, StatusCode::FORBIDDEN);
+ status_forbid(res);
return Ok(());
}
fs::copy(path, &dest).await?;
- status!(res, StatusCode::NO_CONTENT);
+ status_no_content(res);
Ok(())
}
let dest = match self.extract_dest(headers) {
Some(dest) => dest,
None => {
- status!(res, StatusCode::BAD_REQUEST);
+ *res.status_mut() = StatusCode::BAD_REQUEST;
return Ok(());
}
};
fs::rename(path, &dest).await?;
- status!(res, StatusCode::NO_CONTENT);
+ status_no_content(res);
Ok(())
}
res.headers_mut().insert(
"content-type",
- "application/xml; charset=utf-8".parse().unwrap(),
+ HeaderValue::from_static("application/xml; charset=utf-8"),
);
res.headers_mut()
.insert("lock-token", format!("<{}>", token).parse().unwrap());
};
if !pass {
let value = generate_www_auth(false);
- status!(res, StatusCode::UNAUTHORIZED);
+ set_webdav_headers(res);
+ *res.status_mut() = StatusCode::UNAUTHORIZED;
+ res.headers_mut().typed_insert(Connection::close());
res.headers_mut()
.insert(WWW_AUTHENTICATE, value.parse().unwrap());
}
fn add_cors(res: &mut Response) {
res.headers_mut()
.typed_insert(AccessControlAllowOrigin::ANY);
+ res.headers_mut()
+ .typed_insert(AccessControlAllowCredentials);
+
res.headers_mut().typed_insert(
vec![RANGE, CONTENT_TYPE, ACCEPT, ORIGIN, WWW_AUTHENTICATE]
.into_iter()
*res.status_mut() = StatusCode::MULTI_STATUS;
res.headers_mut().insert(
"content-type",
- "application/xml; charset=utf-8".parse().unwrap(),
+ HeaderValue::from_static("application/xml; charset=utf-8"),
);
*res.body_mut() = Body::from(format!(
r#"<?xml version="1.0" encoding="utf-8" ?>
let parts: Vec<_> = v.split('/').map(urlencoding::encode).collect();
parts.join("/")
}
+
+fn status_forbid(res: &mut Response) {
+ *res.status_mut() = StatusCode::FORBIDDEN;
+}
+
+fn status_not_found(res: &mut Response) {
+ *res.status_mut() = StatusCode::NOT_FOUND;
+}
+
+fn status_no_content(res: &mut Response) {
+ *res.status_mut() = StatusCode::NO_CONTENT;
+}
+
+fn set_webdav_headers(res: &mut Response) {
+ res.headers_mut().insert(
+ "Allow",
+ HeaderValue::from_static("GET,HEAD,PUT,OPTIONS,DELETE,PROPFIND,COPY,MOVE"),
+ );
+ res.headers_mut()
+ .insert("DAV", HeaderValue::from_static("1,2"));
+}