s3/bucket/
utils.rs

1use std::str::FromStr;
2
3use http::HeaderName;
4
5use crate::bucket::*;
6use crate::command::Command;
7use crate::request::RequestImpl;
8
9impl Bucket {
10    /// Get path_style field of the Bucket struct
11    pub fn is_path_style(&self) -> bool {
12        self.path_style
13    }
14
15    /// Get negated path_style field of the Bucket struct
16    pub fn is_subdomain_style(&self) -> bool {
17        !self.path_style
18    }
19
20    /// Configure bucket to use path-style urls and headers
21    pub fn set_path_style(&mut self) {
22        self.path_style = true;
23    }
24
25    /// Configure bucket to use subdomain style urls and headers \[default\]
26    pub fn set_subdomain_style(&mut self) {
27        self.path_style = false;
28    }
29
30    /// Configure bucket to apply this request timeout to all HTTP
31    /// requests, or no (infinity) timeout if `None`.  Defaults to
32    /// 30 seconds.
33    ///
34    /// Only the [`hyper`] backend obeys this option;
35    /// async code may instead await with a timeout.
36    pub fn set_request_timeout(&mut self, timeout: Option<Duration>) {
37        self.request_timeout = timeout;
38    }
39
40    /// Configure bucket to use the older ListObjects API
41    ///
42    /// If your provider doesn't support the ListObjectsV2 interface, set this to
43    /// use the v1 ListObjects interface instead. This is currently needed at least
44    /// for Google Cloud Storage.
45    pub fn set_listobjects_v1(&mut self) {
46        self.listobjects_v2 = false;
47    }
48
49    /// Configure bucket to use the newer ListObjectsV2 API
50    pub fn set_listobjects_v2(&mut self) {
51        self.listobjects_v2 = true;
52    }
53
54    /// Get a reference to the name of the S3 bucket.
55    pub fn name(&self) -> String {
56        self.name.to_string()
57    }
58
59    // Get a reference to the hostname of the S3 API endpoint.
60    pub fn host(&self) -> String {
61        if self.path_style {
62            self.path_style_host()
63        } else {
64            self.subdomain_style_host()
65        }
66    }
67
68    pub fn url(&self) -> String {
69        if self.path_style {
70            format!(
71                "{}://{}/{}",
72                self.scheme(),
73                self.path_style_host(),
74                self.name()
75            )
76        } else {
77            format!("{}://{}", self.scheme(), self.subdomain_style_host())
78        }
79    }
80
81    /// Get a paths-style reference to the hostname of the S3 API endpoint.
82    pub fn path_style_host(&self) -> String {
83        self.region.host()
84    }
85
86    pub fn subdomain_style_host(&self) -> String {
87        format!("{}.{}", self.name, self.region.host())
88    }
89
90    // pub fn self_host(&self) -> String {
91    //     format!("{}.{}", self.name, self.region.host())
92    // }
93
94    pub fn scheme(&self) -> String {
95        self.region.scheme()
96    }
97
98    /// Get the region this object will connect to.
99    pub fn region(&self) -> Region {
100        self.region.clone()
101    }
102
103    /// Get a reference to the AWS access key.
104    pub fn access_key(&self) -> Result<Option<String>, S3Error> {
105        Ok(self
106            .credentials()
107            .try_read()
108            .map_err(|_| S3Error::RLCredentials)?
109            .access_key
110            .clone()
111            .map(|key| key.replace('\n', "")))
112    }
113
114    /// Get a reference to the AWS secret key.
115    pub fn secret_key(&self) -> Result<Option<String>, S3Error> {
116        Ok(self
117            .credentials()
118            .try_read()
119            .map_err(|_| S3Error::RLCredentials)?
120            .secret_key
121            .clone()
122            .map(|key| key.replace('\n', "")))
123    }
124
125    /// Get a reference to the AWS security token.
126    pub fn security_token(&self) -> Result<Option<String>, S3Error> {
127        Ok(self
128            .credentials()
129            .try_read()
130            .map_err(|_| S3Error::RLCredentials)?
131            .security_token
132            .clone())
133    }
134
135    /// Get a reference to the AWS session token.
136    pub fn session_token(&self) -> Result<Option<String>, S3Error> {
137        Ok(self
138            .credentials()
139            .try_read()
140            .map_err(|_| S3Error::RLCredentials)?
141            .session_token
142            .clone())
143    }
144
145    /// Get a reference to the full [`Credentials`](struct.Credentials.html)
146    /// object used by this `Bucket`.
147    pub fn credentials(&self) -> Arc<RwLock<Credentials>> {
148        self.credentials.clone()
149    }
150
151    /// Change the credentials used by the Bucket.
152    pub fn set_credentials(&mut self, credentials: Credentials) {
153        self.credentials = Arc::new(RwLock::new(credentials));
154    }
155
156    /// Add an extra header to send with requests to S3.
157    ///
158    /// Add an extra header to send with requests. Note that the library
159    /// already sets a number of headers - headers set with this method will be
160    /// overridden by the library headers:
161    ///   * Host
162    ///   * Content-Type
163    ///   * Date
164    ///   * Content-Length
165    ///   * Authorization
166    ///   * X-Amz-Content-Sha256
167    ///   * X-Amz-Date
168    pub fn add_header(&mut self, key: &str, value: &str) {
169        self.extra_headers
170            .insert(HeaderName::from_str(key).unwrap(), value.parse().unwrap());
171    }
172
173    /// Get a reference to the extra headers to be passed to the S3 API.
174    pub fn extra_headers(&self) -> &HeaderMap {
175        &self.extra_headers
176    }
177
178    /// Get a mutable reference to the extra headers to be passed to the S3
179    /// API.
180    pub fn extra_headers_mut(&mut self) -> &mut HeaderMap {
181        &mut self.extra_headers
182    }
183
184    /// Add an extra query pair to the URL used for S3 API access.
185    pub fn add_query(&mut self, key: &str, value: &str) {
186        self.extra_query.insert(key.into(), value.into());
187    }
188
189    /// Get a reference to the extra query pairs to be passed to the S3 API.
190    pub fn extra_query(&self) -> &Query {
191        &self.extra_query
192    }
193
194    /// Get a mutable reference to the extra query pairs to be passed to the S3
195    /// API.
196    pub fn extra_query_mut(&mut self) -> &mut Query {
197        &mut self.extra_query
198    }
199
200    pub fn request_timeout(&self) -> Option<Duration> {
201        self.request_timeout
202    }
203
204    /// Get Bucket location.
205    ///
206    /// # Example:
207    ///
208    /// ```no_run
209    /// use s3::bucket::Bucket;
210    /// use s3::creds::Credentials;
211    /// use anyhow::Result;
212    ///
213    /// # #[tokio::main]
214    /// # async fn main() -> Result<()> {
215    ///
216    /// let bucket_name = "rust-s3-test";
217    /// let region = "us-east-1".parse()?;
218    /// let credentials = Credentials::default()?;
219    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
220    ///
221    /// let (region, status_code) = bucket.location().await?;
222    /// #
223    /// # Ok(())
224    /// # }
225    /// ```
226    pub async fn location(&self) -> Result<(Region, u16), S3Error> {
227        let request = RequestImpl::new(self, "?location", Command::GetBucketLocation)?;
228        let response_data = request.response_data(false).await?;
229        let region_string = String::from_utf8_lossy(response_data.as_slice());
230        let region = match quick_xml::de::from_reader(region_string.as_bytes()) {
231            Ok(r) => {
232                let location_result: BucketLocationResult = r;
233                location_result.region.parse()?
234            }
235            Err(e) => {
236                if response_data.status_code() == 200 {
237                    Region::Custom {
238                        region: "Custom".to_string(),
239                        endpoint: "".to_string(),
240                    }
241                } else {
242                    Region::Custom {
243                        region: format!("Error encountered : {}", e),
244                        endpoint: "".to_string(),
245                    }
246                }
247            }
248        };
249        Ok((region, response_data.status_code()))
250    }
251}