1use std::str::FromStr;
2
3use crate::{bucket::CHUNK_SIZE, serde_types::HeadObjectResult};
4use anyhow::Result;
5
6use async_std::fs::File;
7use async_std::path::Path;
8use futures::io::{AsyncRead, AsyncReadExt};
9
10pub async fn etag_for_path(path: impl AsRef<Path>) -> Result<String> {
11 let mut file = File::open(path).await?;
12 let mut digests = Vec::new();
13 let mut chunks = 0;
14 loop {
15 let chunk = read_chunk(&mut file).await?;
16 let digest: [u8; 16] = md5::compute(&chunk).into();
17 digests.extend_from_slice(&digest);
18 chunks += 1;
19 if chunk.len() < CHUNK_SIZE {
20 break;
21 }
22 }
23 let digest = format!("{:x}", md5::compute(digests));
24 let etag = if chunks <= 1 {
25 digest
26 } else {
27 format!("{}-{}", digest, chunks)
28 };
29 Ok(etag)
30}
31
32pub async fn read_chunk<R: AsyncRead + Unpin>(reader: &mut R) -> Result<Vec<u8>> {
33 let mut chunk = Vec::with_capacity(CHUNK_SIZE);
34 let mut take = reader.take(CHUNK_SIZE as u64);
35 take.read_to_end(&mut chunk).await?;
36
37 Ok(chunk)
38}
39
40#[cfg(feature = "sync")]
41pub fn read_chunk<R: Read>(reader: &mut R) -> Result<Vec<u8>> {
42 let mut chunk = Vec::with_capacity(CHUNK_SIZE);
43 let mut take = reader.take(CHUNK_SIZE as u64);
44 take.read_to_end(&mut chunk)?;
45
46 Ok(chunk)
47}
48pub trait GetAndConvertHeaders {
49 fn get_and_convert<T: FromStr>(&self, header: &str) -> Option<T>;
50 fn get_string(&self, header: &str) -> Option<String>;
51}
52
53impl GetAndConvertHeaders for http::header::HeaderMap {
54 fn get_and_convert<T: FromStr>(&self, header: &str) -> Option<T> {
55 self.get(header)?.to_str().ok()?.parse::<T>().ok()
56 }
57 fn get_string(&self, header: &str) -> Option<String> {
58 Some(self.get(header)?.to_str().ok()?.to_owned())
59 }
60}
61
62impl From<&http::HeaderMap> for HeadObjectResult {
63 fn from(headers: &http::HeaderMap) -> Self {
64 let mut result = HeadObjectResult {
65 accept_ranges: headers.get_string("accept-ranges"),
66 cache_control: headers.get_string("Cache-Control"),
67 content_disposition: headers.get_string("Content-Disposition"),
68 content_encoding: headers.get_string("Content-Encoding"),
69 content_language: headers.get_string("Content-Language"),
70 content_length: headers.get_and_convert("Content-Length"),
71 content_type: headers.get_string("Content-Type"),
72 delete_marker: headers.get_and_convert("x-amz-delete-marker"),
73 e_tag: headers.get_string("ETag"),
74 expiration: headers.get_string("x-amz-expiration"),
75 expires: headers.get_string("Expires"),
76 last_modified: headers.get_string("Last-Modified"),
77 ..Default::default()
78 };
79 let mut values = ::std::collections::HashMap::new();
80 for (key, value) in headers.iter() {
81 if key.as_str().starts_with("x-amz-meta-") {
82 if let Ok(value) = value.to_str() {
83 values.insert(
84 key.as_str()["x-amz-meta-".len()..].to_owned(),
85 value.to_owned(),
86 );
87 }
88 }
89 }
90 result.metadata = Some(values);
91 result.missing_meta = headers.get_and_convert("x-amz-missing-meta");
92 result.object_lock_legal_hold_status = headers.get_string("x-amz-object-lock-legal-hold");
93 result.object_lock_mode = headers.get_string("x-amz-object-lock-mode");
94 result.object_lock_retain_until_date =
95 headers.get_string("x-amz-object-lock-retain-until-date");
96 result.parts_count = headers.get_and_convert("x-amz-mp-parts-count");
97 result.replication_status = headers.get_string("x-amz-replication-status");
98 result.request_charged = headers.get_string("x-amz-request-charged");
99 result.restore = headers.get_string("x-amz-restore");
100 result.sse_customer_algorithm =
101 headers.get_string("x-amz-server-side-encryption-customer-algorithm");
102 result.sse_customer_key_md5 =
103 headers.get_string("x-amz-server-side-encryption-customer-key-MD5");
104 result.ssekms_key_id = headers.get_string("x-amz-server-side-encryption-aws-kms-key-id");
105 result.server_side_encryption = headers.get_string("x-amz-server-side-encryption");
106 result.storage_class = headers.get_string("x-amz-storage-class");
107 result.version_id = headers.get_string("x-amz-version-id");
108 result.website_redirect_location = headers.get_string("x-amz-website-redirect-location");
109 result
110 }
111}