Skip to content

Commit a426259

Browse files
committed
refactor: [torrust#1338] bittorrent_http_tracker_core::services::scrape::ScrapeService
1 parent 1b01bf0 commit a426259

File tree

1 file changed

+109
-65
lines changed
  • packages/http-tracker-core/src/services

1 file changed

+109
-65
lines changed

packages/http-tracker-core/src/services/scrape.rs

+109-65
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use std::sync::Arc;
1212

1313
use bittorrent_http_tracker_protocol::v1::requests::scrape::Scrape;
1414
use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::{self, ClientIpSources, PeerIpResolutionError};
15-
use bittorrent_primitives::info_hash::InfoHash;
1615
use bittorrent_tracker_core::authentication::service::AuthenticationService;
1716
use bittorrent_tracker_core::authentication::{self, Key};
1817
use bittorrent_tracker_core::error::{ScrapeError, TrackerCoreError, WhitelistError};
@@ -63,6 +62,9 @@ impl ScrapeService {
6362

6463
/// Handles a scrape request.
6564
///
65+
/// When the peer is not authenticated and the tracker is running in `private`
66+
/// mode, the tracker returns empty stats for all the torrents.
67+
///
6668
/// # Errors
6769
///
6870
/// This function will return an error if:
@@ -74,67 +76,43 @@ impl ScrapeService {
7476
client_ip_sources: &ClientIpSources,
7577
maybe_key: Option<Key>,
7678
) -> Result<ScrapeData, HttpScrapeError> {
77-
// Authentication
78-
let return_fake_scrape_data = if self.core_config.private {
79-
match maybe_key {
80-
Some(key) => match self.authentication_service.authenticate(&key).await {
81-
Ok(()) => false,
82-
Err(_error) => true,
83-
},
84-
None => true,
85-
}
79+
let scrape_data = if self.authentication_is_required() && !self.is_authenticated(maybe_key).await {
80+
ScrapeData::zeroed(&scrape_request.info_hashes)
8681
} else {
87-
false
82+
self.scrape_handler.scrape(&scrape_request.info_hashes).await?
8883
};
8984

90-
// Authorization for scrape requests is handled at the `bittorrent_tracker_core`
91-
// level for each torrent.
85+
let remote_client_ip = self.resolve_remote_client_ip(client_ip_sources)?;
9286

93-
let peer_ip = match peer_ip_resolver::invoke(self.core_config.net.on_reverse_proxy, client_ip_sources) {
94-
Ok(peer_ip) => peer_ip,
95-
Err(error) => return Err(error.into()),
96-
};
87+
self.send_stats_event(&remote_client_ip).await;
9788

98-
if return_fake_scrape_data {
99-
return Ok(fake(&self.opt_http_stats_event_sender, &scrape_request.info_hashes, &peer_ip).await);
100-
}
89+
Ok(scrape_data)
90+
}
10191

102-
let scrape_data = self.scrape_handler.scrape(&scrape_request.info_hashes).await?;
92+
fn authentication_is_required(&self) -> bool {
93+
self.core_config.private
94+
}
10395

104-
send_scrape_event(&peer_ip, &self.opt_http_stats_event_sender).await;
96+
async fn is_authenticated(&self, maybe_key: Option<Key>) -> bool {
97+
if let Some(key) = maybe_key {
98+
return self.authentication_service.authenticate(&key).await.is_ok();
99+
}
105100

106-
Ok(scrape_data)
101+
false
107102
}
108-
}
109103

110-
/// The HTTP tracker fake `scrape` service. It returns zeroed stats.
111-
///
112-
/// When the peer is not authenticated and the tracker is running in `private` mode,
113-
/// the tracker returns empty stats for all the torrents.
114-
///
115-
/// > **NOTICE**: tracker statistics are not updated in this case.
116-
pub async fn fake(
117-
opt_http_stats_event_sender: &Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
118-
info_hashes: &Vec<InfoHash>,
119-
original_peer_ip: &IpAddr,
120-
) -> ScrapeData {
121-
send_scrape_event(original_peer_ip, opt_http_stats_event_sender).await;
122-
123-
ScrapeData::zeroed(info_hashes)
124-
}
104+
/// Resolves the client's real IP address considering proxy headers.
105+
fn resolve_remote_client_ip(&self, client_ip_sources: &ClientIpSources) -> Result<IpAddr, PeerIpResolutionError> {
106+
peer_ip_resolver::invoke(self.core_config.net.on_reverse_proxy, client_ip_sources)
107+
}
125108

126-
async fn send_scrape_event(
127-
original_peer_ip: &IpAddr,
128-
opt_http_stats_event_sender: &Arc<Option<Box<dyn statistics::event::sender::Sender>>>,
129-
) {
130-
if let Some(http_stats_event_sender) = opt_http_stats_event_sender.as_deref() {
131-
match original_peer_ip {
132-
IpAddr::V4(_) => {
133-
http_stats_event_sender.send_event(statistics::event::Event::Tcp4Scrape).await;
134-
}
135-
IpAddr::V6(_) => {
136-
http_stats_event_sender.send_event(statistics::event::Event::Tcp6Scrape).await;
137-
}
109+
async fn send_stats_event(&self, original_peer_ip: &IpAddr) {
110+
if let Some(http_stats_event_sender) = self.opt_http_stats_event_sender.as_deref() {
111+
let event = match original_peer_ip {
112+
IpAddr::V4(_) => statistics::event::Event::Tcp4Scrape,
113+
IpAddr::V6(_) => statistics::event::Event::Tcp6Scrape,
114+
};
115+
http_stats_event_sender.send_event(event).await;
138116
}
139117
}
140118
}
@@ -211,7 +189,6 @@ mod tests {
211189
use tokio::sync::mpsc::error::SendError;
212190
use torrust_tracker_configuration::Configuration;
213191
use torrust_tracker_primitives::{peer, DurationSinceUnixEpoch};
214-
use torrust_tracker_test_helpers::configuration;
215192

216193
use crate::statistics;
217194
use crate::tests::sample_info_hash;
@@ -222,10 +199,6 @@ mod tests {
222199
authentication_service: Arc<AuthenticationService>,
223200
}
224201

225-
fn initialize_services_for_public_tracker() -> Container {
226-
initialize_services_with_configuration(&configuration::ephemeral_public())
227-
}
228-
229202
fn initialize_services_with_configuration(config: &Configuration) -> Container {
230203
let database = initialize_database(&config.core);
231204
let in_memory_whitelist = Arc::new(InMemoryWhitelist::default());
@@ -436,28 +409,34 @@ mod tests {
436409
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
437410
use std::sync::Arc;
438411

412+
use bittorrent_http_tracker_protocol::v1::requests::scrape::Scrape;
413+
use bittorrent_http_tracker_protocol::v1::services::peer_ip_resolver::ClientIpSources;
439414
use bittorrent_tracker_core::announce_handler::PeersWanted;
440415
use mockall::predicate::eq;
441416
use torrust_tracker_primitives::core::ScrapeData;
417+
use torrust_tracker_test_helpers::configuration;
442418

443-
use crate::services::scrape::fake;
444419
use crate::services::scrape::tests::{
445-
initialize_services_for_public_tracker, sample_info_hashes, sample_peer, MockHttpStatsEventSender,
420+
initialize_services_with_configuration, sample_info_hashes, sample_peer, MockHttpStatsEventSender,
446421
};
422+
use crate::services::scrape::ScrapeService;
447423
use crate::statistics;
448424
use crate::tests::sample_info_hash;
449425

450426
#[tokio::test]
451-
async fn it_should_always_return_the_zeroed_scrape_data_for_a_torrent() {
427+
async fn it_should_return_the_zeroed_scrape_data_when_the_tracker_is_running_in_private_mode_and_the_peer_is_not_authenticated(
428+
) {
429+
let config = configuration::ephemeral_private();
430+
431+
let container = initialize_services_with_configuration(&config);
432+
452433
let (http_stats_event_sender, _http_stats_repository) = statistics::setup::factory(false);
453434
let http_stats_event_sender = Arc::new(http_stats_event_sender);
454435

455-
let container = initialize_services_for_public_tracker();
456-
457436
let info_hash = sample_info_hash();
458437
let info_hashes = vec![info_hash];
459438

460-
// Announce a new peer to force scrape data to contain not zeroed data
439+
// Announce a new peer to force scrape data to contain non zeroed data
461440
let mut peer = sample_peer();
462441
let original_peer_ip = peer.ip();
463442
container
@@ -466,7 +445,26 @@ mod tests {
466445
.await
467446
.unwrap();
468447

469-
let scrape_data = fake(&http_stats_event_sender, &info_hashes, &original_peer_ip).await;
448+
let scrape_request = Scrape {
449+
info_hashes: sample_info_hashes(),
450+
};
451+
452+
let client_ip_sources = ClientIpSources {
453+
right_most_x_forwarded_for: None,
454+
connection_info_ip: Some(original_peer_ip),
455+
};
456+
457+
let scrape_service = Arc::new(ScrapeService::new(
458+
Arc::new(config.core),
459+
container.scrape_handler.clone(),
460+
container.authentication_service.clone(),
461+
http_stats_event_sender.clone(),
462+
));
463+
464+
let scrape_data = scrape_service
465+
.handle_scrape(&scrape_request, &client_ip_sources, None)
466+
.await
467+
.unwrap();
470468

471469
let expected_scrape_data = ScrapeData::zeroed(&info_hashes);
472470

@@ -475,6 +473,10 @@ mod tests {
475473

476474
#[tokio::test]
477475
async fn it_should_send_the_tcp_4_scrape_event_when_the_peer_uses_ipv4() {
476+
let config = configuration::ephemeral();
477+
478+
let container = initialize_services_with_configuration(&config);
479+
478480
let mut http_stats_event_sender_mock = MockHttpStatsEventSender::new();
479481
http_stats_event_sender_mock
480482
.expect_send_event()
@@ -486,11 +488,34 @@ mod tests {
486488

487489
let peer_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1));
488490

489-
fake(&http_stats_event_sender, &sample_info_hashes(), &peer_ip).await;
491+
let scrape_request = Scrape {
492+
info_hashes: sample_info_hashes(),
493+
};
494+
495+
let client_ip_sources = ClientIpSources {
496+
right_most_x_forwarded_for: None,
497+
connection_info_ip: Some(peer_ip),
498+
};
499+
500+
let scrape_service = Arc::new(ScrapeService::new(
501+
Arc::new(config.core),
502+
container.scrape_handler.clone(),
503+
container.authentication_service.clone(),
504+
http_stats_event_sender.clone(),
505+
));
506+
507+
scrape_service
508+
.handle_scrape(&scrape_request, &client_ip_sources, None)
509+
.await
510+
.unwrap();
490511
}
491512

492513
#[tokio::test]
493514
async fn it_should_send_the_tcp_6_scrape_event_when_the_peer_uses_ipv6() {
515+
let config = configuration::ephemeral();
516+
517+
let container = initialize_services_with_configuration(&config);
518+
494519
let mut http_stats_event_sender_mock = MockHttpStatsEventSender::new();
495520
http_stats_event_sender_mock
496521
.expect_send_event()
@@ -502,7 +527,26 @@ mod tests {
502527

503528
let peer_ip = IpAddr::V6(Ipv6Addr::new(0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969, 0x6969));
504529

505-
fake(&http_stats_event_sender, &sample_info_hashes(), &peer_ip).await;
530+
let scrape_request = Scrape {
531+
info_hashes: sample_info_hashes(),
532+
};
533+
534+
let client_ip_sources = ClientIpSources {
535+
right_most_x_forwarded_for: None,
536+
connection_info_ip: Some(peer_ip),
537+
};
538+
539+
let scrape_service = Arc::new(ScrapeService::new(
540+
Arc::new(config.core),
541+
container.scrape_handler.clone(),
542+
container.authentication_service.clone(),
543+
http_stats_event_sender.clone(),
544+
));
545+
546+
scrape_service
547+
.handle_scrape(&scrape_request, &client_ip_sources, None)
548+
.await
549+
.unwrap();
506550
}
507551
}
508552
}

0 commit comments

Comments
 (0)