Skip to content

Commit 2bc2bff

Browse files
committed
Merge torrust#664: Tracker Checker: check UDP trackers
873f98d refactor: [torrust#639] Tracker Checker: extract mod for Health checks (Jose Celano) 70924ed refactor: [torrust#639] Tracker Checker: extract mod for HTTP checks (Jose Celano) 77c32a1 refactor: [torrust#639] Tracker Checker: extract mod for UDP checks (Jose Celano) 592c0dd fix: add timeouts to UdpClient operations (Jose Celano) 6b74c66 feat: [torrust#639] Tracker Checker. Setup logging (Jose Celano) bbfca2c feat: [torrust#639] Tracker Checker. Check UDP trackers (Jose Celano) 661b521 chore: [torrust#639] add cargo dependency: hex_literal (Jose Celano) 1b92b77 refactor: [torrust#639] UDP client. Extract aquatic reponses wrappers (Jose Celano) a2e123c refactor: [torrust#639] UDP client. Extract command handlers (Jose Celano) 011fdb7 refactor: [torrust#639] Tracker Checker: extract checker:Client to check UDP servers (Jose Celano) 3b735a7 refactor: [torrust#639] Tracker Checker: prepare outout for UDP checks (Jose Celano) Pull request description: Add checks for UDP trackers to the Tracker Checker. The output will look like the following: ```output Running checks for trackers ... UDP trackers ... ✓ - Announce at 127.0.0.1:6969 is OK ✓ - Scrape at 127.0.0.1:6969 is OK HTTP trackers ... ✓ - Announce at http://127.0.0.1:7070/ is OK ✓ - Scrape at http://127.0.0.1:7070/ is OK Health checks ... ✓ - Health API at http://127.0.0.1:1313/health_check is OK ``` See subtasks in torrust#639 ACKs for top commit: josecelano: ACK 873f98d Tree-SHA512: 04c5472c72d6c8b6659c07d18b9240e1320249c3cc7455c727e7030a8e18aebd7bee0962ab1f3153323fafbc722a9ec8efad2c67d36ff826e00d5b9ef3c234f4
2 parents c291ef1 + 873f98d commit 2bc2bff

File tree

14 files changed

+675
-325
lines changed

14 files changed

+675
-325
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ url = "2.5.0"
7474
tempfile = "3.9.0"
7575
clap = { version = "4.4.18", features = ["derive", "env"]}
7676
anyhow = "1.0.79"
77+
hex-literal = "0.4.1"
7778

7879
[dev-dependencies]
7980
criterion = { version = "0.5.1", features = ["async_tokio"] }

src/console/clients/checker/app.rs

+24
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::sync::Arc;
1717

1818
use anyhow::{Context, Result};
1919
use clap::Parser;
20+
use log::{debug, LevelFilter};
2021

2122
use super::config::Configuration;
2223
use super::console::Console;
@@ -39,6 +40,8 @@ struct Args {
3940
///
4041
/// Will return an error if the configuration was not provided.
4142
pub async fn run() -> Result<Vec<CheckResult>> {
43+
setup_logging(LevelFilter::Info);
44+
4245
let args = Args::parse();
4346

4447
let config = setup_config(args)?;
@@ -53,6 +56,27 @@ pub async fn run() -> Result<Vec<CheckResult>> {
5356
Ok(service.run_checks().await)
5457
}
5558

59+
fn setup_logging(level: LevelFilter) {
60+
if let Err(_err) = fern::Dispatch::new()
61+
.format(|out, message, record| {
62+
out.finish(format_args!(
63+
"{} [{}][{}] {}",
64+
chrono::Local::now().format("%+"),
65+
record.target(),
66+
record.level(),
67+
message
68+
));
69+
})
70+
.level(level)
71+
.chain(std::io::stdout())
72+
.apply()
73+
{
74+
panic!("Failed to initialize logging.")
75+
}
76+
77+
debug!("logging initialized.");
78+
}
79+
5680
fn setup_config(args: Args) -> Result<Configuration> {
5781
match (args.config_path, args.config_content) {
5882
(Some(config_path), _) => load_config_from_file(&config_path),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use std::time::Duration;
2+
3+
use colored::Colorize;
4+
use reqwest::{Client as HttpClient, Url, Url as ServiceUrl};
5+
6+
use crate::console::clients::checker::console::Console;
7+
use crate::console::clients::checker::printer::Printer;
8+
use crate::console::clients::checker::service::{CheckError, CheckResult};
9+
10+
pub async fn run(health_checks: &Vec<ServiceUrl>, console: &Console, check_results: &mut Vec<CheckResult>) {
11+
console.println("Health checks ...");
12+
13+
for health_check_url in health_checks {
14+
match run_health_check(health_check_url.clone(), console).await {
15+
Ok(()) => check_results.push(Ok(())),
16+
Err(err) => check_results.push(Err(err)),
17+
}
18+
}
19+
}
20+
21+
async fn run_health_check(url: Url, console: &Console) -> Result<(), CheckError> {
22+
let client = HttpClient::builder().timeout(Duration::from_secs(5)).build().unwrap();
23+
24+
let colored_url = url.to_string().yellow();
25+
26+
match client.get(url.clone()).send().await {
27+
Ok(response) => {
28+
if response.status().is_success() {
29+
console.println(&format!("{} - Health API at {} is OK", "✓".green(), colored_url));
30+
Ok(())
31+
} else {
32+
console.eprintln(&format!(
33+
"{} - Health API at {} is failing: {:?}",
34+
"✗".red(),
35+
colored_url,
36+
response
37+
));
38+
Err(CheckError::HealthCheckError { url })
39+
}
40+
}
41+
Err(err) => {
42+
console.eprintln(&format!(
43+
"{} - Health API at {} is failing: {:?}",
44+
"✗".red(),
45+
colored_url,
46+
err
47+
));
48+
Err(CheckError::HealthCheckError { url })
49+
}
50+
}
51+
}
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::str::FromStr;
2+
3+
use colored::Colorize;
4+
use log::debug;
5+
use reqwest::Url as ServiceUrl;
6+
use url::Url;
7+
8+
use crate::console::clients::checker::console::Console;
9+
use crate::console::clients::checker::printer::Printer;
10+
use crate::console::clients::checker::service::{CheckError, CheckResult};
11+
use crate::shared::bit_torrent::info_hash::InfoHash;
12+
use crate::shared::bit_torrent::tracker::http::client::requests::announce::QueryBuilder;
13+
use crate::shared::bit_torrent::tracker::http::client::responses::announce::Announce;
14+
use crate::shared::bit_torrent::tracker::http::client::responses::scrape;
15+
use crate::shared::bit_torrent::tracker::http::client::{requests, Client};
16+
17+
pub async fn run(http_trackers: &Vec<ServiceUrl>, console: &Console, check_results: &mut Vec<CheckResult>) {
18+
console.println("HTTP trackers ...");
19+
20+
for http_tracker in http_trackers {
21+
let colored_tracker_url = http_tracker.to_string().yellow();
22+
23+
match check_http_announce(http_tracker).await {
24+
Ok(()) => {
25+
check_results.push(Ok(()));
26+
console.println(&format!("{} - Announce at {} is OK", "✓".green(), colored_tracker_url));
27+
}
28+
Err(err) => {
29+
check_results.push(Err(err));
30+
console.println(&format!("{} - Announce at {} is failing", "✗".red(), colored_tracker_url));
31+
}
32+
}
33+
34+
match check_http_scrape(http_tracker).await {
35+
Ok(()) => {
36+
check_results.push(Ok(()));
37+
console.println(&format!("{} - Scrape at {} is OK", "✓".green(), colored_tracker_url));
38+
}
39+
Err(err) => {
40+
check_results.push(Err(err));
41+
console.println(&format!("{} - Scrape at {} is failing", "✗".red(), colored_tracker_url));
42+
}
43+
}
44+
}
45+
}
46+
47+
async fn check_http_announce(tracker_url: &Url) -> Result<(), CheckError> {
48+
let info_hash_str = "9c38422213e30bff212b30c360d26f9a02136422".to_string(); // # DevSkim: ignore DS173237
49+
let info_hash = InfoHash::from_str(&info_hash_str).expect("a valid info-hash is required");
50+
51+
// todo: HTTP request could panic.For example, if the server is not accessible.
52+
// We should change the client to catch that error and return a `CheckError`.
53+
// Otherwise the checking process will stop. The idea is to process all checks
54+
// and return a final report.
55+
let response = Client::new(tracker_url.clone())
56+
.announce(&QueryBuilder::with_default_values().with_info_hash(&info_hash).query())
57+
.await;
58+
59+
if let Ok(body) = response.bytes().await {
60+
if let Ok(_announce_response) = serde_bencode::from_bytes::<Announce>(&body) {
61+
Ok(())
62+
} else {
63+
debug!("announce body {:#?}", body);
64+
Err(CheckError::HttpError {
65+
url: tracker_url.clone(),
66+
})
67+
}
68+
} else {
69+
Err(CheckError::HttpError {
70+
url: tracker_url.clone(),
71+
})
72+
}
73+
}
74+
75+
async fn check_http_scrape(url: &Url) -> Result<(), CheckError> {
76+
let info_hashes: Vec<String> = vec!["9c38422213e30bff212b30c360d26f9a02136422".to_string()]; // # DevSkim: ignore DS173237
77+
let query = requests::scrape::Query::try_from(info_hashes).expect("a valid array of info-hashes is required");
78+
79+
// todo: HTTP request could panic.For example, if the server is not accessible.
80+
// We should change the client to catch that error and return a `CheckError`.
81+
// Otherwise the checking process will stop. The idea is to process all checks
82+
// and return a final report.
83+
let response = Client::new(url.clone()).scrape(&query).await;
84+
85+
if let Ok(body) = response.bytes().await {
86+
if let Ok(_scrape_response) = scrape::Response::try_from_bencoded(&body) {
87+
Ok(())
88+
} else {
89+
debug!("scrape body {:#?}", body);
90+
Err(CheckError::HttpError { url: url.clone() })
91+
}
92+
} else {
93+
Err(CheckError::HttpError { url: url.clone() })
94+
}
95+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub mod health;
2+
pub mod http;
3+
pub mod udp;
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use std::net::SocketAddr;
2+
3+
use aquatic_udp_protocol::{Port, TransactionId};
4+
use colored::Colorize;
5+
use hex_literal::hex;
6+
use log::debug;
7+
8+
use crate::console::clients::checker::console::Console;
9+
use crate::console::clients::checker::printer::Printer;
10+
use crate::console::clients::checker::service::{CheckError, CheckResult};
11+
use crate::console::clients::udp::checker;
12+
use crate::shared::bit_torrent::info_hash::InfoHash;
13+
14+
const ASSIGNED_BY_OS: u16 = 0;
15+
const RANDOM_TRANSACTION_ID: i32 = -888_840_697;
16+
17+
pub async fn run(udp_trackers: &Vec<SocketAddr>, console: &Console, check_results: &mut Vec<CheckResult>) {
18+
console.println("UDP trackers ...");
19+
20+
for udp_tracker in udp_trackers {
21+
debug!("UDP tracker: {:?}", udp_tracker);
22+
23+
let colored_tracker_url = udp_tracker.to_string().yellow();
24+
25+
let transaction_id = TransactionId(RANDOM_TRANSACTION_ID);
26+
27+
let mut client = checker::Client::default();
28+
29+
debug!("Bind and connect");
30+
31+
let Ok(bound_to) = client.bind_and_connect(ASSIGNED_BY_OS, udp_tracker).await else {
32+
check_results.push(Err(CheckError::UdpError {
33+
socket_addr: *udp_tracker,
34+
}));
35+
console.println(&format!("{} - Can't connect to socket {}", "✗".red(), colored_tracker_url));
36+
break;
37+
};
38+
39+
debug!("Send connection request");
40+
41+
let Ok(connection_id) = client.send_connection_request(transaction_id).await else {
42+
check_results.push(Err(CheckError::UdpError {
43+
socket_addr: *udp_tracker,
44+
}));
45+
console.println(&format!(
46+
"{} - Can't make tracker connection request to {}",
47+
"✗".red(),
48+
colored_tracker_url
49+
));
50+
break;
51+
};
52+
53+
let info_hash = InfoHash(hex!("9c38422213e30bff212b30c360d26f9a02136422")); // # DevSkim: ignore DS173237
54+
55+
debug!("Send announce request");
56+
57+
if (client
58+
.send_announce_request(connection_id, transaction_id, info_hash, Port(bound_to.port()))
59+
.await)
60+
.is_ok()
61+
{
62+
check_results.push(Ok(()));
63+
console.println(&format!("{} - Announce at {} is OK", "✓".green(), colored_tracker_url));
64+
} else {
65+
let err = CheckError::UdpError {
66+
socket_addr: *udp_tracker,
67+
};
68+
check_results.push(Err(err));
69+
console.println(&format!("{} - Announce at {} is failing", "✗".red(), colored_tracker_url));
70+
}
71+
72+
debug!("Send scrape request");
73+
74+
let info_hashes = vec![InfoHash(hex!("9c38422213e30bff212b30c360d26f9a02136422"))]; // # DevSkim: ignore DS173237
75+
76+
if (client.send_scrape_request(connection_id, transaction_id, info_hashes).await).is_ok() {
77+
check_results.push(Ok(()));
78+
console.println(&format!("{} - Announce at {} is OK", "✓".green(), colored_tracker_url));
79+
} else {
80+
let err = CheckError::UdpError {
81+
socket_addr: *udp_tracker,
82+
};
83+
check_results.push(Err(err));
84+
console.println(&format!("{} - Announce at {} is failing", "✗".red(), colored_tracker_url));
85+
}
86+
}
87+
}

src/console/clients/checker/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod app;
2+
pub mod checks;
23
pub mod config;
34
pub mod console;
45
pub mod logger;

0 commit comments

Comments
 (0)