use base64::{engine::general_purpose::STANDARD as B64, Engine};
use tracing::{debug, trace};
use crate::error::{FinTSError, Result};
pub struct FinTSConnection {
url: String,
client: reqwest::Client,
}
impl FinTSConnection {
pub fn new(url: &str) -> Result<Self> {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(300))
.pool_idle_timeout(std::time::Duration::from_secs(2))
.pool_max_idle_per_host(0)
.tcp_nodelay(true)
.build()
.map_err(|e| FinTSError::Transport(format!("Failed to create HTTP client: {}", e)))?;
Ok(Self {
url: url.to_string(),
client,
})
}
pub async fn send(&self, message_bytes: &[u8]) -> Result<Vec<u8>> {
let encoded = B64.encode(message_bytes);
debug!(
"Sending {} bytes (base64: {} bytes) to {}",
message_bytes.len(),
encoded.len(),
self.url
);
trace!("Request (raw): {:?}", String::from_utf8_lossy(message_bytes));
let response = self
.client
.post(&self.url)
.header("Content-Type", "text/plain")
.body(encoded)
.send()
.await
.map_err(|e| FinTSError::Transport(format!("HTTP request failed: {}", e)))?;
let status = response.status();
if !status.is_success() {
let body = response.text().await.unwrap_or_default();
return Err(FinTSError::Http {
status: status.as_u16(),
message: body,
});
}
let response_bytes = response
.bytes()
.await
.map_err(|e| FinTSError::Transport(format!("Failed to read response: {}", e)))?;
let response_text: String = response_bytes
.iter()
.map(|&b| b as char)
.filter(|c| !c.is_whitespace())
.collect();
debug!("Received {} bytes base64 response", response_text.len());
let decoded = B64.decode(response_text.as_bytes()).map_err(|e| {
FinTSError::Transport(format!("Failed to base64-decode response: {}", e))
})?;
debug!("Response: {} bytes decoded", decoded.len());
trace!("Response (raw decoded): {}", String::from_utf8_lossy(&decoded));
Ok(decoded)
}
}