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};
#[derive(Clone)]
pub struct HttpClientConfig {
pub base_path: String,
pub auth: Option<HttpClientAuth>,
}
#[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,
}
}
}
#[derive(Clone)]
pub struct HttpClient {
client: Client,
config: HttpClientConfig,
}
impl HttpClient {
pub fn new(config: HttpClientConfig) -> Self {
let client = Client::builder()
.user_agent("SignerRemote Client v0.3.2")
.timeout(std::time::Duration::from_secs(30)) .connect_timeout(std::time::Duration::from_secs(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)
}
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
}
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)
}
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
}
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
}
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)
}
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
}
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)
}
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
}
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
}
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);
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
)))
}
}
}