Skip to content

Commit 1b745a6

Browse files
committed
Merge #1262: Overhaul core Tracker: review tracker-core package documentation
181c27e docs: [#1261] review docs for tracker-core package (Jose Celano) 74d0d28 docs: [#1261] fix doc errors in tracker-core (Jose Celano) Pull request description: Overhaul core Tracker: review `tracker-core` package documentation. ACKs for top commit: josecelano: ACK 181c27e Tree-SHA512: 993bf7a44bc53c0234af2a563312fd07f8037c9728917c0e474c66237db8faec1841b1fa35d939c35989927d3f8cd71d9bfd6fe5c7d91ae56a22087a89d6e476
2 parents efe7e98 + 181c27e commit 1b745a6

33 files changed

+1463
-628
lines changed

packages/http-protocol/src/v1/query.rs

+7
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ mod tests {
249249
assert_eq!(query.get_param("param2"), Some("value2".to_string()));
250250
}
251251

252+
#[test]
253+
fn should_ignore_duplicate_param_values_when_asked_to_return_only_one_value() {
254+
let query = Query::from(vec![("param1", "value1"), ("param1", "value2")]);
255+
256+
assert_eq!(query.get_param("param1"), Some("value1".to_string()));
257+
}
258+
252259
#[test]
253260
fn should_fail_parsing_an_invalid_query_string() {
254261
let invalid_raw_query = "name=value=value";

packages/tracker-core/src/announce_handler.rs

+121-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,95 @@
1+
//! Announce handler.
2+
//!
3+
//! Handling `announce` requests is the most important task for a `BitTorrent`
4+
//! tracker.
5+
//!
6+
//! A `BitTorrent` swarm is a network of peers that are all trying to download
7+
//! the same torrent. When a peer wants to find other peers it announces itself
8+
//! to the swarm via the tracker. The peer sends its data to the tracker so that
9+
//! the tracker can add it to the swarm. The tracker responds to the peer with
10+
//! the list of other peers in the swarm so that the peer can contact them to
11+
//! start downloading pieces of the file from them.
12+
//!
13+
//! Once you have instantiated the `AnnounceHandler` you can `announce` a new [`peer::Peer`](torrust_tracker_primitives) with:
14+
//!
15+
//! ```rust,no_run
16+
//! use std::net::SocketAddr;
17+
//! use std::net::IpAddr;
18+
//! use std::net::Ipv4Addr;
19+
//! use std::str::FromStr;
20+
//!
21+
//! use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes, PeerId};
22+
//! use torrust_tracker_primitives::DurationSinceUnixEpoch;
23+
//! use torrust_tracker_primitives::peer;
24+
//! use bittorrent_primitives::info_hash::InfoHash;
25+
//!
26+
//! let info_hash = InfoHash::from_str("3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0").unwrap();
27+
//!
28+
//! let peer = peer::Peer {
29+
//! peer_id: PeerId(*b"-qB00000000000000001"),
30+
//! peer_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), 8081),
31+
//! updated: DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
32+
//! uploaded: NumberOfBytes::new(0),
33+
//! downloaded: NumberOfBytes::new(0),
34+
//! left: NumberOfBytes::new(0),
35+
//! event: AnnounceEvent::Completed,
36+
//! };
37+
//!
38+
//! let peer_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
39+
//! ```
40+
//!
41+
//! ```text
42+
//! let announce_data = announce_handler.announce(&info_hash, &mut peer, &peer_ip).await;
43+
//! ```
44+
//!
45+
//! The handler returns the list of peers for the torrent with the infohash
46+
//! `3b245504cf5f11bbdbe1201cea6a6bf45aee1bc0`, filtering out the peer that is
47+
//! making the `announce` request.
48+
//!
49+
//! > **NOTICE**: that the peer argument is mutable because the handler can
50+
//! > change the peer IP if the peer is using a loopback IP.
51+
//!
52+
//! The `peer_ip` argument is the resolved peer ip. It's a common practice that
53+
//! trackers ignore the peer ip in the `announce` request params, and resolve
54+
//! the peer ip using the IP of the client making the request. As the tracker is
55+
//! a domain service, the peer IP must be provided for the handler user, which
56+
//! is usually a higher component with access the the request metadata, for
57+
//! example, connection data, proxy headers, etcetera.
58+
//!
59+
//! The returned struct is:
60+
//!
61+
//! ```rust,no_run
62+
//! use torrust_tracker_primitives::peer;
63+
//! use torrust_tracker_configuration::AnnouncePolicy;
64+
//!
65+
//! pub struct AnnounceData {
66+
//! pub peers: Vec<peer::Peer>,
67+
//! pub swarm_stats: SwarmMetadata,
68+
//! pub policy: AnnouncePolicy, // the tracker announce policy.
69+
//! }
70+
//!
71+
//! pub struct SwarmMetadata {
72+
//! pub completed: u32, // The number of peers that have ever completed downloading
73+
//! pub seeders: u32, // The number of active peers that have completed downloading (seeders)
74+
//! pub leechers: u32, // The number of active peers that have not completed downloading (leechers)
75+
//! }
76+
//!
77+
//! // Core tracker configuration
78+
//! pub struct AnnounceInterval {
79+
//! // ...
80+
//! pub interval: u32, // Interval in seconds that the client should wait between sending regular announce requests to the tracker
81+
//! pub interval_min: u32, // Minimum announce interval. Clients must not reannounce more frequently than this
82+
//! // ...
83+
//! }
84+
//! ```
85+
//!
86+
//! ## Related BEPs:
87+
//!
88+
//! Refer to `BitTorrent` BEPs and other sites for more information about the `announce` request:
89+
//!
90+
//! - [BEP 3. The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html)
91+
//! - [BEP 23. Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html)
92+
//! - [Vuze docs](https://wiki.vuze.com/w/Announce)
193
use std::net::IpAddr;
294
use std::sync::Arc;
395

@@ -10,18 +102,20 @@ use torrust_tracker_primitives::swarm_metadata::SwarmMetadata;
10102
use super::torrent::repository::in_memory::InMemoryTorrentRepository;
11103
use super::torrent::repository::persisted::DatabasePersistentTorrentRepository;
12104

105+
/// Handles `announce` requests from `BitTorrent` clients.
13106
pub struct AnnounceHandler {
14107
/// The tracker configuration.
15108
config: Core,
16109

17-
/// The in-memory torrents repository.
110+
/// Repository for in-memory torrent data.
18111
in_memory_torrent_repository: Arc<InMemoryTorrentRepository>,
19112

20-
/// The persistent torrents repository.
113+
/// Repository for persistent torrent data (database).
21114
db_torrent_repository: Arc<DatabasePersistentTorrentRepository>,
22115
}
23116

24117
impl AnnounceHandler {
118+
/// Creates a new `AnnounceHandler`.
25119
#[must_use]
26120
pub fn new(
27121
config: &Core,
@@ -35,9 +129,20 @@ impl AnnounceHandler {
35129
}
36130
}
37131

38-
/// It handles an announce request.
132+
/// Processes an announce request from a peer.
39133
///
40134
/// BEP 03: [The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html).
135+
///
136+
/// # Parameters
137+
///
138+
/// - `info_hash`: The unique identifier of the torrent.
139+
/// - `peer`: The peer announcing itself (may be updated if IP is adjusted).
140+
/// - `remote_client_ip`: The IP address of the client making the request.
141+
/// - `peers_wanted`: Specifies how many peers the client wants in the response.
142+
///
143+
/// # Returns
144+
///
145+
/// An `AnnounceData` struct containing the list of peers, swarm statistics, and tracker policy.
41146
pub fn announce(
42147
&self,
43148
info_hash: &InfoHash,
@@ -77,9 +182,8 @@ impl AnnounceHandler {
77182
}
78183
}
79184

80-
/// It updates the torrent entry in memory, it also stores in the database
81-
/// the torrent info data which is persistent, and finally return the data
82-
/// needed for a `announce` request response.
185+
/// Updates the torrent data in memory, persists statistics if needed, and
186+
/// returns the updated swarm stats.
83187
#[must_use]
84188
fn upsert_peer_and_get_stats(&self, info_hash: &InfoHash, peer: &peer::Peer) -> SwarmMetadata {
85189
let swarm_metadata_before = self.in_memory_torrent_repository.get_swarm_metadata(info_hash);
@@ -95,7 +199,7 @@ impl AnnounceHandler {
95199
swarm_metadata_after
96200
}
97201

98-
/// It stores the torrents stats into the database (if persistency is enabled).
202+
/// Persists torrent statistics to the database if persistence is enabled.
99203
fn persist_stats(&self, info_hash: &InfoHash, swarm_metadata: &SwarmMetadata) {
100204
if self.config.tracker_policy.persistent_torrent_completed_stat {
101205
let completed = swarm_metadata.downloaded;
@@ -106,22 +210,25 @@ impl AnnounceHandler {
106210
}
107211
}
108212

109-
/// How many peers the peer announcing wants in the announce response.
213+
/// Specifies how many peers a client wants in the announce response.
110214
#[derive(Clone, Debug, PartialEq, Default)]
111215
pub enum PeersWanted {
112-
/// The peer wants as many peers as possible in the announce response.
216+
/// Request as many peers as possible (default behavior).
113217
#[default]
114218
AsManyAsPossible,
115-
/// The peer only wants a certain amount of peers in the announce response.
219+
220+
/// Request a specific number of peers.
116221
Only { amount: usize },
117222
}
118223

119224
impl PeersWanted {
225+
/// Request a specific number of peers.
120226
#[must_use]
121227
pub fn only(limit: u32) -> Self {
122228
limit.into()
123229
}
124230

231+
/// Returns the maximum number of peers allowed based on the request and tracker limit.
125232
fn limit(&self) -> usize {
126233
match self {
127234
PeersWanted::AsManyAsPossible => TORRENT_PEERS_LIMIT,
@@ -159,6 +266,10 @@ impl From<u32> for PeersWanted {
159266
}
160267
}
161268

269+
/// Assigns the correct IP address to a peer based on tracker settings.
270+
///
271+
/// If the client IP is a loopback address and the tracker has an external IP
272+
/// configured, the external IP will be assigned to the peer.
162273
#[must_use]
163274
fn assign_ip_address_to_peer(remote_client_ip: &IpAddr, tracker_external_ip: Option<IpAddr>) -> IpAddr {
164275
if let Some(host_ip) = tracker_external_ip.filter(|_| remote_client_ip.is_loopback()) {

0 commit comments

Comments
 (0)