use std::sync::atomic::AtomicU64;
use nodedb_types::error::{NodeDbError, NodeDbResult};
use nodedb_types::protocol::Limits;
use tokio::net::TcpStream;
use super::stream::ConnStream;
use super::tls::{TlsConfig, build_tls_client_config};
pub struct NativeConnection {
pub(super) stream: ConnStream,
pub(super) seq: AtomicU64,
pub(super) authenticated: bool,
pub proto_version: u16,
pub capabilities: u64,
pub server_version: String,
pub limits: Limits,
}
impl NativeConnection {
pub async fn connect(addr: &str) -> NodeDbResult<Self> {
let stream = TcpStream::connect(addr)
.await
.map_err(|e| NodeDbError::sync_connection_failed(format!("connect to {addr}: {e}")))?;
let mut conn = Self {
stream: ConnStream::Plain(stream),
seq: AtomicU64::new(1),
authenticated: false,
proto_version: 0,
capabilities: 0,
server_version: String::new(),
limits: Limits::default(),
};
conn.perform_client_handshake().await?;
Ok(conn)
}
pub async fn connect_tls(addr: &str, tls: &TlsConfig) -> NodeDbResult<Self> {
let tcp = TcpStream::connect(addr)
.await
.map_err(|e| NodeDbError::sync_connection_failed(format!("connect to {addr}: {e}")))?;
let config = build_tls_client_config(tls)?;
let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(config));
let server_name = tls
.server_name
.as_deref()
.unwrap_or_else(|| addr.split(':').next().unwrap_or("localhost"));
let sni = tokio_rustls::rustls::pki_types::ServerName::try_from(server_name.to_string())
.map_err(|e| {
NodeDbError::sync_connection_failed(format!(
"invalid server name '{server_name}': {e}"
))
})?;
let tls_stream = connector.connect(sni, tcp).await.map_err(|e| {
NodeDbError::sync_connection_failed(format!("TLS handshake failed: {e}"))
})?;
let mut conn = Self {
stream: ConnStream::Tls(Box::new(tls_stream)),
seq: AtomicU64::new(1),
authenticated: false,
proto_version: 0,
capabilities: 0,
server_version: String::new(),
limits: Limits::default(),
};
conn.perform_client_handshake().await?;
Ok(conn)
}
pub fn is_authenticated(&self) -> bool {
self.authenticated
}
}