Skip to content

Commit 95c5aa4

Browse files
committed
Merge #663: Tracker Checker: check HTTP trackers
4456203 feat: [#640] Tracker Chekcer: scrape check (Jose Celano) cb5bb68 feat: [#640] Tracker Chekcer: announce check (Jose Celano) Pull request description: Implement checks for HTTP Trackers in the Tracker Checker. - [x] Add check for `announce` request. - [x] Add check for `scrape` request. ACKs for top commit: josecelano: ACK 4456203 Tree-SHA512: edd0883513d4dec1041cec36c23ba11b867eaf49f9b3dda35165f7fc13f1875bea1990678982681907e0ff243ea64e6507fc34f1df78f7cc86ef50e873811999
2 parents 005a8cf + 4456203 commit 95c5aa4

File tree

2 files changed

+120
-25
lines changed

2 files changed

+120
-25
lines changed

src/console/clients/checker/service.rs

+103-25
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
use std::net::SocketAddr;
2+
use std::str::FromStr;
23
use std::sync::Arc;
34
use std::time::Duration;
45

56
use colored::Colorize;
6-
use reqwest::{Client, Url};
7+
use reqwest::{Client as HttpClient, Url};
78

89
use super::config::Configuration;
910
use super::console::Console;
1011
use crate::console::clients::checker::printer::Printer;
12+
use crate::shared::bit_torrent::info_hash::InfoHash;
13+
use crate::shared::bit_torrent::tracker::http::client::requests::announce::QueryBuilder;
14+
use crate::shared::bit_torrent::tracker::http::client::responses::announce::Announce;
15+
use crate::shared::bit_torrent::tracker::http::client::responses::scrape;
16+
use crate::shared::bit_torrent::tracker::http::client::{requests, Client};
1117

1218
pub struct Service {
1319
pub(crate) config: Arc<Configuration>,
@@ -19,7 +25,7 @@ pub type CheckResult = Result<(), CheckError>;
1925
#[derive(Debug)]
2026
pub enum CheckError {
2127
UdpError,
22-
HttpError,
28+
HttpError { url: Url },
2329
HealthCheckError { url: Url },
2430
}
2531

@@ -30,10 +36,15 @@ impl Service {
3036
pub async fn run_checks(&self) -> Vec<CheckResult> {
3137
self.console.println("Running checks for trackers ...");
3238

39+
let mut check_results = vec![];
40+
3341
self.check_udp_trackers();
34-
self.check_http_trackers();
3542

36-
self.run_health_checks().await
43+
self.check_http_trackers(&mut check_results).await;
44+
45+
self.run_health_checks(&mut check_results).await;
46+
47+
check_results
3748
}
3849

3950
fn check_udp_trackers(&self) {
@@ -44,63 +55,130 @@ impl Service {
4455
}
4556
}
4657

47-
fn check_http_trackers(&self) {
58+
async fn check_http_trackers(&self, check_results: &mut Vec<CheckResult>) {
4859
self.console.println("HTTP trackers ...");
4960

5061
for http_tracker in &self.config.http_trackers {
51-
self.check_http_tracker(http_tracker);
62+
let colored_tracker_url = http_tracker.to_string().yellow();
63+
64+
match self.check_http_announce(http_tracker).await {
65+
Ok(()) => {
66+
check_results.push(Ok(()));
67+
self.console
68+
.println(&format!("{} - Announce at {} is OK", "✓".green(), colored_tracker_url));
69+
}
70+
Err(err) => {
71+
check_results.push(Err(err));
72+
self.console
73+
.println(&format!("{} - Announce at {} is failing", "✗".red(), colored_tracker_url));
74+
}
75+
}
76+
77+
match self.check_http_scrape(http_tracker).await {
78+
Ok(()) => {
79+
check_results.push(Ok(()));
80+
self.console
81+
.println(&format!("{} - Scrape at {} is OK", "✓".green(), colored_tracker_url));
82+
}
83+
Err(err) => {
84+
check_results.push(Err(err));
85+
self.console
86+
.println(&format!("{} - Scrape at {} is failing", "✗".red(), colored_tracker_url));
87+
}
88+
}
5289
}
5390
}
5491

55-
async fn run_health_checks(&self) -> Vec<CheckResult> {
92+
async fn run_health_checks(&self, check_results: &mut Vec<CheckResult>) {
5693
self.console.println("Health checks ...");
5794

58-
let mut check_results = vec![];
59-
6095
for health_check_url in &self.config.health_checks {
6196
match self.run_health_check(health_check_url.clone()).await {
6297
Ok(()) => check_results.push(Ok(())),
6398
Err(err) => check_results.push(Err(err)),
6499
}
65100
}
66-
67-
check_results
68101
}
69102

70103
fn check_udp_tracker(&self, address: &SocketAddr) {
71104
// todo:
72105
// - Make announce request
73106
// - Make scrape request
74-
self.console
75-
.println(&format!("{} - UDP tracker at {:?} is OK (TODO)", "✓".green(), address));
107+
108+
let colored_address = address.to_string().yellow();
109+
110+
self.console.println(&format!(
111+
"{} - UDP tracker at udp://{} is OK ({})",
112+
"✓".green(),
113+
colored_address,
114+
"TODO".red(),
115+
));
76116
}
77117

78-
fn check_http_tracker(&self, url: &Url) {
79-
// todo:
80-
// - Make announce request
81-
// - Make scrape request
82-
self.console
83-
.println(&format!("{} - HTTP tracker at {} is OK (TODO)", "✓".green(), url));
118+
async fn check_http_announce(&self, url: &Url) -> Result<(), CheckError> {
119+
let info_hash_str = "9c38422213e30bff212b30c360d26f9a02136422".to_string(); // # DevSkim: ignore DS173237
120+
let info_hash = InfoHash::from_str(&info_hash_str).expect("a valid info-hash is required");
121+
122+
let response = Client::new(url.clone())
123+
.announce(&QueryBuilder::with_default_values().with_info_hash(&info_hash).query())
124+
.await;
125+
126+
if let Ok(body) = response.bytes().await {
127+
if let Ok(_announce_response) = serde_bencode::from_bytes::<Announce>(&body) {
128+
Ok(())
129+
} else {
130+
Err(CheckError::HttpError { url: url.clone() })
131+
}
132+
} else {
133+
Err(CheckError::HttpError { url: url.clone() })
134+
}
135+
}
136+
137+
async fn check_http_scrape(&self, url: &Url) -> Result<(), CheckError> {
138+
let info_hashes: Vec<String> = vec!["9c38422213e30bff212b30c360d26f9a02136422".to_string()]; // # DevSkim: ignore DS173237
139+
let query = requests::scrape::Query::try_from(info_hashes).expect("a valid array of info-hashes is required");
140+
141+
let response = Client::new(url.clone()).scrape(&query).await;
142+
143+
if let Ok(body) = response.bytes().await {
144+
if let Ok(_scrape_response) = scrape::Response::try_from_bencoded(&body) {
145+
Ok(())
146+
} else {
147+
Err(CheckError::HttpError { url: url.clone() })
148+
}
149+
} else {
150+
Err(CheckError::HttpError { url: url.clone() })
151+
}
84152
}
85153

86154
async fn run_health_check(&self, url: Url) -> Result<(), CheckError> {
87-
let client = Client::builder().timeout(Duration::from_secs(5)).build().unwrap();
155+
let client = HttpClient::builder().timeout(Duration::from_secs(5)).build().unwrap();
156+
157+
let colored_url = url.to_string().yellow();
88158

89159
match client.get(url.clone()).send().await {
90160
Ok(response) => {
91161
if response.status().is_success() {
92162
self.console
93-
.println(&format!("{} - Health API at {} is OK", "✓".green(), url));
163+
.println(&format!("{} - Health API at {} is OK", "✓".green(), colored_url));
94164
Ok(())
95165
} else {
96-
self.console
97-
.eprintln(&format!("{} - Health API at {} failing: {:?}", "✗".red(), url, response));
166+
self.console.eprintln(&format!(
167+
"{} - Health API at {} is failing: {:?}",
168+
"✗".red(),
169+
colored_url,
170+
response
171+
));
98172
Err(CheckError::HealthCheckError { url })
99173
}
100174
}
101175
Err(err) => {
102-
self.console
103-
.eprintln(&format!("{} - Health API at {} failing: {:?}", "✗".red(), url, err));
176+
self.console.eprintln(&format!(
177+
"{} - Health API at {} is failing: {:?}",
178+
"✗".red(),
179+
colored_url,
180+
err
181+
));
104182
Err(CheckError::HealthCheckError { url })
105183
}
106184
}

src/shared/bit_torrent/tracker/http/client/requests/scrape.rs

+17
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ impl TryFrom<&[String]> for Query {
4545
}
4646
}
4747

48+
impl TryFrom<Vec<String>> for Query {
49+
type Error = ConversionError;
50+
51+
fn try_from(info_hashes: Vec<String>) -> Result<Self, Self::Error> {
52+
let mut validated_info_hashes: Vec<ByteArray20> = Vec::new();
53+
54+
for info_hash in info_hashes {
55+
let validated_info_hash = InfoHash::from_str(&info_hash).map_err(|_| ConversionError(info_hash.clone()))?;
56+
validated_info_hashes.push(validated_info_hash.0);
57+
}
58+
59+
Ok(Self {
60+
info_hash: validated_info_hashes,
61+
})
62+
}
63+
}
64+
4865
/// HTTP Tracker Scrape Request:
4966
///
5067
/// <https://www.bittorrent.org/beps/bep_0048.html>

0 commit comments

Comments
 (0)