rusty_oss/actions/
head_object.rs

1use std::time::Duration;
2
3use time::OffsetDateTime;
4use url::Url;
5
6use super::OSSAction;
7use crate::actions::Method;
8use crate::signing::sign;
9use crate::{Bucket, Credentials, Map};
10
11/// Retrieve an object's metadata from OSS, using a `HEAD` request.
12///
13/// Find out more about `HeadObject` from the [OSS API Reference][api]
14///
15/// [api]: https://help.aliyun.com/zh/oss/developer-reference/headobject
16#[derive(Debug, Clone)]
17pub struct HeadObject<'a> {
18    bucket: &'a Bucket,
19    credentials: Option<&'a Credentials>,
20    object: &'a str,
21
22    query: Map<'a>,
23    headers: Map<'a>,
24}
25
26impl<'a> HeadObject<'a> {
27    #[inline]
28    pub fn new(bucket: &'a Bucket, credentials: Option<&'a Credentials>, object: &'a str) -> Self {
29        Self {
30            bucket,
31            credentials,
32            object,
33
34            query: Map::new(),
35            headers: Map::new(),
36        }
37    }
38}
39
40impl<'a> OSSAction<'a> for HeadObject<'a> {
41    const METHOD: Method = Method::Head;
42
43    fn query_mut(&mut self) -> &mut Map<'a> {
44        &mut self.query
45    }
46
47    fn headers_mut(&mut self) -> &mut Map<'a> {
48        &mut self.headers
49    }
50
51    fn sign_with_time(&self, expires_in: Duration, time: &OffsetDateTime) -> Url {
52        let url = self.bucket.object_url(self.object).unwrap();
53
54        match self.credentials {
55            Some(credentials) => sign(
56                time,
57                Method::Head,
58                url,
59                credentials.key(),
60                credentials.secret(),
61                credentials.token(),
62                self.bucket.region(),
63                expires_in.as_secs(),
64                self.query.iter(),
65                self.headers.iter(),
66            ),
67            None => crate::signing::util::add_query_params(url, self.query.iter()),
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use time::OffsetDateTime;
75
76    use pretty_assertions::assert_eq;
77
78    use super::*;
79    use crate::{Bucket, Credentials, UrlStyle};
80
81    #[test]
82    fn oss_example() {
83        // Fri, 24 May 2013 00:00:00 GMT
84        let date = OffsetDateTime::from_unix_timestamp(1369353600).unwrap();
85        let expires_in = Duration::from_secs(86400);
86
87        let endpoint = "https://oss-cn-hangzhou.aliyuncs.com".parse().unwrap();
88        let bucket = Bucket::new(
89            endpoint,
90            UrlStyle::VirtualHost,
91            "examplebucket",
92            "cn-hangzhou",
93        )
94        .unwrap();
95        let credentials = Credentials::new(
96            "access_key_id",
97            "access_key_secret",
98        );
99
100        let action = HeadObject::new(&bucket, Some(&credentials), "test.html");
101
102        let url = action.sign_with_time(expires_in, &date);
103        let expected = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/test.html?x-oss-additional-headers=host&x-oss-credential=access_key_id%2F20130524%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20130524T000000Z&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-signature=88c2f4ba0415bf543ebd237e8a9e29312480eb346a85cb74ef8d1be89e53c622";
104        assert_eq!(expected, url.as_str());
105    }
106
107    #[test]
108    fn oss_example_custom_query() {
109        // Fri, 24 May 2013 00:00:00 GMT
110        let date = OffsetDateTime::from_unix_timestamp(1369353600).unwrap();
111        let expires_in = Duration::from_secs(86400);
112
113        let endpoint = "https://oss-cn-hangzhou.aliyuncs.com".parse().unwrap();
114        let bucket = Bucket::new(
115            endpoint,
116            UrlStyle::VirtualHost,
117            "examplebucket",
118            "cn-hangzhou",
119        )
120        .unwrap();
121        let credentials = Credentials::new(
122            "access_key_id",
123            "access_key_secret",
124        );
125
126        let mut action = HeadObject::new(&bucket, Some(&credentials), "test.txt");
127        action
128            .query_mut()
129            .insert("response-content-type", "text/plain");
130
131        let url = action.sign_with_time(expires_in, &date);
132        let expected = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/test.txt?response-content-type=text%2Fplain&x-oss-additional-headers=host&x-oss-credential=access_key_id%2F20130524%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20130524T000000Z&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-signature=228a9d1a301c046fd44daac430f9e75f82d150cae8e125a651d0003d9610e6c7";
133
134        assert_eq!(expected, url.as_str());
135    }
136
137    #[test]
138    fn anonymous_custom_query() {
139        let expires_in = Duration::from_secs(86400);
140
141        let endpoint = "https://oss-cn-hangzhou.aliyuncs.com".parse().unwrap();
142        let bucket = Bucket::new(
143            endpoint,
144            UrlStyle::VirtualHost,
145            "examplebucket",
146            "cn-hangzhou",
147        )
148        .unwrap();
149
150        let mut action = HeadObject::new(&bucket, None, "test.txt");
151        action
152            .query_mut()
153            .insert("response-content-type", "text/plain");
154
155        let url = action.sign(expires_in);
156        let expected =
157            "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/test.txt?response-content-type=text%2Fplain";
158
159        assert_eq!(expected, url.as_str());
160    }
161}