tencentcloud_sms/api/
executor.rs

1use 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    /// 通用的 API 执行方法,支持重试和超时
9    ///
10    /// 此方法封装了所有 API 请求的公共逻辑:
11    /// - 参数验证
12    /// - 重试机制(指数退避)
13    /// - 超时控制
14    /// - HTTP 请求构建
15    /// - 签名生成
16    /// - 响应解析
17    pub async fn execute<Req>(&self, request: Req) -> SmsResult<Req::Output>
18    where
19        Req: ApiRequest,
20    {
21        // 验证请求参数
22        request
23            .validate()
24            .map_err(|e| SmsError::Configuration(e.to_string()))?;
25
26        debug!("开始执行 API: {}", Req::ACTION);
27
28        // 设置总超时时间为配置超时时间的倍数
29        let total_timeout = self.config.timeout * TIMEOUT_MULTIPLIER;
30        debug!("设置总超时时间: {:?}", total_timeout);
31
32        // 使用 timeout 包装整个重试逻辑
33        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    /// 单次执行 API 请求(不重试)
78    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        // 生成签名
95        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        // 构造请求
108        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        // 添加 Token(临时凭证)
120        if let Some(token) = self.credential.token() {
121            req_builder = req_builder.header("X-TC-Token", token.expose_secret() as &str);
122        }
123
124        // 发送请求
125        debug!("发送 HTTP POST 请求");
126        let response = req_builder.body(payload).send().await?;
127
128        let status = response.status();
129        debug!("收到 HTTP 响应,状态码: {}", status);
130
131        // 检查 HTTP 状态码
132        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        // 解析响应
140        debug!("解析响应 JSON");
141        let parsed_response = Req::parse_response(&response_text)?;
142
143        // 处理响应
144        Req::handle_response(parsed_response)
145    }
146}