signer-remote 0.4.1

Signer remote communication package.
Documentation
//! HTTP 客户端实现
use reqwest::{Client, RequestBuilder, Response};
use serde::{Serialize, de::DeserializeOwned};
use signer_auth::{SignerJWT, SignerJWTClaims, SignerJWTHeader};
use signer_core::{SignerKeys, SignerUser};

use crate::error::{RemoteError, RemoteResult};

/// HTTP 客户端配置
#[derive(Clone)]
pub struct HttpClientConfig {
    /// 基础路径
    pub base_path: String,
    /// 认证信息
    pub auth: Option<HttpClientAuth>,
}

/// HTTP 客户端认证信息
#[derive(Debug, Clone)]
pub struct HttpClientAuth {
    /// 用户密钥
    pub keys: SignerKeys,
    /// 用户信息
    pub user: SignerUser,
    /// 过期时间(分钟)
    pub expire_duration: chrono::Duration,
}

impl HttpClientConfig {
    /// 创建新的配置
    pub fn new(keys: SignerKeys, user: SignerUser, base_path: String) -> Self {
        Self {
            base_path,
            auth: Some(HttpClientAuth {
                keys,
                user,
                expire_duration: chrono::Duration::minutes(5),
            }),
        }
    }

    /// 创建无认证的配置
    pub fn new_no_auth(base_path: String) -> Self {
        Self {
            base_path,
            auth: None,
        }
    }
}

/// HTTP 客户端
#[derive(Clone)]
pub struct HttpClient {
    /// 内部 reqwest 客户端
    client: Client,
    /// 配置
    config: HttpClientConfig,
}

impl HttpClient {
    /// 创建新的 HTTP 客户端
    pub fn new(config: HttpClientConfig) -> Self {
        let client = Client::builder()
            .user_agent("SignerRemote Client v0.3.2")
            .timeout(std::time::Duration::from_secs(30)) // 设置 30 秒超时
            .connect_timeout(std::time::Duration::from_secs(10)) // 设置 10 秒连接超时
            .build()
            .expect("创建 HTTP 客户端失败");

        Self { client, config }
    }

    /// 获取认证头
    fn get_auth_header(&self) -> RemoteResult<Option<String>> {
        if let Some(auth) = &self.config.auth {
            let jwt = SignerJWT::new(
                SignerJWTHeader::default(&auth.user),
                SignerJWTClaims::default(
                    &auth.keys,
                    &auth.user,
                    self.config.base_path.clone(),
                    uuid::Uuid::new_v4().to_string(),
                )
                .with_expired_duration(auth.expire_duration),
            )
            .encode(&auth.keys)
            .map_err(|e| {
                RemoteError::Internal(format!("编码 JWT 字符串失败: {}", e))
            })?;

            Ok(Some(format!("Bearer {}", jwt)))
        } else {
            Ok(None)
        }
    }

    /// 创建请求构建器
    fn request(&self, method: reqwest::Method, path: &str) -> RemoteResult<RequestBuilder> {
        let url = format!("{}{}", self.config.base_path, path);
        let mut builder = self.client.request(method, url);

        if let Some(auth) = self.get_auth_header()? {
            builder = builder.header(reqwest::header::AUTHORIZATION, auth);
        }

        Ok(builder)
    }

    /// 发送 GET 请求
    pub async fn get<T: DeserializeOwned>(&self, path: &str) -> RemoteResult<T> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        self.process_response(response).await
    }

    /// 发送 GET 请求并返回原始响应
    pub async fn get_raw(&self, path: &str) -> RemoteResult<Response> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        Ok(response)
    }

    /// 发送带自定义头部的 GET 请求
    pub async fn get_with_header<T: DeserializeOwned>(
        &self,
        path: &str,
        header_name: &str,
        header_value: &str,
    ) -> RemoteResult<T> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .header(header_name, header_value)
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        self.process_response(response).await
    }

    /// 发送带查询参数的 GET 请求
    pub async fn get_with_query<T: DeserializeOwned, Q: Serialize>(
        &self,
        path: &str,
        query: &Q,
    ) -> RemoteResult<T> {
        let response = self
            .request(reqwest::Method::GET, path)?
            .query(query)
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        self.process_response(response).await
    }

    /// 发送 HEAD 请求
    pub async fn head(&self, path: &str) -> RemoteResult<Response> {
        let response = self
            .request(reqwest::Method::HEAD, path)?
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        Ok(response)
    }

    /// 发送 POST 请求
    pub async fn post<T: DeserializeOwned, B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> RemoteResult<T> {
        let response = self
            .request(reqwest::Method::POST, path)?
            .json(body)
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        self.process_response(response).await
    }

    /// 发送 POST 请求并返回原始响应
    pub async fn post_raw<B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> RemoteResult<Response> {
        let response = self
            .request(reqwest::Method::POST, path)?
            .json(body)
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        Ok(response)
    }

    /// 发送 PATCH 请求
    pub async fn patch<T: DeserializeOwned, B: Serialize>(
        &self,
        path: &str,
        body: &B,
    ) -> RemoteResult<T> {
        let response = self
            .request(reqwest::Method::PATCH, path)?
            .json(body)
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        self.process_response(response).await
    }

    /// 发送 DELETE 请求
    pub async fn delete<T: DeserializeOwned>(&self, path: &str) -> RemoteResult<T> {
        let response = self
            .request(reqwest::Method::DELETE, path)?
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        self.process_response(response).await
    }

    /// 发送 POST multipart 请求
    pub async fn post_multipart(
        &self,
        path: &str,
        form: reqwest::multipart::Form,
    ) -> RemoteResult<Response> {
        let response = self
            .request(reqwest::Method::POST, path)?
            .multipart(form)
            .send()
            .await
            .map_err(|e| {
                RemoteError::Network(e)
            })?;

        Ok(response)
    }

    /// 处理响应
    async fn process_response<T: DeserializeOwned>(
        &self,
        response: Response,
    ) -> RemoteResult<T> {
        let status = response.status();
        if status.is_success() {
            // 先将响应体读取为文本,以便在出错时打印
            let response_text = response.text().await.map_err(|e| {
                RemoteError::Network(e)
            })?;
            
            // 打印响应体
            tracing::debug!("Received response body: {}", response_text);

            // 尝试将文本反序列化为 T
            let result = serde_json::from_str::<T>(&response_text).map_err(|e| {
                tracing::error!("Failed to deserialize response: {}", e);
                RemoteError::Json {
                    source: e,
                    reason: "Failed to deserialize HTTP response".to_string(),
                    input: response_text.clone(),
                }
            })?;
            Ok(result)
        } else {
            let body = response
                .text()
                .await
                .unwrap_or_else(|_| "无法读取响应体".to_string());
            Err(RemoteError::Internal(format!(
                "HTTP 请求失败: 状态码 {}, 响应体: {}",
                status, body
            )))
        }
    }
}