Skip to content

Commit 84479e1

Browse files
committed
More reliable results + don't close window when results are finished
1 parent db57d43 commit 84479e1

3 files changed

Lines changed: 84 additions & 43 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dns_speed_test"
3-
version = "0.1.0"
3+
version = "1.1.0"
44
edition = "2021"
55

66
[dependencies]

src/main.rs

Lines changed: 82 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,16 @@ const TEST_DOMAINS: &[&str] = &[
3131
"cloudflare.com",
3232
"microsoft.com",
3333
"github.com",
34-
"netflix.com"
34+
"netflix.com",
35+
"amazon.com",
36+
"facebook.com",
37+
"wikipedia.org",
38+
"reddit.com"
3539
];
3640

37-
const TEST_ROUNDS: u32 = 3;
41+
const TEST_ROUNDS: u32 = 5;
42+
const TIMEOUT_SECS: u64 = 3;
43+
const COOLDOWN_MS: u64 = 100;
3844

3945
#[derive(Debug)]
4046
struct TestResult {
@@ -43,21 +49,31 @@ struct TestResult {
4349
min_latency: Duration,
4450
max_latency: Duration,
4551
success_rate: f64,
52+
failed_domains: Vec<String>,
53+
median_duration: Duration,
4654
}
4755

4856
async fn measure_latency(addr: &str) -> Option<Duration> {
4957
let start = Instant::now();
50-
if let Ok(mut stream) = tokio::net::TcpStream::connect(format!("{}:53", addr)).await {
51-
let _ = stream.shutdown().await;
52-
Some(start.elapsed())
53-
} else {
54-
None
58+
match tokio::time::timeout(
59+
Duration::from_secs(TIMEOUT_SECS),
60+
tokio::net::TcpStream::connect(format!("{}:53", addr))
61+
).await {
62+
Ok(Ok(mut stream)) => {
63+
let _ = stream.shutdown().await;
64+
Some(start.elapsed())
65+
},
66+
_ => None
5567
}
5668
}
5769

5870
async fn test_dns_speed(provider: &DnsProvider) -> TestResult {
5971
let mut opts = ResolverOpts::default();
60-
opts.timeout = Duration::from_secs(5);
72+
opts.timeout = Duration::from_secs(TIMEOUT_SECS);
73+
opts.attempts = 1;
74+
opts.use_hosts_file = false;
75+
opts.cache_size = 0;
76+
opts.edns0 = false;
6177

6278
let socket_addr = format!("{}:53", provider.ip)
6379
.parse::<SocketAddr>()
@@ -73,45 +89,59 @@ async fn test_dns_speed(provider: &DnsProvider) -> TestResult {
7389
);
7490

7591
let resolver = TokioAsyncResolver::tokio(config, opts);
76-
let mut total_duration = Duration::default();
77-
let mut successful_queries = 0;
92+
let mut durations = Vec::new();
93+
let mut failed_domains = Vec::new();
7894
let mut total_queries = 0;
79-
let mut min_latency = Duration::from_secs(5);
80-
let mut max_latency = Duration::default();
81-
82-
if let Some(latency) = measure_latency(provider.ip).await {
83-
min_latency = latency;
84-
max_latency = latency;
85-
}
8695

8796
let _ = resolver.lookup_ip(Name::from_ascii("example.com").unwrap()).await;
88-
sleep(Duration::from_millis(100)).await;
97+
sleep(Duration::from_millis(COOLDOWN_MS)).await;
8998

90-
for _ in 0..TEST_ROUNDS {
99+
for round in 0..TEST_ROUNDS {
91100
for domain in TEST_DOMAINS {
92101
total_queries += 1;
93-
let query_start = Instant::now();
94102

103+
let tcp_latency = measure_latency(provider.ip).await;
104+
if tcp_latency.is_none() {
105+
failed_domains.push(format!("{} (TCP Failed)", domain));
106+
continue;
107+
}
108+
109+
let query_start = Instant::now();
95110
match resolver.lookup_ip(Name::from_ascii(domain).unwrap()).await {
96111
Ok(_) => {
97-
let duration = query_start.elapsed();
98-
total_duration += duration;
99-
successful_queries += 1;
100-
min_latency = min_latency.min(duration);
101-
max_latency = max_latency.max(duration);
112+
durations.push(query_start.elapsed());
102113
},
103-
Err(_) => continue,
114+
Err(_) => {
115+
failed_domains.push(domain.to_string());
116+
}
104117
}
105118

106-
sleep(Duration::from_millis(50)).await;
119+
sleep(Duration::from_millis(COOLDOWN_MS)).await;
120+
}
121+
122+
if round < TEST_ROUNDS - 1 {
123+
sleep(Duration::from_millis(COOLDOWN_MS * 2)).await;
107124
}
108125
}
109126

127+
durations.sort();
128+
let successful_queries = durations.len();
110129
let success_rate = (successful_queries as f64) / (total_queries as f64) * 100.0;
111-
let avg_duration = if successful_queries > 0 {
112-
total_duration / successful_queries
130+
131+
let avg_duration = if !durations.is_empty() {
132+
Duration::from_secs_f64(
133+
durations.iter().map(|d| d.as_secs_f64()).sum::<f64>() / successful_queries as f64
134+
)
135+
} else {
136+
Duration::from_secs(TIMEOUT_SECS)
137+
};
138+
139+
let min_latency = durations.first().copied().unwrap_or(Duration::from_secs(TIMEOUT_SECS));
140+
let max_latency = durations.last().copied().unwrap_or(Duration::from_secs(TIMEOUT_SECS));
141+
let median_duration = if !durations.is_empty() {
142+
durations[durations.len() / 2]
113143
} else {
114-
Duration::from_secs(5)
144+
Duration::from_secs(TIMEOUT_SECS)
115145
};
116146

117147
TestResult {
@@ -120,49 +150,60 @@ async fn test_dns_speed(provider: &DnsProvider) -> TestResult {
120150
min_latency,
121151
max_latency,
122152
success_rate,
153+
failed_domains,
154+
median_duration,
123155
}
124156
}
125157

126158
#[tokio::main]
127159
async fn main() {
128-
println!("Testing DNS query speeds...\n");
160+
println!("DNS Speed Test (Testing {} domains × {} rounds)\n", TEST_DOMAINS.len(), TEST_ROUNDS);
129161

130162
let mut results = Vec::new();
131163

132164
for provider in DNS_PROVIDERS {
133165
print!("Testing {}... ", provider.name);
134166
let result = test_dns_speed(provider).await;
135167
println!("{:.2} ms (Success rate: {:.1}%)",
136-
result.avg_duration.as_secs_f64() * 1000.0,
168+
result.median_duration.as_secs_f64() * 1000.0,
137169
result.success_rate
138170
);
139171
results.push(result);
140172
}
141173

142-
results.sort_by(|a, b| a.avg_duration.cmp(&b.avg_duration));
174+
results.sort_by(|a, b| a.median_duration.cmp(&b.median_duration));
143175

144-
println!("\nDetailed Results (sorted by speed):");
145-
println!("{:-<75}", "");
146-
println!("{:<15} {:>10} {:>12} {:>12} {:>15}",
147-
"Provider", "Avg (ms)", "Min (ms)", "Max (ms)", "Success Rate");
148-
println!("{:-<75}", "");
176+
println!("\nDetailed Results (sorted by median speed):");
177+
println!("{:-<90}", "");
178+
println!("{:<15} {:>10} {:>10} {:>12} {:>12} {:>15}",
179+
"Provider", "Median", "Avg (ms)", "Min (ms)", "Max (ms)", "Success Rate");
180+
println!("{:-<90}", "");
149181

150182
for result in &results {
151183
println!(
152-
"{:<15} {:>10.2} {:>12.2} {:>12.2} {:>14.1}%",
184+
"{:<15} {:>10.2} {:>10.2} {:>12.2} {:>12.2} {:>14.1}%",
153185
result.provider,
186+
result.median_duration.as_secs_f64() * 1000.0,
154187
result.avg_duration.as_secs_f64() * 1000.0,
155188
result.min_latency.as_secs_f64() * 1000.0,
156189
result.max_latency.as_secs_f64() * 1000.0,
157190
result.success_rate
158191
);
192+
193+
if !result.failed_domains.is_empty() {
194+
println!(" Failed domains: {}", result.failed_domains.join(", "));
195+
}
159196
}
160197

161198
if let Some(fastest) = results.first() {
162-
println!("\nFastest DNS provider: {} ({:.2} ms, {:.1}% success rate)",
199+
println!("\nFastest DNS provider: {} ({:.2} ms median, {:.1}% success rate)",
163200
fastest.provider,
164-
fastest.avg_duration.as_secs_f64() * 1000.0,
201+
fastest.median_duration.as_secs_f64() * 1000.0,
165202
fastest.success_rate
166203
);
167204
}
205+
206+
println!("\nPress Enter to exit...");
207+
let mut input = String::new();
208+
std::io::stdin().read_line(&mut input).unwrap();
168209
}

0 commit comments

Comments
 (0)