feat: add api to get the hash of a file (#375)

This commit is contained in:
sigoden 2024-04-19 08:48:54 +08:00 committed by GitHub
parent a277698322
commit 9353b2e759
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 0 deletions

1
Cargo.lock generated
View file

@ -507,6 +507,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_yaml", "serde_yaml",
"sha-crypt", "sha-crypt",
"sha2",
"smart-default", "smart-default",
"socket2", "socket2",
"tokio", "tokio",

View file

@ -52,6 +52,7 @@ hyper-util = { version = "0.1", features = ["server-auto", "tokio"] }
http-body-util = "0.1" http-body-util = "0.1"
bytes = "1.5" bytes = "1.5"
pin-project-lite = "0.2" pin-project-lite = "0.2"
sha2 = "0.10.8"
[features] [features]
default = ["tls"] default = ["tls"]

View file

@ -29,6 +29,7 @@ use hyper::{
Method, StatusCode, Uri, Method, StatusCode, Uri,
}; };
use serde::Serialize; use serde::Serialize;
use sha2::{Digest, Sha256};
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
@ -307,6 +308,8 @@ impl Server {
} else if query_params.contains_key("view") { } else if query_params.contains_key("view") {
self.handle_edit_file(path, DataKind::View, head_only, user, &mut res) self.handle_edit_file(path, DataKind::View, head_only, user, &mut res)
.await?; .await?;
} else if query_params.contains_key("hash") {
self.handle_hash_file(path, head_only, &mut res).await?;
} else { } else {
self.handle_send_file(path, headers, head_only, &mut res) self.handle_send_file(path, headers, head_only, &mut res)
.await?; .await?;
@ -915,6 +918,24 @@ impl Server {
Ok(()) Ok(())
} }
async fn handle_hash_file(
&self,
path: &Path,
head_only: bool,
res: &mut Response,
) -> Result<()> {
let output = sha256_file(path).await?;
res.headers_mut()
.typed_insert(ContentType::from(mime_guess::mime::TEXT_HTML_UTF_8));
res.headers_mut()
.typed_insert(ContentLength(output.as_bytes().len() as u64));
if head_only {
return Ok(());
}
*res.body_mut() = body_full(output);
Ok(())
}
async fn handle_propfind_dir( async fn handle_propfind_dir(
&self, &self,
path: &Path, path: &Path,
@ -1716,3 +1737,20 @@ fn parse_upload_offset(headers: &HeaderMap<HeaderValue>, size: u64) -> Result<Op
let (start, _) = parse_range(value, size).ok_or_else(err)?; let (start, _) = parse_range(value, size).ok_or_else(err)?;
Ok(Some(start)) Ok(Some(start))
} }
async fn sha256_file(path: &Path) -> Result<String> {
let mut file = fs::File::open(path).await?;
let mut hasher = Sha256::new();
let mut buffer = [0u8; 8192];
loop {
let bytes_read = file.read(&mut buffer).await?;
if bytes_read == 0 {
break;
}
hasher.update(&buffer[..bytes_read]);
}
let result = hasher.finalize();
Ok(format!("{:x}", result))
}

View file

@ -189,6 +189,21 @@ fn head_file(server: TestServer) -> Result<(), Error> {
Ok(()) Ok(())
} }
#[rstest]
fn hash_file(server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}index.html?hash", server.url()))?;
assert_eq!(
resp.headers().get("content-type").unwrap(),
"text/html; charset=utf-8"
);
assert_eq!(resp.status(), 200);
assert_eq!(
resp.text()?,
"c8dd395e3202674b9512f7b7f956e0d96a8ba8f572e785b0d5413ab83766dbc4"
);
Ok(())
}
#[rstest] #[rstest]
fn get_file_404(server: TestServer) -> Result<(), Error> { fn get_file_404(server: TestServer) -> Result<(), Error> {
let resp = reqwest::blocking::get(format!("{}404", server.url()))?; let resp = reqwest::blocking::get(format!("{}404", server.url()))?;