s3compat/
utils.rs

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}