-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
380 additions
and
371 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
use indicatif::{ProgressBar, ProgressStyle}; | ||
use reqwest::Client; | ||
use std::time::{Duration, Instant}; | ||
use tokio::time; | ||
|
||
/// measure bandwidth by simulating a download and upload process. | ||
/// returns a tuple containing download and upload speeds in mbps. | ||
pub async fn measure_bandwidth() -> Option<(u64, u64)> { | ||
println!("Measuring bandwidth"); | ||
|
||
let client = reqwest::Client::new(); | ||
let start_time = Instant::now(); | ||
|
||
// instantiate new progress bar with an arbitrary initial length | ||
let pb = ProgressBar::new(100); | ||
pb.set_style(ProgressStyle::default_bar() | ||
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})") | ||
.progress_chars("#>-")); | ||
|
||
// simulate a download process | ||
let download_url = | ||
"https://drive.google.com/uc?id=1ie1FhaN5ZzwCqc8E0Mz8hS_x9LYMRCk5&export=download"; | ||
let download_result = download_task(client.clone(), download_url, pb.clone()).await; | ||
|
||
// simulate an upload process | ||
let upload_url = "https://example.com/upload_endpoint"; | ||
let upload_result = upload_task(client.clone(), upload_url).await; | ||
|
||
// Handle errors from both tasks | ||
if let (Ok(download_size), Ok(upload_size)) = (download_result, upload_result) { | ||
let end_time = Instant::now(); | ||
let elapsed_time = end_time.duration_since(start_time).as_secs_f64(); | ||
|
||
// calculate download and upload speeds in Mbps | ||
let download_speed = (download_size as f64 / elapsed_time) * 8.0 / 1_000_000.0; // Mbps | ||
let upload_speed = (upload_size as f64 / elapsed_time) * 8.0 / 1_000_000.0; // Mbps | ||
|
||
pb.finish_with_message("Download and upload complete!"); | ||
|
||
Some((download_speed as u64, upload_speed as u64)) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
async fn download_task(client: Client, url: &str, pb: ProgressBar) -> Result<u64, reqwest::Error> { | ||
let mut response = client.get(url).send().await?; | ||
let total_size = response.content_length().unwrap_or_default(); | ||
pb.set_length(total_size); | ||
|
||
let mut download_size = 0u64; | ||
while let Some(chunk) = response.chunk().await? { | ||
download_size += chunk.len() as u64; | ||
pb.set_position(download_size); | ||
} | ||
|
||
Ok(download_size) | ||
} | ||
|
||
async fn upload_task(client: Client, url: &str) -> Result<u64, reqwest::Error> { | ||
let response = client.post(url).body(Vec::new()).send().await?; | ||
Ok(response.content_length().unwrap_or_default()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use clap::{App, Arg}; | ||
use std::io; | ||
|
||
/// struct to hold CLI arguments. | ||
pub struct CliArgs { | ||
pub target_host: String, | ||
} | ||
|
||
/// parse CLI arguments and return a CliArgs struct. | ||
pub fn parse_cli_args() -> CliArgs { | ||
let matches = App::new("PantheonProbe") | ||
.arg( | ||
Arg::with_name("target") | ||
.short("t") | ||
.long("target") | ||
.value_name("HOST") | ||
.help("Sets the target host or IP address") | ||
.takes_value(true), | ||
) | ||
.get_matches(); | ||
|
||
let target_host = matches | ||
.value_of("target") | ||
.map(|s| s.to_string()) | ||
.unwrap_or_else(|| { | ||
let mut target = String::new(); | ||
println!("Enter your desired target host or IP address:"); | ||
std::io::stdin() | ||
.read_line(&mut target) | ||
.expect("Oops! Failed to read line"); | ||
target.trim().to_string() | ||
}); | ||
|
||
CliArgs { target_host } | ||
} | ||
|
||
/// prompt the user to continue or not | ||
pub fn should_continue() -> bool { | ||
println!("Do you wish to continue? (y/n)"); | ||
let mut input = String::new(); | ||
std::io::stdin() | ||
.read_line(&mut input) | ||
.expect("Oops! Failed to read line"); | ||
let input = input.trim().to_lowercase(); | ||
input == "y" || input == "yes" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs}; | ||
use std::time::{Duration, Instant}; | ||
|
||
// measure dns resolution time | ||
// returns time taken to resolve the target host's domain name to an IP address | ||
pub fn measure_dns_resolution_time(host: &str) -> Option<Duration> { | ||
println!("Measuring DNS Resolution Time for {}", host); | ||
|
||
let start_time = Instant::now(); | ||
|
||
// attempt resolving the hostname to IP addresses | ||
let ips: Vec<_> = match host.parse::<IpAddr>() { | ||
Ok(ip) => vec![ip], | ||
Err(_) => match host.parse::<Ipv4Addr>() { | ||
Ok(ipv4) => vec![IpAddr::V4(ipv4)], | ||
Err(_) => match (host, 0).to_socket_addrs() { | ||
Ok(addrs) => addrs.map(|a| a.ip()).collect(), | ||
Err(err) => { | ||
eprintln!("Error resolving {}: {}", host, err); | ||
return None; | ||
} | ||
}, | ||
}, | ||
}; | ||
|
||
let end_time = Instant::now(); | ||
let elapsed_time = end_time.duration_since(start_time); | ||
|
||
if ips.is_empty() { | ||
eprintln!("Failed to resolve IP address for {}", host); | ||
None | ||
} else { | ||
println!("DNS Resolution Time for {}: {:?}", host, elapsed_time); | ||
Some(elapsed_time) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
use indicatif::{ProgressBar, ProgressStyle}; | ||
use std::time::Duration; | ||
|
||
use crate::latency::measure_latency; | ||
|
||
/// measure jitter by calculating the difference in latency over a series of measurements. | ||
/// returns the average jitter in milliseconds. | ||
pub fn measure_jitter(target_host: &str) -> Option<f64> { | ||
println!("Calculating jitter"); | ||
|
||
let mut previous_latency: Option<Duration> = None; | ||
let mut jitter_sum: f64 = 0.0; | ||
let mut packet_count = 0; | ||
|
||
// instantiate new progress bar | ||
let pb = ProgressBar::new(100); | ||
pb.set_style( | ||
ProgressStyle::default_bar() | ||
.template( | ||
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})", | ||
) | ||
.progress_chars("#>-"), | ||
); | ||
|
||
pb.set_message("Starting jitter measurement..."); | ||
pb.inc(10); // Increment the progress bar by 10% after starting | ||
|
||
// measure latency for a certain number of packets | ||
for _ in 0..10 { | ||
if let Some(latency) = measure_latency(target_host) { | ||
pb.inc(10); // Increment the progress bar by 10% after each latency measurement | ||
if let Some(previous) = previous_latency { | ||
if let Some(latency_diff) = latency.checked_sub(previous) { | ||
let latency_diff_ms = latency_diff.as_secs_f64() * 1000.0; | ||
jitter_sum += latency_diff_ms; | ||
packet_count += 1; | ||
} | ||
} | ||
previous_latency = Some(latency); | ||
} | ||
} | ||
|
||
// compute average jitter | ||
if packet_count > 0 { | ||
let average_jitter = jitter_sum / packet_count as f64; | ||
pb.finish_with_message("Jitter measurement complete!"); | ||
Some(average_jitter) | ||
} else { | ||
pb.finish_with_message("Failed to measure jitter."); | ||
None | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use indicatif::{ProgressBar, ProgressStyle}; | ||
use std::process::{Command, Output}; | ||
use std::time::{Duration, Instant}; | ||
|
||
/// measure latency to a target host using the ping command. | ||
/// returns the measured latency as a duration. | ||
pub fn measure_latency(target_host: &str) -> Option<Duration> { | ||
let ping_command = match cfg!(target_os = "windows") { | ||
true => "ping -n 1", | ||
false => "ping -c 1", | ||
}; | ||
|
||
// instantiate new progress bar | ||
let pb = ProgressBar::new(100); | ||
pb.set_style( | ||
ProgressStyle::default_bar() | ||
.template( | ||
"{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})", | ||
) | ||
.progress_chars("#>-"), | ||
); | ||
|
||
pb.set_message("Starting latency measurement..."); | ||
pb.inc(25); // Increment the progress bar by 25% after starting | ||
|
||
let output: Output = Command::new("sh") | ||
.arg("-c") | ||
.arg(format!("{} {}", ping_command, target_host)) | ||
.output() | ||
.expect("Oops! Failed to execute the ping command"); | ||
|
||
pb.set_message("Ping command executed..."); | ||
pb.inc(25); // Increment the progress bar by 25% after executing the ping command | ||
|
||
if output.status.success() { | ||
// parse output to extract latency | ||
let output_str = String::from_utf8_lossy(&output.stdout); | ||
|
||
pb.set_message("Parsing ping output..."); | ||
pb.inc(25); // Increment the progress bar by 25% after parsing the output | ||
|
||
if let Some(latency) = extract_latency_from_ping_output(&output_str) { | ||
pb.set_message("Latency measurement complete!"); | ||
pb.inc(25); // Increment the progress bar by 25% after completing the measurement | ||
pb.finish(); | ||
return Some(latency); | ||
} | ||
} | ||
|
||
pb.finish_with_message("Failed to measure latency."); | ||
None | ||
} | ||
|
||
/// measure latency to a target host using the ping command. | ||
/// returns the measured latency as a duration. | ||
fn extract_latency_from_ping_output(output: &str) -> Option<Duration> { | ||
let lines: Vec<&str> = output.lines().collect(); | ||
for line in lines { | ||
if line.contains("time=") { | ||
if let Some(time_start) = line.find("time=") { | ||
let time_end = line[time_start + 5..].find(" ").unwrap_or(line.len()); | ||
let latency_str = &line[time_start + 5..time_start + 5 + time_end]; | ||
if let Ok(latency_ms) = latency_str.parse::<f64>() { | ||
return Some(Duration::from_millis(latency_ms as u64)); | ||
} | ||
} | ||
} | ||
} | ||
None | ||
} |
Oops, something went wrong.