rusty_s3/actions/
head_bucket.rs1use std::time::Duration;
2
3use jiff::Timestamp;
4use url::Url;
5
6use super::S3Action;
7use crate::actions::Method;
8use crate::signing::sign;
9use crate::{Bucket, Credentials, Map};
10
11#[derive(Debug, Clone)]
17pub struct HeadBucket<'a> {
18 bucket: &'a Bucket,
19 credentials: Option<&'a Credentials>,
20
21 query: Map<'a>,
22 headers: Map<'a>,
23}
24
25impl<'a> HeadBucket<'a> {
26 #[inline]
27 #[must_use]
28 pub const fn new(bucket: &'a Bucket, credentials: Option<&'a Credentials>) -> Self {
29 Self {
30 bucket,
31 credentials,
32
33 query: Map::new(),
34 headers: Map::new(),
35 }
36 }
37}
38
39impl<'a> S3Action<'a> for HeadBucket<'a> {
40 const METHOD: Method = Method::Head;
41
42 fn query_mut(&mut self) -> &mut Map<'a> {
43 &mut self.query
44 }
45
46 fn headers_mut(&mut self) -> &mut Map<'a> {
47 &mut self.headers
48 }
49
50 fn sign_with_time(&self, expires_in: Duration, time: &Timestamp) -> Url {
51 let url = self.bucket.base_url().clone();
52
53 match self.credentials {
54 Some(credentials) => sign(
55 time,
56 Self::METHOD,
57 url,
58 credentials.key(),
59 credentials.secret(),
60 credentials.token(),
61 self.bucket.region(),
62 expires_in.as_secs(),
63 self.query.iter(),
64 self.headers.iter(),
65 ),
66 None => crate::signing::util::add_query_params(url, self.query.iter()),
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use pretty_assertions::assert_eq;
74
75 use super::*;
76 use crate::{Bucket, Credentials, UrlStyle};
77
78 #[test]
79 fn aws_example() {
80 let date = Timestamp::from_second(1369353600).unwrap();
82 let expires_in = Duration::from_secs(86400);
83
84 let endpoint = "https://s3.amazonaws.com".parse().unwrap();
85 let bucket = Bucket::new(
86 endpoint,
87 UrlStyle::VirtualHost,
88 "examplebucket",
89 "us-east-1",
90 )
91 .unwrap();
92 let credentials = Credentials::new(
93 "AKIAIOSFODNN7EXAMPLE",
94 "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
95 );
96
97 let action = HeadBucket::new(&bucket, Some(&credentials));
98
99 let url = action.sign_with_time(expires_in, &date);
100
101 let expected = "https://examplebucket.s3.amazonaws.com/?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=97f0c782bfd320e7b75026ed746d7e0c759da7b6bf12ed485bbfef4530c16191";
102
103 assert_eq!(expected, url.as_str());
104 }
105
106 #[test]
107 fn aws_example_custom_query() {
108 let date = Timestamp::from_second(1369353600).unwrap();
110 let expires_in = Duration::from_secs(86400);
111
112 let endpoint = "https://s3.amazonaws.com".parse().unwrap();
113 let bucket = Bucket::new(
114 endpoint,
115 UrlStyle::VirtualHost,
116 "examplebucket",
117 "us-east-1",
118 )
119 .unwrap();
120 let credentials = Credentials::new(
121 "AKIAIOSFODNN7EXAMPLE",
122 "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
123 );
124
125 let mut action = HeadBucket::new(&bucket, Some(&credentials));
126 action
127 .query_mut()
128 .insert("response-content-type", "text/plain");
129
130 let url = action.sign_with_time(expires_in, &date);
131 let expected = "https://examplebucket.s3.amazonaws.com/?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20130524%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130524T000000Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&response-content-type=text%2Fplain&X-Amz-Signature=1f567b0987313c6ed9c0e92e4e3b70590f96e836b91033f659e6457bfa82dcd0";
132
133 assert_eq!(expected, url.as_str());
134 }
135
136 #[test]
137 fn anonymous_custom_query() {
138 let expires_in = Duration::from_secs(86400);
139
140 let endpoint = "https://s3.amazonaws.com".parse().unwrap();
141 let bucket = Bucket::new(
142 endpoint,
143 UrlStyle::VirtualHost,
144 "examplebucket",
145 "us-east-1",
146 )
147 .unwrap();
148
149 let mut action = HeadBucket::new(&bucket, None);
150 action
151 .query_mut()
152 .insert("response-content-type", "text/plain");
153
154 let url = action.sign(expires_in);
155 let expected = "https://examplebucket.s3.amazonaws.com/?response-content-type=text%2Fplain";
156
157 assert_eq!(expected, url.as_str());
158 }
159}