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