Skip to content

Commit 4948440

Browse files
author
007
committed
Use raw sockets for TLS, to reduce CPU overhead (we don't need to decrypt the TLS stream for a download speedtest)
1 parent ed603be commit 4948440

File tree

5 files changed

+269
-50
lines changed

5 files changed

+269
-50
lines changed

Cargo.lock

Lines changed: 95 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cf_speedtest"
3-
version = "0.5.5"
3+
version = "0.6.1"
44
authors = ["https://github.com/12932"]
55
repository = "https://github.com/12932/cf_speedtest"
66
keywords = ["cross-platform", "speedtest", "cli", "speed", "test"]
@@ -24,6 +24,7 @@ rustls = { version = "0.23.33", default-features = false, features = ["ring"] }
2424
webpki-roots = "1.0.3"
2525
ctrlc = "3.5.1"
2626
phf = { version = "0.13", features = ["macros"] }
27+
socket2 = "0.6.1"
2728

2829
[profile.release]
2930
debug = false

src/main.rs

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::io::Read;
21
use std::io::{self, Write};
32
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
43
use std::sync::{Arc, Mutex};
@@ -16,6 +15,8 @@ use args::UserArgs;
1615
mod agent;
1716
use crate::agent::create_configured_agent;
1817

18+
mod raw_socket;
19+
1920
mod locations;
2021
mod table;
2122
#[cfg(test)]
@@ -259,61 +260,73 @@ fn upload_test(
259260
}
260261
}
261262

262-
// download some bytes from cloudflare
263+
// download some bytes from cloudflare using raw encrypted byte reading
263264
fn download_test(
264265
bytes_to_request: usize,
265266
total_bytes_counter: &Arc<AtomicUsize>,
266267
current_down_speed: &Arc<AtomicUsize>,
267268
exit_signal: &Arc<AtomicBool>,
268269
) -> Result<()> {
269-
let agent: Agent = create_configured_agent();
270-
271-
let resp = match agent
272-
.get(format!("{CLOUDFLARE_SPEEDTEST_DOWNLOAD_URL}&bytes={bytes_to_request}").as_str())
273-
.call()
274-
{
275-
Ok(resp) => resp,
276-
Err(err) => {
277-
if !CTRL_C_PRESSED.load(Ordering::Relaxed) {
278-
eprintln!("Error in download thread: {err}");
279-
}
280-
return Ok(());
281-
}
282-
};
283-
284-
let body = resp.into_body();
285-
let mut resp_reader = body.into_reader();
286-
let mut total_bytes_sank: usize = 0;
287-
270+
// Keep making new requests until exit_signal is set
288271
loop {
289272
// exit if we have passed deadline
290273
if exit_signal.load(Ordering::Relaxed) {
291274
return Ok(());
292275
}
293276

294-
// if we are fast, take big chunks
295-
// if we are slow, take small chunks
296-
let current_recv_buff =
297-
get_appropriate_buff_size(current_down_speed.load(Ordering::Relaxed));
277+
// Establish connection, perform TLS handshake, send HTTP request
278+
let mut conn = match raw_socket::RawDownloadConnection::connect(
279+
CLOUDFLARE_SPEEDTEST_DOWNLOAD_URL,
280+
bytes_to_request,
281+
) {
282+
Ok(conn) => conn,
283+
Err(err) => {
284+
if !CTRL_C_PRESSED.load(Ordering::Relaxed) {
285+
eprintln!("Error in download thread: {err}");
286+
}
287+
return Ok(());
288+
}
289+
};
290+
291+
let mut total_bytes_sank: usize = 0;
298292

299-
// copy bytes into the void
300-
let bytes_sank = std::io::copy(
301-
&mut resp_reader.by_ref().take(current_recv_buff),
302-
&mut std::io::sink(),
303-
)? as usize;
293+
// Read from this connection until it's exhausted
294+
loop {
295+
// exit if we have passed deadline
296+
if exit_signal.load(Ordering::Relaxed) {
297+
return Ok(());
298+
}
304299

305-
//println!("Thread {:?} sank {} bytes", std::thread::current().id(), bytes_sank);
300+
// if we are fast, take big chunks
301+
// if we are slow, take small chunks
302+
let current_recv_buff =
303+
get_appropriate_buff_size(current_down_speed.load(Ordering::Relaxed)) as usize;
304+
305+
// Read raw encrypted bytes directly from socket (no TLS decryption!)
306+
let mut buf = vec![0u8; current_recv_buff];
307+
let bytes_read = match conn.read_encrypted_bytes(&mut buf) {
308+
Ok(n) => n,
309+
Err(err) => {
310+
if !CTRL_C_PRESSED.load(Ordering::Relaxed) {
311+
eprintln!("Error reading from socket: {err}");
312+
}
313+
// Connection error, break to create a new connection
314+
break;
315+
}
316+
};
306317

307-
if bytes_sank == 0 {
308-
if total_bytes_sank == 0 {
309-
eprintln!("Cloudflare sent an empty response?");
318+
if bytes_read == 0 {
319+
if total_bytes_sank == 0 {
320+
eprintln!("Cloudflare sent an empty response?");
321+
}
322+
// Connection exhausted, break inner loop to make a new request
323+
break;
310324
}
311325

312-
return Ok(());
326+
// Count the encrypted bytes we received (wire bytes including TLS overhead)
327+
total_bytes_sank += bytes_read;
328+
total_bytes_counter.fetch_add(bytes_read, Ordering::SeqCst);
313329
}
314-
315-
total_bytes_sank += bytes_sank;
316-
total_bytes_counter.fetch_add(bytes_sank, Ordering::SeqCst);
317330
}
318331
}
319332

0 commit comments

Comments
 (0)