use crate::error::{Error, Result};
use reqwest::{Client as ReqwestClient, StatusCode};
use std::time::Duration;
use tokio::time::timeout;
use tracing::{debug, error, info};
const DEFAULT_TIMEOUT_SECS: u64 = 30;
pub struct DIDCommClient {
client: ReqwestClient,
timeout_secs: u64,
}
impl DIDCommClient {
pub fn new(timeout_secs: Option<u64>) -> Self {
Self {
client: ReqwestClient::new(),
timeout_secs: timeout_secs.unwrap_or(DEFAULT_TIMEOUT_SECS),
}
}
pub fn with_timeout(mut self, timeout_secs: u64) -> Self {
self.timeout_secs = timeout_secs;
self
}
pub async fn deliver_message(&self, endpoint: &str, message: &str) -> Result<()> {
info!("Delivering DIDComm message to {}", endpoint);
debug!("Message size: {} bytes", message.len());
let request_timeout = Duration::from_secs(self.timeout_secs);
let request = self
.client
.post(endpoint)
.header("Content-Type", "application/didcomm-encrypted+json")
.body(message.to_string());
let response = match timeout(request_timeout, request.send()).await {
Ok(result) => match result {
Ok(response) => response,
Err(e) => return Err(Error::Http(format!("Failed to send message: {}", e))),
},
Err(_) => {
return Err(Error::Http(format!(
"Request timed out after {} seconds",
self.timeout_secs
)))
}
};
match response.status() {
StatusCode::OK | StatusCode::ACCEPTED | StatusCode::CREATED => {
info!("Message delivered successfully");
Ok(())
}
status => {
let error_body = match response.text().await {
Ok(body) => body,
Err(_) => "<unable to read error response>".to_string(),
};
error!(
"Failed to deliver message: Status {}, Body: {}",
status, error_body
);
Err(Error::Http(format!(
"Delivery failed with status code {}: {}",
status, error_body
)))
}
}
}
}
impl Default for DIDCommClient {
fn default() -> Self {
Self::new(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_client_creation() {
let client = DIDCommClient::new(Some(10));
assert_eq!(client.timeout_secs, 10);
let default_client = DIDCommClient::default();
assert_eq!(default_client.timeout_secs, DEFAULT_TIMEOUT_SECS);
let custom_client = DIDCommClient::default().with_timeout(15);
assert_eq!(custom_client.timeout_secs, 15);
}
}