Skip to main content

aliyun_oss/operations/
bucket_tls.rs

1//! TLS version configuration operations.
2
3use crate::client::{BucketOperations, OSSClientInner};
4use crate::error::{ErrorContext, OssError, OssErrorKind, Result};
5use crate::http::client::HttpRequest;
6use crate::types::bucket::BucketName;
7use serde::{Deserialize, Serialize};
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize)]
11#[serde(rename = "TLSConfiguration")]
12struct TlsConfig {
13    #[serde(rename = "TLSVersion")]
14    tls_version: Vec<String>,
15}
16
17#[derive(Debug, Clone, Deserialize)]
18#[serde(rename = "TLSConfiguration")]
19struct TlsConfigResp {
20    #[serde(rename = "TLSVersion", default)]
21    tls_version: Vec<String>,
22}
23
24pub struct PutBucketTlsBuilder {
25    client: Arc<OSSClientInner>,
26    bucket: BucketName,
27    versions: Vec<String>,
28}
29impl PutBucketTlsBuilder {
30    pub(crate) fn new(
31        client: Arc<OSSClientInner>,
32        bucket: BucketName,
33        versions: Vec<String>,
34    ) -> Self {
35        Self {
36            client,
37            bucket,
38            versions,
39        }
40    }
41    pub async fn send(self) -> Result<PutBucketTlsOutput> {
42        let ep = self.client.endpoint.clone();
43        let uri = format!("https://{}.{}?tls", self.bucket.as_str(), ep);
44        let qp = vec![("tls".into(), String::new())];
45        let cfg = TlsConfig {
46            tls_version: self.versions,
47        };
48        let xml = crate::util::xml::to_xml(&cfg)?;
49        let req = HttpRequest::builder()
50            .method(http::Method::PUT)
51            .uri(&uri)
52            .body(bytes::Bytes::from(xml))
53            .build();
54        let r = self
55            .client
56            .send_signed(req, Some(&self.bucket), qp)
57            .await
58            .map_err(|e| OssError {
59                kind: OssErrorKind::TransportError,
60                context: Box::new(ErrorContext {
61                    operation: Some("PutBucketTls".into()),
62                    bucket: Some(self.bucket.to_string()),
63                    endpoint: Some(ep),
64                    ..Default::default()
65                }),
66                source: Some(Box::new(e)),
67            })?;
68        if r.status().is_success() {
69            Ok(PutBucketTlsOutput {
70                request_id: r
71                    .headers
72                    .get("x-oss-request-id")
73                    .and_then(|v| v.to_str().ok())
74                    .unwrap_or("")
75                    .to_string(),
76            })
77        } else {
78            Err(OssError {
79                kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
80                    status_code: r.status().as_u16(),
81                    code: String::new(),
82                    message: String::new(),
83                    request_id: String::new(),
84                    host_id: String::new(),
85                    resource: Some(self.bucket.to_string()),
86                    string_to_sign: None,
87                })),
88                context: Box::new(ErrorContext {
89                    operation: Some("PutBucketTls".into()),
90                    bucket: Some(self.bucket.to_string()),
91                    ..Default::default()
92                }),
93                source: None,
94            })
95        }
96    }
97}
98#[derive(Debug, Clone)]
99pub struct PutBucketTlsOutput {
100    pub request_id: String,
101}
102
103pub struct GetBucketTlsBuilder {
104    client: Arc<OSSClientInner>,
105    bucket: BucketName,
106}
107impl GetBucketTlsBuilder {
108    pub(crate) fn new(client: Arc<OSSClientInner>, bucket: BucketName) -> Self {
109        Self { client, bucket }
110    }
111    pub async fn send(self) -> Result<GetBucketTlsOutput> {
112        let ep = self.client.endpoint.clone();
113        let uri = format!("https://{}.{}?tls", self.bucket.as_str(), ep);
114        let qp = vec![("tls".into(), String::new())];
115        let req = HttpRequest::builder()
116            .method(http::Method::GET)
117            .uri(&uri)
118            .build();
119        let r = self
120            .client
121            .send_signed(req, Some(&self.bucket), qp)
122            .await
123            .map_err(|e| OssError {
124                kind: OssErrorKind::TransportError,
125                context: Box::new(ErrorContext {
126                    operation: Some("GetBucketTls".into()),
127                    bucket: Some(self.bucket.to_string()),
128                    endpoint: Some(ep),
129                    ..Default::default()
130                }),
131                source: Some(Box::new(e)),
132            })?;
133        if r.is_success() {
134            let c: TlsConfigResp = crate::util::xml::from_xml(r.body_as_str().unwrap_or(""))
135                .map_err(|e| OssError {
136                    kind: OssErrorKind::DeserializationError,
137                    context: Box::new(ErrorContext {
138                        operation: Some("parse XML".into()),
139                        bucket: Some(self.bucket.to_string()),
140                        ..Default::default()
141                    }),
142                    source: Some(Box::new(e)),
143                })?;
144            Ok(GetBucketTlsOutput {
145                tls_version: c.tls_version,
146            })
147        } else {
148            Err(OssError {
149                kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
150                    status_code: r.status().as_u16(),
151                    code: String::new(),
152                    message: String::new(),
153                    request_id: String::new(),
154                    host_id: String::new(),
155                    resource: Some(self.bucket.to_string()),
156                    string_to_sign: None,
157                })),
158                context: Box::new(ErrorContext {
159                    operation: Some("GetBucketTls".into()),
160                    bucket: Some(self.bucket.to_string()),
161                    ..Default::default()
162                }),
163                source: None,
164            })
165        }
166    }
167}
168#[derive(Debug, Clone)]
169pub struct GetBucketTlsOutput {
170    pub tls_version: Vec<String>,
171}
172
173impl BucketOperations {
174    pub fn put_tls(&self, versions: Vec<String>) -> PutBucketTlsBuilder {
175        PutBucketTlsBuilder::new(
176            self.client_inner().clone(),
177            self.bucket_name().clone(),
178            versions,
179        )
180    }
181    pub fn get_tls(&self) -> GetBucketTlsBuilder {
182        GetBucketTlsBuilder::new(self.client_inner().clone(), self.bucket_name().clone())
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::client::OSSClientInner;
190    use crate::config::credentials::Credentials;
191    use crate::http::client::{HttpClient, HttpRequest, HttpResponse};
192    use crate::types::region::Region;
193    use std::sync::Mutex;
194
195    struct Rc {
196        r: Arc<Mutex<Vec<HttpRequest>>>,
197    }
198    #[async_trait::async_trait]
199    impl HttpClient for Rc {
200        async fn send(&self, req: HttpRequest) -> crate::error::Result<HttpResponse> {
201            self.r.lock().unwrap().push(req);
202            let mut h = http::HeaderMap::new();
203            h.insert("x-oss-request-id", http::HeaderValue::from_static("rid"));
204            Ok(HttpResponse {
205                status: http::StatusCode::OK,
206                headers: h,
207                body: bytes::Bytes::new(),
208            })
209        }
210    }
211    fn ci() -> (Arc<OSSClientInner>, Arc<Mutex<Vec<HttpRequest>>>) {
212        let rq = Arc::new(Mutex::new(Vec::new()));
213        let h = Arc::new(Rc { r: rq.clone() });
214        let cr = Arc::new(crate::config::credentials::StaticCredentialsProvider::new(
215            Credentials::builder()
216                .access_key_id("ak")
217                .access_key_secret("sk")
218                .build()
219                .unwrap(),
220        ));
221        (
222            Arc::new(OSSClientInner {
223                http: h,
224                credentials: cr,
225                signer: Arc::from(crate::signer::create_signer(crate::signer::SignVersion::V4)),
226                region: Region::CnHangzhou,
227                endpoint: "oss-cn-hangzhou.aliyuncs.com".into(),
228            }),
229            rq,
230        )
231    }
232
233    #[test]
234    fn tls_xml_generation() {
235        let c = TlsConfig {
236            tls_version: vec!["TLSv1.2".into(), "TLSv1.3".into()],
237        };
238        let x = crate::util::xml::to_xml(&c).unwrap();
239        assert!(x.contains("<TLSVersion>TLSv1.2</TLSVersion>"));
240    }
241    #[tokio::test]
242    async fn put_sends_request() {
243        let (i, r) = ci();
244        PutBucketTlsBuilder::new(
245            i,
246            BucketName::new("test-bucket").unwrap(),
247            vec!["TLSv1.2".into()],
248        )
249        .send()
250        .await
251        .unwrap();
252        assert_eq!(r.lock().unwrap()[0].method, http::Method::PUT);
253    }
254}