use crate::protocol::adapter::DynProtocolConfig;
use crate::protocol::{ConfigError, ProtocolConfig};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use crate::{transport::transport::Transport, SessionId, TransportError};
use std::sync::Arc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TcpClientConfig {
pub target_address: std::net::SocketAddr,
pub connect_timeout: Duration,
pub nodelay: bool,
pub keepalive: Option<Duration>,
pub read_buffer_size: usize,
pub write_buffer_size: usize,
pub read_timeout: Option<Duration>,
pub write_timeout: Option<Duration>,
pub retry_config: RetryConfig,
pub local_bind_address: Option<std::net::SocketAddr>,
}
impl Default for TcpClientConfig {
fn default() -> Self {
Self {
target_address: "127.0.0.1:80".parse().unwrap(),
connect_timeout: Duration::from_secs(10),
nodelay: true,
keepalive: Some(Duration::from_secs(60)),
read_buffer_size: 8192,
write_buffer_size: 8192,
read_timeout: Some(Duration::from_secs(30)),
write_timeout: Some(Duration::from_secs(30)),
retry_config: RetryConfig::default(),
local_bind_address: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
pub max_retries: u32,
pub retry_interval: Duration,
pub backoff_multiplier: f64,
pub max_retry_interval: Duration,
pub jitter: bool,
}
impl Default for RetryConfig {
fn default() -> Self {
Self {
max_retries: 3,
retry_interval: Duration::from_millis(500),
backoff_multiplier: 2.0,
max_retry_interval: Duration::from_secs(30),
jitter: true,
}
}
}
impl ProtocolConfig for TcpClientConfig {
fn validate(&self) -> Result<(), ConfigError> {
if self.read_buffer_size == 0 {
return Err(ConfigError::InvalidValue {
field: "read_buffer_size".to_string(),
value: "0".to_string(),
reason: "must be > 0".to_string(),
suggestion: "set a positive value".to_string(),
});
}
if self.write_buffer_size == 0 {
return Err(ConfigError::InvalidValue {
field: "write_buffer_size".to_string(),
value: "0".to_string(),
reason: "must be > 0".to_string(),
suggestion: "set a positive value".to_string(),
});
}
if self.retry_config.max_retries > 100 {
return Err(ConfigError::InvalidValue {
field: "max_retries".to_string(),
value: self.retry_config.max_retries.to_string(),
reason: "excessive retry count may cause resource exhaustion".to_string(),
suggestion: "use a reasonable value (< 100)".to_string(),
});
}
Ok(())
}
fn default_config() -> Self {
Self::default()
}
fn merge(mut self, other: Self) -> Self {
if other.target_address.port() != 80 {
self.target_address = other.target_address;
}
if other.connect_timeout != Duration::from_secs(10) {
self.connect_timeout = other.connect_timeout;
}
if !other.nodelay {
self.nodelay = other.nodelay;
}
if other.keepalive.is_some() {
self.keepalive = other.keepalive;
}
if other.read_buffer_size != 8192 {
self.read_buffer_size = other.read_buffer_size;
}
if other.write_buffer_size != 8192 {
self.write_buffer_size = other.write_buffer_size;
}
if other.read_timeout.is_some() {
self.read_timeout = other.read_timeout;
}
if other.write_timeout.is_some() {
self.write_timeout = other.write_timeout;
}
if other.retry_config.max_retries != 3 {
self.retry_config = other.retry_config;
}
if other.local_bind_address.is_some() {
self.local_bind_address = other.local_bind_address;
}
self
}
}
impl TcpClientConfig {
pub fn new(target_address: &str) -> Result<Self, ConfigError> {
let addr = target_address
.parse()
.map_err(|e| ConfigError::InvalidAddress {
address: target_address.to_string(),
reason: format!("Invalid target address: {}", e),
source: Some(Box::new(e)),
})?;
Ok(Self {
target_address: addr,
..Self::default()
})
}
pub fn default_config() -> Self {
Self::default()
}
pub fn with_target_address<A: Into<std::net::SocketAddr>>(mut self, addr: A) -> Self {
self.target_address = addr.into();
self
}
pub fn with_target_str(mut self, addr: &str) -> Result<Self, ConfigError> {
self.target_address = addr.parse().map_err(|e| ConfigError::InvalidAddress {
address: addr.to_string(),
reason: format!("Invalid target address: {}", e),
source: Some(Box::new(e)),
})?;
Ok(self)
}
pub fn with_connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
pub fn with_nodelay(mut self, nodelay: bool) -> Self {
self.nodelay = nodelay;
self
}
pub fn with_keepalive(mut self, keepalive: Option<Duration>) -> Self {
self.keepalive = keepalive;
self
}
pub fn with_read_buffer_size(mut self, size: usize) -> Self {
self.read_buffer_size = size;
self
}
pub fn with_write_buffer_size(mut self, size: usize) -> Self {
self.write_buffer_size = size;
self
}
pub fn with_read_timeout(mut self, timeout: Option<Duration>) -> Self {
self.read_timeout = timeout;
self
}
pub fn with_write_timeout(mut self, timeout: Option<Duration>) -> Self {
self.write_timeout = timeout;
self
}
pub fn with_retry_config(mut self, config: RetryConfig) -> Self {
self.retry_config = config;
self
}
pub fn with_local_bind_address(mut self, addr: Option<std::net::SocketAddr>) -> Self {
self.local_bind_address = addr;
self
}
pub fn build(self) -> Result<Self, ConfigError> {
ProtocolConfig::validate(&self)?;
Ok(self)
}
pub fn high_performance(target_address: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_address)?
.with_nodelay(true)
.with_read_buffer_size(65536)
.with_write_buffer_size(65536)
.with_connect_timeout(Duration::from_secs(5))
.with_keepalive(Some(Duration::from_secs(30))))
}
pub fn low_latency(target_address: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_address)?
.with_nodelay(true)
.with_read_buffer_size(4096)
.with_write_buffer_size(4096)
.with_connect_timeout(Duration::from_secs(3)))
}
pub fn reliable(target_address: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_address)?
.with_retry_config(RetryConfig {
max_retries: 10,
retry_interval: Duration::from_secs(1),
backoff_multiplier: 1.5,
max_retry_interval: Duration::from_secs(60),
jitter: true,
})
.with_connect_timeout(Duration::from_secs(30))
.with_keepalive(Some(Duration::from_secs(120))))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebSocketClientConfig {
pub target_url: String,
pub connect_timeout: Duration,
pub headers: std::collections::HashMap<String, String>,
pub subprotocols: Vec<String>,
pub max_frame_size: usize,
pub max_message_size: usize,
pub ping_interval: Option<Duration>,
pub pong_timeout: Duration,
pub retry_config: RetryConfig,
pub verify_tls: bool,
}
impl Default for WebSocketClientConfig {
fn default() -> Self {
Self {
target_url: "ws://localhost:80/".to_string(),
connect_timeout: Duration::from_secs(10),
headers: std::collections::HashMap::new(),
subprotocols: vec![],
max_frame_size: 64 * 1024,
max_message_size: 1024 * 1024,
ping_interval: Some(Duration::from_secs(30)),
pong_timeout: Duration::from_secs(10),
retry_config: RetryConfig::default(),
verify_tls: true,
}
}
}
impl ProtocolConfig for WebSocketClientConfig {
fn validate(&self) -> Result<(), ConfigError> {
if !self.target_url.starts_with("ws://") && !self.target_url.starts_with("wss://") {
return Err(ConfigError::InvalidValue {
field: "target_url".to_string(),
value: self.target_url.clone(),
reason: "must start with 'ws://' or 'wss://'".to_string(),
suggestion: "use a valid WebSocket URL".to_string(),
});
}
if self.max_frame_size == 0 {
return Err(ConfigError::InvalidValue {
field: "max_frame_size".to_string(),
value: "0".to_string(),
reason: "must be > 0".to_string(),
suggestion: "set a positive value".to_string(),
});
}
if self.max_message_size == 0 {
return Err(ConfigError::InvalidValue {
field: "max_message_size".to_string(),
value: "0".to_string(),
reason: "must be > 0".to_string(),
suggestion: "set a positive value".to_string(),
});
}
Ok(())
}
fn default_config() -> Self {
Self::default()
}
fn merge(mut self, other: Self) -> Self {
if other.target_url != "ws://localhost:80/" {
self.target_url = other.target_url;
}
if other.connect_timeout != Duration::from_secs(10) {
self.connect_timeout = other.connect_timeout;
}
if !other.headers.is_empty() {
self.headers = other.headers;
}
if !other.subprotocols.is_empty() {
self.subprotocols = other.subprotocols;
}
if other.max_frame_size != 64 * 1024 {
self.max_frame_size = other.max_frame_size;
}
if other.max_message_size != 1024 * 1024 {
self.max_message_size = other.max_message_size;
}
if other.ping_interval.is_some() {
self.ping_interval = other.ping_interval;
}
if other.pong_timeout != Duration::from_secs(10) {
self.pong_timeout = other.pong_timeout;
}
if other.retry_config.max_retries != 3 {
self.retry_config = other.retry_config;
}
if !other.verify_tls {
self.verify_tls = other.verify_tls;
}
self
}
}
impl WebSocketClientConfig {
pub fn new(target_url: &str) -> Result<Self, ConfigError> {
if !target_url.starts_with("ws://") && !target_url.starts_with("wss://") {
return Err(ConfigError::InvalidValue {
field: "target_url".to_string(),
value: target_url.to_string(),
reason: "must start with 'ws://' or 'wss://'".to_string(),
suggestion: "use a valid WebSocket URL like 'ws://127.0.0.1:8080/path'".to_string(),
});
}
Ok(Self {
target_url: target_url.to_string(),
..Self::default()
})
}
pub fn default_config() -> Self {
Self::default()
}
pub fn with_target_url<S: Into<String>>(mut self, url: S) -> Self {
self.target_url = url.into();
self
}
pub fn with_connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
pub fn with_header<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.headers.insert(key.into(), value.into());
self
}
pub fn with_headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
self.headers = headers;
self
}
pub fn with_subprotocols(mut self, subprotocols: Vec<String>) -> Self {
self.subprotocols = subprotocols;
self
}
pub fn with_max_frame_size(mut self, size: usize) -> Self {
self.max_frame_size = size;
self
}
pub fn with_max_message_size(mut self, size: usize) -> Self {
self.max_message_size = size;
self
}
pub fn with_ping_interval(mut self, interval: Option<Duration>) -> Self {
self.ping_interval = interval;
self
}
pub fn with_pong_timeout(mut self, timeout: Duration) -> Self {
self.pong_timeout = timeout;
self
}
pub fn with_retry_config(mut self, config: RetryConfig) -> Self {
self.retry_config = config;
self
}
pub fn with_verify_tls(mut self, verify: bool) -> Self {
self.verify_tls = verify;
self
}
pub fn build(self) -> Result<Self, ConfigError> {
ProtocolConfig::validate(&self)?;
Ok(self)
}
pub fn json_api(target_url: &str) -> Result<Self, ConfigError> {
let mut headers = std::collections::HashMap::new();
headers.insert("Content-Type".to_string(), "application/json".to_string());
Ok(Self::new(target_url)?
.with_headers(headers)
.with_subprotocols(vec!["json".to_string()])
.with_max_frame_size(16 * 1024)
.with_max_message_size(512 * 1024))
}
pub fn realtime(target_url: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_url)?
.with_ping_interval(Some(Duration::from_secs(10)))
.with_pong_timeout(Duration::from_secs(5))
.with_max_frame_size(8 * 1024)
.with_connect_timeout(Duration::from_secs(5)))
}
pub fn file_transfer(target_url: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_url)?
.with_max_frame_size(1024 * 1024) .with_max_message_size(100 * 1024 * 1024) .with_ping_interval(None) .with_connect_timeout(Duration::from_secs(30)))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuicClientConfig {
pub target_address: std::net::SocketAddr,
pub server_name: Option<String>,
pub connect_timeout: Duration,
pub verify_certificate: bool,
pub ca_cert_pem: Option<String>,
pub max_concurrent_streams: u64,
pub max_idle_timeout: Duration,
pub keep_alive_interval: Option<Duration>,
pub initial_rtt: Duration,
pub retry_config: RetryConfig,
pub local_bind_address: Option<std::net::SocketAddr>,
}
impl Default for QuicClientConfig {
fn default() -> Self {
Self {
target_address: "127.0.0.1:443".parse().unwrap(),
server_name: None,
connect_timeout: Duration::from_secs(10),
verify_certificate: true,
ca_cert_pem: None,
max_concurrent_streams: 100,
max_idle_timeout: Duration::from_secs(30),
keep_alive_interval: Some(Duration::from_secs(15)),
initial_rtt: Duration::from_millis(100),
retry_config: RetryConfig::default(),
local_bind_address: None,
}
}
}
impl ProtocolConfig for QuicClientConfig {
fn validate(&self) -> Result<(), ConfigError> {
if self.max_concurrent_streams == 0 {
return Err(ConfigError::InvalidValue {
field: "max_concurrent_streams".to_string(),
value: "0".to_string(),
reason: "must be > 0".to_string(),
suggestion: "set a positive value".to_string(),
});
}
Ok(())
}
fn default_config() -> Self {
Self::default()
}
fn merge(mut self, other: Self) -> Self {
if other.target_address.port() != 443 {
self.target_address = other.target_address;
}
if other.server_name.is_some() {
self.server_name = other.server_name;
}
if other.connect_timeout != Duration::from_secs(10) {
self.connect_timeout = other.connect_timeout;
}
if !other.verify_certificate {
self.verify_certificate = other.verify_certificate;
}
if other.ca_cert_pem.is_some() {
self.ca_cert_pem = other.ca_cert_pem;
}
if other.max_concurrent_streams != 100 {
self.max_concurrent_streams = other.max_concurrent_streams;
}
if other.max_idle_timeout != Duration::from_secs(30) {
self.max_idle_timeout = other.max_idle_timeout;
}
if other.keep_alive_interval.is_some() {
self.keep_alive_interval = other.keep_alive_interval;
}
if other.initial_rtt != Duration::from_millis(100) {
self.initial_rtt = other.initial_rtt;
}
if other.retry_config.max_retries != 3 {
self.retry_config = other.retry_config;
}
if other.local_bind_address.is_some() {
self.local_bind_address = other.local_bind_address;
}
self
}
}
impl QuicClientConfig {
pub fn new(target_address: &str) -> Result<Self, ConfigError> {
let addr = target_address
.parse()
.map_err(|e| ConfigError::InvalidAddress {
address: target_address.to_string(),
reason: format!("Invalid target address: {}", e),
source: Some(Box::new(e)),
})?;
Ok(Self {
target_address: addr,
..Self::default()
})
}
pub fn default_config() -> Self {
Self::default()
}
pub fn with_target_address<A: Into<std::net::SocketAddr>>(mut self, addr: A) -> Self {
self.target_address = addr.into();
self
}
pub fn with_target_str(mut self, addr: &str) -> Result<Self, ConfigError> {
self.target_address = addr.parse().map_err(|e| ConfigError::InvalidAddress {
address: addr.to_string(),
reason: format!("Invalid target address: {}", e),
source: Some(Box::new(e)),
})?;
Ok(self)
}
pub fn with_server_name<S: Into<String>>(mut self, name: S) -> Self {
self.server_name = Some(name.into());
self
}
pub fn with_connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
pub fn with_verify_certificate(mut self, verify: bool) -> Self {
self.verify_certificate = verify;
self
}
pub fn danger_skip_verification(mut self) -> Self {
tracing::warn!(
"[SECURITY] QUIC certificate verification disabled; use only for local testing"
);
self.verify_certificate = false;
self
}
pub fn with_ca_cert_pem<S: Into<String>>(mut self, cert_pem: S) -> Self {
self.ca_cert_pem = Some(cert_pem.into());
self
}
pub fn with_max_concurrent_streams(mut self, count: u64) -> Self {
self.max_concurrent_streams = count;
self
}
pub fn with_max_idle_timeout(mut self, timeout: Duration) -> Self {
self.max_idle_timeout = timeout;
self
}
pub fn with_keep_alive_interval(mut self, interval: Option<Duration>) -> Self {
self.keep_alive_interval = interval;
self
}
pub fn with_initial_rtt(mut self, rtt: Duration) -> Self {
self.initial_rtt = rtt;
self
}
pub fn with_retry_config(mut self, config: RetryConfig) -> Self {
self.retry_config = config;
self
}
pub fn with_local_bind_address(mut self, addr: Option<std::net::SocketAddr>) -> Self {
self.local_bind_address = addr;
self
}
pub fn build(self) -> Result<Self, ConfigError> {
ProtocolConfig::validate(&self)?;
Ok(self)
}
pub fn high_performance(target_address: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_address)?
.with_max_concurrent_streams(1000)
.with_initial_rtt(Duration::from_millis(20))
.with_connect_timeout(Duration::from_secs(5)))
}
pub fn low_latency(target_address: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_address)?
.with_initial_rtt(Duration::from_millis(10))
.with_keep_alive_interval(Some(Duration::from_secs(5)))
.with_max_idle_timeout(Duration::from_secs(10)))
}
pub fn insecure(target_address: &str) -> Result<Self, ConfigError> {
Ok(Self::new(target_address)?
.danger_skip_verification()
.with_server_name("localhost"))
}
}
impl DynProtocolConfig for QuicClientConfig {
fn protocol_name(&self) -> &'static str {
"quic"
}
fn validate_dyn(&self) -> Result<(), ConfigError> {
ProtocolConfig::validate(self)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_dyn(&self) -> Box<dyn DynProtocolConfig> {
Box::new(self.clone())
}
}
impl crate::protocol::adapter::DynClientConfig for WebSocketClientConfig {
fn build_connection_dyn(
&self,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = Result<Box<dyn crate::Connection>, crate::error::TransportError>,
> + Send
+ '_,
>,
> {
Box::pin(async move {
let connection = crate::protocol::adapter::ClientConfig::build_connection(self).await?;
Ok(Box::new(connection) as Box<dyn crate::Connection>)
})
}
fn get_target_info(&self) -> String {
self.target_url.clone()
}
fn clone_client_dyn(&self) -> Box<dyn crate::protocol::adapter::DynClientConfig> {
Box::new(self.clone())
}
}
impl crate::protocol::adapter::DynClientConfig for TcpClientConfig {
fn build_connection_dyn(
&self,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = Result<Box<dyn crate::Connection>, crate::error::TransportError>,
> + Send
+ '_,
>,
> {
Box::pin(async move {
let connection = crate::protocol::adapter::ClientConfig::build_connection(self).await?;
Ok(Box::new(connection) as Box<dyn crate::Connection>)
})
}
fn get_target_info(&self) -> String {
self.target_address.to_string()
}
fn clone_client_dyn(&self) -> Box<dyn crate::protocol::adapter::DynClientConfig> {
Box::new(self.clone())
}
}
impl crate::protocol::adapter::DynClientConfig for QuicClientConfig {
fn build_connection_dyn(
&self,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = Result<Box<dyn crate::Connection>, crate::error::TransportError>,
> + Send
+ '_,
>,
> {
Box::pin(async move {
let connection = crate::protocol::adapter::ClientConfig::build_connection(self).await?;
Ok(Box::new(connection) as Box<dyn crate::Connection>)
})
}
fn get_target_info(&self) -> String {
self.target_address.to_string()
}
fn clone_client_dyn(&self) -> Box<dyn crate::protocol::adapter::DynClientConfig> {
Box::new(self.clone())
}
}
impl DynProtocolConfig for TcpClientConfig {
fn protocol_name(&self) -> &'static str {
"tcp"
}
fn validate_dyn(&self) -> Result<(), ConfigError> {
ProtocolConfig::validate(self)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_dyn(&self) -> Box<dyn DynProtocolConfig> {
Box::new(self.clone())
}
}
impl DynProtocolConfig for WebSocketClientConfig {
fn protocol_name(&self) -> &'static str {
"websocket"
}
fn validate_dyn(&self) -> Result<(), ConfigError> {
ProtocolConfig::validate(self)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn clone_dyn(&self) -> Box<dyn DynProtocolConfig> {
Box::new(self.clone())
}
}
pub trait ConnectableConfig {
async fn connect(self, transport: Arc<Transport>) -> Result<SessionId, TransportError>;
}
impl ConnectableConfig for TcpClientConfig {
async fn connect(self, transport: Arc<Transport>) -> Result<SessionId, TransportError> {
tracing::info!("🔌 TCP 客户端开始连接到 {}", self.target_address);
let session_id = SessionId(1); let connection = crate::protocol::adapter::ClientConfig::build_connection(&self).await?;
transport
.set_connection(Box::new(connection), session_id)
.await;
tracing::info!(
"✅ TCP 客户端连接成功: {} -> 会话ID: {}",
self.target_address,
session_id
);
Ok(session_id)
}
}
impl ConnectableConfig for WebSocketClientConfig {
async fn connect(self, transport: Arc<Transport>) -> Result<SessionId, TransportError> {
tracing::info!("🔌 WebSocket 客户端开始连接到 {}", self.target_url);
let session_id = SessionId(1); let connection = crate::protocol::adapter::ClientConfig::build_connection(&self).await?;
transport
.set_connection(Box::new(connection), session_id)
.await;
tracing::info!(
"✅ WebSocket 客户端连接成功: {} -> 会话ID: {}",
self.target_url,
session_id
);
Ok(session_id)
}
}
impl ConnectableConfig for QuicClientConfig {
async fn connect(self, transport: Arc<Transport>) -> Result<SessionId, TransportError> {
tracing::info!("🔌 QUIC 客户端开始连接到 {}", self.target_address);
let session_id = SessionId(1); let connection = crate::protocol::adapter::ClientConfig::build_connection(&self).await?;
transport
.set_connection(Box::new(connection), session_id)
.await;
tracing::info!(
"✅ QUIC 客户端连接成功: {} -> 会话ID: {}",
self.target_address,
session_id
);
Ok(session_id)
}
}