Skip to content

Commit

Permalink
feat: modularity
Browse files Browse the repository at this point in the history
  • Loading branch information
0xTxbi committed Nov 25, 2023
1 parent b31135c commit 653b9c3
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 371 deletions.
63 changes: 63 additions & 0 deletions src/bandwidth.rs
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())
}
46 changes: 46 additions & 0 deletions src/cli.rs
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"
}
36 changes: 36 additions & 0 deletions src/dns_resolution_time.rs
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)
}
}
52 changes: 52 additions & 0 deletions src/jitter.rs
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
}
}
70 changes: 70 additions & 0 deletions src/latency.rs
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
}
Loading

0 comments on commit 653b9c3

Please sign in to comment.