1use 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}