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)
1
93
use std:: net:: IpAddr ;
2
94
use std:: sync:: Arc ;
3
95
@@ -10,18 +102,20 @@ use torrust_tracker_primitives::swarm_metadata::SwarmMetadata;
10
102
use super :: torrent:: repository:: in_memory:: InMemoryTorrentRepository ;
11
103
use super :: torrent:: repository:: persisted:: DatabasePersistentTorrentRepository ;
12
104
105
+ /// Handles `announce` requests from `BitTorrent` clients.
13
106
pub struct AnnounceHandler {
14
107
/// The tracker configuration.
15
108
config : Core ,
16
109
17
- /// The in-memory torrents repository .
110
+ /// Repository for in-memory torrent data .
18
111
in_memory_torrent_repository : Arc < InMemoryTorrentRepository > ,
19
112
20
- /// The persistent torrents repository .
113
+ /// Repository for persistent torrent data (database) .
21
114
db_torrent_repository : Arc < DatabasePersistentTorrentRepository > ,
22
115
}
23
116
24
117
impl AnnounceHandler {
118
+ /// Creates a new `AnnounceHandler`.
25
119
#[ must_use]
26
120
pub fn new (
27
121
config : & Core ,
@@ -35,9 +129,20 @@ impl AnnounceHandler {
35
129
}
36
130
}
37
131
38
- /// It handles an announce request.
132
+ /// Processes an announce request from a peer .
39
133
///
40
134
/// 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.
41
146
pub fn announce (
42
147
& self ,
43
148
info_hash : & InfoHash ,
@@ -77,9 +182,8 @@ impl AnnounceHandler {
77
182
}
78
183
}
79
184
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.
83
187
#[ must_use]
84
188
fn upsert_peer_and_get_stats ( & self , info_hash : & InfoHash , peer : & peer:: Peer ) -> SwarmMetadata {
85
189
let swarm_metadata_before = self . in_memory_torrent_repository . get_swarm_metadata ( info_hash) ;
@@ -95,7 +199,7 @@ impl AnnounceHandler {
95
199
swarm_metadata_after
96
200
}
97
201
98
- /// It stores the torrents stats into the database ( if persistency is enabled) .
202
+ /// Persists torrent statistics to the database if persistence is enabled.
99
203
fn persist_stats ( & self , info_hash : & InfoHash , swarm_metadata : & SwarmMetadata ) {
100
204
if self . config . tracker_policy . persistent_torrent_completed_stat {
101
205
let completed = swarm_metadata. downloaded ;
@@ -106,22 +210,25 @@ impl AnnounceHandler {
106
210
}
107
211
}
108
212
109
- /// How many peers the peer announcing wants in the announce response.
213
+ /// Specifies how many peers a client wants in the announce response.
110
214
#[ derive( Clone , Debug , PartialEq , Default ) ]
111
215
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) .
113
217
#[ default]
114
218
AsManyAsPossible ,
115
- /// The peer only wants a certain amount of peers in the announce response.
219
+
220
+ /// Request a specific number of peers.
116
221
Only { amount : usize } ,
117
222
}
118
223
119
224
impl PeersWanted {
225
+ /// Request a specific number of peers.
120
226
#[ must_use]
121
227
pub fn only ( limit : u32 ) -> Self {
122
228
limit. into ( )
123
229
}
124
230
231
+ /// Returns the maximum number of peers allowed based on the request and tracker limit.
125
232
fn limit ( & self ) -> usize {
126
233
match self {
127
234
PeersWanted :: AsManyAsPossible => TORRENT_PEERS_LIMIT ,
@@ -159,6 +266,10 @@ impl From<u32> for PeersWanted {
159
266
}
160
267
}
161
268
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.
162
273
#[ must_use]
163
274
fn assign_ip_address_to_peer ( remote_client_ip : & IpAddr , tracker_external_ip : Option < IpAddr > ) -> IpAddr {
164
275
if let Some ( host_ip) = tracker_external_ip. filter ( |_| remote_client_ip. is_loopback ( ) ) {
0 commit comments