use super::{ProbeResult, Prober};
use crate::error::NetPulseError;
use async_trait::async_trait;
use std::time::Instant;
use tokio::net::TcpStream;
use tokio::time::{timeout, Duration};
pub struct TcpProber {
port: u16,
timeout_ms: u64,
}
impl TcpProber {
pub fn new(port: u16, timeout_ms: u64) -> Self {
Self { port, timeout_ms }
}
}
#[async_trait]
impl Prober for TcpProber {
fn name(&self) -> &'static str {
"tcp"
}
async fn probe(
&self,
target: &str,
seq: u64,
_ttl: Option<u32>,
) -> Result<ProbeResult, NetPulseError> {
let addr = if target.contains(':') {
target.to_string()
} else {
format!("{}:{}", target, self.port)
};
let start = Instant::now();
let connect_result = timeout(
Duration::from_millis(self.timeout_ms),
TcpStream::connect(&addr),
)
.await;
match connect_result {
Ok(Ok(_stream)) => {
let rtt_us = start.elapsed().as_micros() as u64;
Ok(ProbeResult::success(target, seq, rtt_us, None))
}
Ok(Err(e)) => {
match e.kind() {
std::io::ErrorKind::ConnectionRefused => {
let rtt_us = start.elapsed().as_micros() as u64;
Ok(ProbeResult::success(target, seq, rtt_us, None))
}
_ => Ok(ProbeResult::loss(target, seq)),
}
}
Err(_elapsed) => {
Ok(ProbeResult::loss(target, seq))
}
}
}
}