1use std::net::{TcpStream, ToSocketAddrs};
2use std::sync::mpsc;
3use std::thread;
4use std::time::{Duration, Instant};
5
6use log::{debug, warn};
7
8use crate::event::AppEvent;
9
10pub fn ping_host(
21 alias: String,
22 hostname: String,
23 port: u16,
24 tx: mpsc::Sender<AppEvent>,
25 generation: u64,
26) {
27 thread::spawn(move || {
28 ping_host_inner(&alias, &hostname, port, &tx, generation);
29 });
30}
31
32fn ping_host_inner(
34 alias: &str,
35 hostname: &str,
36 port: u16,
37 tx: &mpsc::Sender<AppEvent>,
38 generation: u64,
39) {
40 let clean = hostname.trim_start_matches('[').trim_end_matches(']');
42 let addr_str = if clean.contains(':') {
43 format!("[{}]:{}", clean, port)
44 } else {
45 format!("{}:{}", hostname, port)
46 };
47
48 let (done_tx, done_rx) = mpsc::channel();
51 let addr_str_clone = addr_str.clone();
52 thread::spawn(move || {
53 let start = Instant::now();
56 let connected = match addr_str_clone.to_socket_addrs() {
57 Ok(addrs) => addrs
58 .into_iter()
59 .any(|addr| TcpStream::connect_timeout(&addr, Duration::from_secs(3)).is_ok()),
60 Err(_) => false,
61 };
62 let rtt_ms = if connected {
63 Some(start.elapsed().as_millis().min(u32::MAX as u128) as u32)
64 } else {
65 None
66 };
67 let _ = done_tx.send(rtt_ms);
68 });
69
70 let rtt_ms = match done_rx.recv_timeout(Duration::from_secs(5)) {
71 Ok(rtt) => rtt,
72 Err(e) => {
73 warn!(
74 "[external] ping timeout: alias={} addr={} ({})",
75 alias, addr_str, e
76 );
77 None
78 }
79 };
80
81 debug!(
82 "[purple] ping result: alias={} addr={} rtt_ms={:?} gen={}",
83 alias, addr_str, rtt_ms, generation
84 );
85 let _ = tx.send(AppEvent::PingResult {
86 alias: alias.to_string(),
87 rtt_ms,
88 generation,
89 });
90}
91
92pub fn ping_all(hosts: &[(String, String, u16)], tx: mpsc::Sender<AppEvent>, generation: u64) {
96 let hosts = hosts.to_vec();
97 debug!(
98 "[purple] ping_all: hosts={} gen={}",
99 hosts.len(),
100 generation
101 );
102 thread::spawn(move || {
103 let max_concurrent: usize = 10;
104 let (slot_tx, slot_rx) = mpsc::channel();
105 for _ in 0..max_concurrent {
106 let _ = slot_tx.send(());
107 }
108 for (alias, hostname, port) in hosts {
109 let _ = slot_rx.recv(); let slot_tx = slot_tx.clone();
111 let tx = tx.clone();
112 thread::spawn(move || {
113 ping_host_inner(&alias, &hostname, port, &tx, generation);
114 let _ = slot_tx.send(()); });
116 }
117 });
118}