use anyhow::Result;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
use std::thread;
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream as TokioTcpStream;
#[derive(Debug, Clone, PartialEq)]
pub enum TcpConnectionState {
Disconnected,
Connecting,
Connected,
Error(String),
}
#[derive(Debug, Clone)]
pub struct TcpConnectionInfo {
pub remote_addr: String,
pub remote_port: u16,
pub local_addr: String,
pub local_port: u16,
pub family: String,
pub state: TcpConnectionState,
}
#[derive(Debug, Clone)]
pub struct TcpConnectionHandle {
pub id: u64,
pub stream: Arc<Mutex<Option<TokioTcpStream>>>,
pub info: Arc<Mutex<TcpConnectionInfo>>,
pub buffer: Arc<Mutex<Vec<u8>>>,
pub reading: Arc<AtomicBool>,
}
impl TcpConnectionHandle {
pub fn new(id: u64) -> Self {
Self {
id,
stream: Arc::new(Mutex::new(None)),
info: Arc::new(Mutex::new(TcpConnectionInfo {
remote_addr: String::new(),
remote_port: 0,
local_addr: String::new(),
local_port: 0,
family: String::new(),
state: TcpConnectionState::Disconnected,
})),
buffer: Arc::new(Mutex::new(Vec::new())),
reading: Arc::new(AtomicBool::new(false)),
}
}
pub async fn connect(&self, host: &str, port: u16) -> Result<()> {
let addr = format!("{}:{}", host, port);
{
let mut info = self.info.lock().unwrap();
info.state = TcpConnectionState::Connecting;
}
match TokioTcpStream::connect(&addr).await {
Ok(stream) => {
let peer_addr = stream.peer_addr()?;
let local_addr = stream.local_addr()?;
{
let mut info = self.info.lock().unwrap();
info.remote_addr = peer_addr.ip().to_string();
info.remote_port = peer_addr.port();
info.local_addr = local_addr.ip().to_string();
info.local_port = local_addr.port();
info.family = if peer_addr.is_ipv4() {
"IPv4".to_string()
} else {
"IPv6".to_string()
};
info.state = TcpConnectionState::Connected;
}
let mut stream_guard = self.stream.lock().unwrap();
*stream_guard = Some(stream);
Ok(())
}
Err(e) => {
{
let mut info = self.info.lock().unwrap();
info.state = TcpConnectionState::Error(e.to_string());
}
Err(anyhow::anyhow!("Connection failed: {}", e))
}
}
}
pub async fn write(&self, data: &[u8]) -> Result<usize> {
let mut stream_guard = self.stream.lock().unwrap();
if let Some(ref mut stream) = *stream_guard {
Ok(stream.write(data).await?)
} else {
Err(anyhow::anyhow!("Not connected"))
}
}
pub async fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut stream_guard = self.stream.lock().unwrap();
if let Some(ref mut stream) = *stream_guard {
Ok(stream.read(buf).await?)
} else {
Err(anyhow::anyhow!("Not connected"))
}
}
pub fn is_connected(&self) -> bool {
let info = self.info.lock().unwrap();
info.state == TcpConnectionState::Connected
}
pub fn get_state(&self) -> TcpConnectionState {
let info = self.info.lock().unwrap();
info.state.clone()
}
pub fn get_info(&self) -> TcpConnectionInfo {
let info = self.info.lock().unwrap();
info.clone()
}
pub fn close(&self) {
let mut stream_guard = self.stream.lock().unwrap();
*stream_guard = None;
let mut info = self.info.lock().unwrap();
info.state = TcpConnectionState::Disconnected;
}
pub fn start_reading(&self) {
if self.reading.swap(true, Ordering::SeqCst) {
return; }
let handle = self.clone();
thread::spawn(move || {
let mut buffer = [0u8; 1024];
loop {
if !handle.reading.load(Ordering::SeqCst) {
break;
}
let read_result = {
let mut stream_guard = handle.stream.lock().unwrap();
if let Some(ref mut stream) = *stream_guard {
let result = tokio::runtime::Runtime::new().ok().and_then(|rt| {
let rt = rt;
let fut = async { stream.read(&mut buffer).await };
Some(rt.block_on(fut))
});
result
} else {
None
}
};
match read_result {
Some(Ok(0)) => {
handle.reading.store(false, Ordering::SeqCst);
break;
}
Some(Ok(n)) => {
let data = buffer[..n].to_vec();
let mut buf_guard = handle.buffer.lock().unwrap();
buf_guard.extend_from_slice(&data);
}
Some(Err(_)) => {
handle.reading.store(false, Ordering::SeqCst);
break;
}
None => {
thread::sleep(Duration::from_millis(10));
}
}
}
});
}
pub fn stop_reading(&self) {
self.reading.store(false, Ordering::SeqCst);
}
pub fn consume_buffer(&self) -> Vec<u8> {
let buf_guard = self.buffer.lock().unwrap();
buf_guard.clone()
}
pub fn has_buffered_data(&self) -> bool {
let buf_guard = self.buffer.lock().unwrap();
!buf_guard.is_empty()
}
}
#[derive(Debug, Default)]
pub struct TcpConnectionManager {
next_id: Arc<Mutex<u64>>,
connections: Arc<Mutex<Vec<TcpConnectionHandle>>>,
}
impl TcpConnectionManager {
pub fn new() -> Self {
Self {
next_id: Arc::new(Mutex::new(1)),
connections: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn create_connection(&self) -> TcpConnectionHandle {
let mut next_id = self.next_id.lock().unwrap();
let id = *next_id;
*next_id += 1;
let handle = TcpConnectionHandle::new(id);
let mut connections = self.connections.lock().unwrap();
connections.push(handle.clone());
handle
}
pub fn get_connection(&self, id: u64) -> Option<TcpConnectionHandle> {
let connections = self.connections.lock().unwrap();
connections.iter().find(|c| c.id == id).cloned()
}
pub fn remove_connection(&self, id: u64) {
let mut connections = self.connections.lock().unwrap();
connections.retain(|c| c.id != id);
}
pub fn get_all_connections(&self) -> Vec<TcpConnectionHandle> {
let connections = self.connections.lock().unwrap();
connections.clone()
}
}
pub static TCP_MANAGER: std::sync::LazyLock<TcpConnectionManager> =
std::sync::LazyLock::new(TcpConnectionManager::new);
#[derive(Debug)]
pub struct AsyncConnectResult {
pub handle_id: u64,
pub success: bool,
pub error: Option<String>,
}
pub fn spawn_async_connect(
host: String,
port: u16,
) -> (u64, tokio::sync::oneshot::Receiver<AsyncConnectResult>) {
let handle = TCP_MANAGER.create_connection();
let handle_id = handle.id;
let (tx, rx) = tokio::sync::oneshot::channel();
tokio::spawn(async move {
let result = handle.connect(&host, port).await;
let async_result = AsyncConnectResult {
handle_id,
success: result.is_ok(),
error: result.err().map(|e| e.to_string()),
};
let _ = tx.send(async_result);
});
(handle_id, rx)
}
pub fn sync_connect(host: &str, port: u16, timeout_secs: u64) -> Result<TcpConnectionHandle> {
let handle = TCP_MANAGER.create_connection();
let _handle_id = handle.id;
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async {
let connect_future = handle.connect(host, port);
if timeout_secs > 0 {
tokio::time::timeout(std::time::Duration::from_secs(timeout_secs), connect_future)
.await??
} else {
connect_future.await?
}
Ok(handle)
})
}
pub fn sync_write(handle: &TcpConnectionHandle, data: &[u8]) -> Result<usize> {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async { handle.write(data).await })
}
pub fn sync_read(handle: &TcpConnectionHandle, buf: &mut [u8]) -> Result<usize> {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(async { handle.read(buf).await })
}
pub fn close_connection(handle: &TcpConnectionHandle) {
handle.close();
TCP_MANAGER.remove_connection(handle.id);
}
#[derive(Debug, Clone)]
pub struct HttpResponse {
pub status_code: u16,
pub status_message: String,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
}
pub struct HttpRequestOptions {
pub method: String,
pub host: String,
pub port: u16,
pub path: String,
pub headers: Vec<(String, String)>,
pub body: Vec<u8>,
}
pub fn sync_http_request(options: HttpRequestOptions, timeout_secs: u64) -> Result<HttpResponse> {
let handle = sync_connect(&options.host, options.port, timeout_secs)?;
let mut request = format!(
"{} {} HTTP/1.1\r\nHost: {}\r\n",
options.method, options.path, options.host
);
for (key, value) in &options.headers {
request.push_str(&format!("{}: {}\r\n", key, value));
}
if !options.body.is_empty() {
request.push_str(&format!("Content-Length: {}\r\n", options.body.len()));
}
request.push_str("\r\n");
sync_write(&handle, request.as_bytes())?;
if !options.body.is_empty() {
sync_write(&handle, &options.body)?;
}
let mut response_buffer = Vec::new();
let mut buf = [0u8; 4096];
loop {
match sync_read(&handle, &mut buf) {
Ok(0) => break, Ok(n) => {
response_buffer.extend_from_slice(&buf[..n]);
}
Err(_) => break,
}
}
close_connection(&handle);
parse_http_response(&response_buffer)
}
fn parse_http_response(data: &[u8]) -> Result<HttpResponse> {
let response_str = match std::str::from_utf8(data) {
Ok(s) => s,
Err(_) => {
return Ok(HttpResponse {
status_code: 200,
status_message: String::from("OK"),
headers: vec![(
String::from("content-type"),
String::from("application/octet-stream"),
)],
body: data.to_vec(),
});
}
};
let parts: Vec<&str> = response_str.split("\r\n\r\n").collect();
let header_section = parts.get(0).unwrap_or(&response_str);
let body = parts.get(1).unwrap_or(&"");
let lines: Vec<&str> = header_section.split("\r\n").collect();
let status_line = lines.get(0).unwrap_or(&"");
let status_parts: Vec<&str> = status_line.splitn(3, ' ').collect();
let status_code = status_parts
.get(1)
.and_then(|s| s.parse().ok())
.unwrap_or(200);
let status_message = status_parts.get(2).unwrap_or(&"").to_string();
let mut headers = Vec::new();
for line in lines.iter().skip(1) {
if line.is_empty() {
continue;
}
if let Some(pos) = line.find(':') {
let key = line[..pos].trim().to_string();
let value = line[pos + 1..].trim().to_string();
headers.push((key, value));
}
}
Ok(HttpResponse {
status_code,
status_message,
headers,
body: body.as_bytes().to_vec(),
})
}