tencentcloud_sms/api/
executor.rs1use crate::api::common::{ApiRequest, TIMEOUT_MULTIPLIER};
2use crate::logging::{debug, error, info, warn};
3use crate::signature::Signer;
4use crate::{SmsClient, SmsError, SmsResult};
5use secrecy::ExposeSecret;
6
7impl SmsClient {
8 pub async fn execute<Req>(&self, request: Req) -> SmsResult<Req::Output>
18 where
19 Req: ApiRequest,
20 {
21 request
23 .validate()
24 .map_err(|e| SmsError::Configuration(e.to_string()))?;
25
26 debug!("开始执行 API: {}", Req::ACTION);
27
28 let total_timeout = self.config.timeout * TIMEOUT_MULTIPLIER;
30 debug!("设置总超时时间: {:?}", total_timeout);
31
32 let result = tokio::time::timeout(total_timeout, async {
34 let mut attempt = 0;
35 let max_attempts = self
36 .config
37 .retry
38 .as_ref()
39 .map(|r| r.max_retries + 1)
40 .unwrap_or(1);
41
42 loop {
43 attempt += 1;
44 debug!("尝试执行 API,第 {}/{} 次", attempt, max_attempts);
45
46 match self.execute_once(&request).await {
47 Ok(result) => {
48 info!("API 执行成功: {}", Req::ACTION);
49 return Ok(result);
50 }
51 Err(e) if e.is_retryable() && attempt < max_attempts => {
52 let delay = self.calculate_backoff(attempt, e.retry_after());
53 warn!("API 执行失败,将在 {:?} 后重试: {}", delay, e);
54 tokio::time::sleep(delay).await;
55 }
56 Err(e) => {
57 error!("API 执行失败: {}", e);
58 return Err(e);
59 }
60 }
61 }
62 })
63 .await;
64
65 match result {
66 Ok(r) => r,
67 Err(_) => {
68 error!("API 执行总超时: {:?}", total_timeout);
69 Err(SmsError::Configuration(format!(
70 "请求总超时: {:?}",
71 total_timeout
72 )))
73 }
74 }
75 }
76
77 async fn execute_once<Req>(&self, request: &Req) -> SmsResult<Req::Output>
79 where
80 Req: ApiRequest,
81 {
82 let timestamp = chrono::Utc::now().timestamp();
83 let payload = serde_json::to_string(request)?;
84 let host = self.config.host();
85
86 let url = if self.config.is_https {
87 format!("https://{}/", host)
88 } else {
89 format!("http://{}/", host)
90 };
91
92 debug!("准备发送 HTTP 请求到: {}", url);
93
94 let signer = Signer::new(
96 self.credential.secret_id(),
97 self.credential.secret_key().expose_secret(),
98 host,
99 Req::ACTION,
100 &payload,
101 timestamp,
102 );
103 let authorization = signer.authorization();
104
105 debug!("签名生成完成,时间戳: {}", timestamp);
106
107 let mut req_builder = self
109 .http_client
110 .post(&url)
111 .header("Authorization", authorization)
112 .header("Content-Type", "application/json; charset=utf-8")
113 .header("Host", host)
114 .header("X-TC-Action", Req::ACTION)
115 .header("X-TC-Version", crate::client::API_VERSION)
116 .header("X-TC-Timestamp", timestamp.to_string())
117 .header("X-TC-Region", self.config.region.as_str());
118
119 if let Some(token) = self.credential.token() {
121 req_builder = req_builder.header("X-TC-Token", token.expose_secret() as &str);
122 }
123
124 debug!("发送 HTTP POST 请求");
126 let response = req_builder.body(payload).send().await?;
127
128 let status = response.status();
129 debug!("收到 HTTP 响应,状态码: {}", status);
130
131 let response = response.error_for_status().map_err(|e| {
133 error!("HTTP 请求失败,状态码: {}: {}", status, e);
134 SmsError::Http(e)
135 })?;
136
137 let response_text = response.text().await?;
138
139 debug!("解析响应 JSON");
141 let parsed_response = Req::parse_response(&response_text)?;
142
143 Req::handle_response(parsed_response)
145 }
146}