From 9353b2e75908a4c00cfdb35df34c88e7c0de472e Mon Sep 17 00:00:00 2001 From: sigoden Date: Fri, 19 Apr 2024 08:48:54 +0800 Subject: [PATCH] feat: add api to get the hash of a file (#375) --- Cargo.lock | 1 + Cargo.toml | 1 + src/server.rs | 38 ++++++++++++++++++++++++++++++++++++++ tests/http.rs | 15 +++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index be55342..2c51186 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,6 +507,7 @@ dependencies = [ "serde_json", "serde_yaml", "sha-crypt", + "sha2", "smart-default", "socket2", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 1fcd455..5a54182 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ hyper-util = { version = "0.1", features = ["server-auto", "tokio"] } http-body-util = "0.1" bytes = "1.5" pin-project-lite = "0.2" +sha2 = "0.10.8" [features] default = ["tls"] diff --git a/src/server.rs b/src/server.rs index d891bc6..832cd1d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -29,6 +29,7 @@ use hyper::{ Method, StatusCode, Uri, }; use serde::Serialize; +use sha2::{Digest, Sha256}; use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashMap; @@ -307,6 +308,8 @@ impl Server { } else if query_params.contains_key("view") { self.handle_edit_file(path, DataKind::View, head_only, user, &mut res) .await?; + } else if query_params.contains_key("hash") { + self.handle_hash_file(path, head_only, &mut res).await?; } else { self.handle_send_file(path, headers, head_only, &mut res) .await?; @@ -915,6 +918,24 @@ impl Server { 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( &self, path: &Path, @@ -1716,3 +1737,20 @@ fn parse_upload_offset(headers: &HeaderMap, size: u64) -> Result Result { + 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)) +} diff --git a/tests/http.rs b/tests/http.rs index f5ad7c4..8ea5894 100644 --- a/tests/http.rs +++ b/tests/http.rs @@ -189,6 +189,21 @@ fn head_file(server: TestServer) -> Result<(), Error> { 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] fn get_file_404(server: TestServer) -> Result<(), Error> { let resp = reqwest::blocking::get(format!("{}404", server.url()))?;