diff --git a/packages/configuration/src/lib.rs b/packages/configuration/src/lib.rs index 58de9458..6b3056e8 100644 --- a/packages/configuration/src/lib.rs +++ b/packages/configuration/src/lib.rs @@ -388,7 +388,7 @@ pub struct HealthCheckApi { } /// Announce policy -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct AnnouncePolicy { /// Interval in seconds that the client should wait between sending regular /// announce requests to the tracker. diff --git a/src/core/mod.rs b/src/core/mod.rs index beb4b133..646558d5 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -732,7 +732,7 @@ impl Tracker { let (stats, stats_updated) = self.torrents.update_torrent_with_peer_and_get_stats(info_hash, peer).await; if self.config.persistent_torrent_completed_stat && stats_updated { - let completed = stats.completed; + let completed = stats.downloaded; let info_hash = *info_hash; drop(self.database.save_persistent_torrent(&info_hash, completed).await); @@ -1390,7 +1390,7 @@ mod tests { let announce_data = tracker.announce(&sample_info_hash(), &mut peer, &peer_ip()).await; - assert_eq!(announce_data.swarm_stats.seeders, 1); + assert_eq!(announce_data.swarm_stats.complete, 1); } #[tokio::test] @@ -1401,7 +1401,7 @@ mod tests { let announce_data = tracker.announce(&sample_info_hash(), &mut peer, &peer_ip()).await; - assert_eq!(announce_data.swarm_stats.leechers, 1); + assert_eq!(announce_data.swarm_stats.incomplete, 1); } #[tokio::test] @@ -1415,7 +1415,7 @@ mod tests { let mut completed_peer = completed_peer(); let announce_data = tracker.announce(&sample_info_hash(), &mut completed_peer, &peer_ip()).await; - assert_eq!(announce_data.swarm_stats.completed, 1); + assert_eq!(announce_data.swarm_stats.downloaded, 1); } } } @@ -1739,11 +1739,11 @@ mod tests { peer.event = AnnounceEvent::Started; let swarm_stats = tracker.update_torrent_with_peer_and_get_stats(&info_hash, &peer).await; - assert_eq!(swarm_stats.completed, 0); + assert_eq!(swarm_stats.downloaded, 0); peer.event = AnnounceEvent::Completed; let swarm_stats = tracker.update_torrent_with_peer_and_get_stats(&info_hash, &peer).await; - assert_eq!(swarm_stats.completed, 1); + assert_eq!(swarm_stats.downloaded, 1); // Remove the newly updated torrent from memory tracker.torrents.get_torrents_mut().await.remove(&info_hash); diff --git a/src/core/torrent/mod.rs b/src/core/torrent/mod.rs index 79828d36..82e37ecb 100644 --- a/src/core/torrent/mod.rs +++ b/src/core/torrent/mod.rs @@ -73,18 +73,8 @@ impl SwarmMetadata { } } -/// Swarm statistics for one torrent. -/// -/// See [BEP 48: Tracker Protocol Extension: Scrape](https://www.bittorrent.org/beps/bep_0048.html) -#[derive(Debug, PartialEq, Default)] -pub struct SwarmStats { - /// The number of peers that have ever completed downloading - pub completed: u32, - /// The number of active peers that have completed downloading (seeders) - pub seeders: u32, - /// The number of active peers that have not completed downloading (leechers) - pub leechers: u32, -} +/// [`SwarmStats`] has the same form as [`SwarmMetadata`] +pub type SwarmStats = SwarmMetadata; impl Entry { #[must_use] diff --git a/src/core/torrent/repository.rs b/src/core/torrent/repository.rs index ac3d0305..d4f8ee5e 100644 --- a/src/core/torrent/repository.rs +++ b/src/core/torrent/repository.rs @@ -77,9 +77,9 @@ impl Repository for Sync { ( SwarmStats { - completed: stats.1, - seeders: stats.0, - leechers: stats.2, + downloaded: stats.1, + complete: stats.0, + incomplete: stats.2, }, stats_updated, ) @@ -131,9 +131,9 @@ impl Repository for SyncSingle { ( SwarmStats { - completed: stats.1, - seeders: stats.0, - leechers: stats.2, + downloaded: stats.1, + complete: stats.0, + incomplete: stats.2, }, stats_updated, ) @@ -176,9 +176,9 @@ impl TRepositoryAsync for RepositoryAsync { ( SwarmStats { - completed: stats.1, - seeders: stats.0, - leechers: stats.2, + downloaded: stats.1, + complete: stats.0, + incomplete: stats.2, }, stats_updated, ) @@ -234,9 +234,9 @@ impl TRepositoryAsync for AsyncSync { ( SwarmStats { - completed: stats.1, - seeders: stats.0, - leechers: stats.2, + downloaded: stats.1, + complete: stats.0, + incomplete: stats.2, }, stats_updated, ) @@ -281,9 +281,9 @@ impl TRepositoryAsync for RepositoryAsyncSingle { ( SwarmStats { - completed: stats.1, - seeders: stats.0, - leechers: stats.2, + downloaded: stats.1, + complete: stats.0, + incomplete: stats.2, }, stats_updated, ) diff --git a/src/servers/http/v1/responses/announce.rs b/src/servers/http/v1/responses/announce.rs index b19a311d..14ae9156 100644 --- a/src/servers/http/v1/responses/announce.rs +++ b/src/servers/http/v1/responses/announce.rs @@ -7,11 +7,11 @@ use std::panic::Location; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; -use serde::{self, Deserialize, Serialize}; use thiserror::Error; use torrust_tracker_configuration::AnnouncePolicy; use torrust_tracker_contrib_bencode::{ben_bytes, ben_int, ben_list, ben_map, BMutAccess, BencodeMut}; +use crate::core::torrent::SwarmStats; use crate::core::{self, AnnounceData}; use crate::servers::http::v1::responses; @@ -22,6 +22,7 @@ use crate::servers::http::v1::responses; /// ```rust /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// use torrust_tracker_configuration::AnnouncePolicy; +/// use torrust_tracker::core::torrent::SwarmStats; /// use torrust_tracker::servers::http::v1::responses::announce::{Normal, NormalPeer}; /// /// let response = Normal { @@ -29,8 +30,11 @@ use crate::servers::http::v1::responses; /// interval: 111, /// interval_min: 222, /// }, -/// complete: 333, -/// incomplete: 444, +/// stats: SwarmStats { +/// downloaded: 0, +/// complete: 333, +/// incomplete: 444, +/// }, /// peers: vec![ /// // IPV4 /// NormalPeer { @@ -60,15 +64,10 @@ use crate::servers::http::v1::responses; /// /// Refer to [BEP 03: The `BitTorrent` Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html) /// for more information. -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Normal { - /// Announce policy pub policy: AnnouncePolicy, - /// Number of peers with the entire file, i.e. seeders. - pub complete: u32, - /// Number of non-seeder peers, aka "leechers". - pub incomplete: u32, - /// A list of peers. The value is a list of dictionaries. + pub stats: SwarmStats, pub peers: Vec, } @@ -85,7 +84,7 @@ pub struct Normal { /// port: 0x7070, // 28784 /// }; /// ``` -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct NormalPeer { /// The peer's ID. pub peer_id: [u8; 20], @@ -131,8 +130,8 @@ impl Normal { } (ben_map! { - "complete" => ben_int!(i64::from(self.complete)), - "incomplete" => ben_int!(i64::from(self.incomplete)), + "complete" => ben_int!(i64::from(self.stats.complete)), + "incomplete" => ben_int!(i64::from(self.stats.incomplete)), "interval" => ben_int!(i64::from(self.policy.interval)), "min interval" => ben_int!(i64::from(self.policy.interval_min)), "peers" => peers_list.clone() @@ -160,8 +159,11 @@ impl From for Normal { interval: domain_announce_response.interval, interval_min: domain_announce_response.interval_min, }, - complete: domain_announce_response.swarm_stats.seeders, - incomplete: domain_announce_response.swarm_stats.leechers, + stats: SwarmStats { + complete: domain_announce_response.swarm_stats.complete, + incomplete: domain_announce_response.swarm_stats.incomplete, + downloaded: 0, + }, peers, } } @@ -176,6 +178,7 @@ impl From for Normal { /// ```rust /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// use torrust_tracker_configuration::AnnouncePolicy; +/// use torrust_tracker::core::torrent::SwarmStats; /// use torrust_tracker::servers::http::v1::responses::announce::{Compact, CompactPeer}; /// /// let response = Compact { @@ -183,8 +186,11 @@ impl From for Normal { /// interval: 111, /// interval_min: 222, /// }, -/// complete: 333, -/// incomplete: 444, +/// stats: SwarmStats { +/// downloaded: 0, +/// complete: 333, +/// incomplete: 444, +/// }, /// peers: vec![ /// // IPV4 /// CompactPeer { @@ -216,15 +222,10 @@ impl From for Normal { /// /// - [BEP 23: Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html) /// - [BEP 07: IPv6 Tracker Extension](https://www.bittorrent.org/beps/bep_0007.html) -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct Compact { - /// Announce policy pub policy: AnnouncePolicy, - /// Number of seeders, aka "completed". - pub complete: u32, - /// Number of non-seeder peers, aka "incomplete". - pub incomplete: u32, - /// Compact peer list. + pub stats: SwarmStats, pub peers: Vec, } @@ -250,7 +251,7 @@ pub struct Compact { /// /// Refer to [BEP 23: Tracker Returns Compact Peer Lists](https://www.bittorrent.org/beps/bep_0023.html) /// for more information. -#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct CompactPeer { /// The peer's IP address. pub ip: IpAddr, @@ -296,8 +297,8 @@ impl Compact { /// Will return `Err` if internally interrupted. pub fn body(&self) -> Result, Box> { let bytes = (ben_map! { - "complete" => ben_int!(i64::from(self.complete)), - "incomplete" => ben_int!(i64::from(self.incomplete)), + "complete" => ben_int!(i64::from(self.stats.complete)), + "incomplete" => ben_int!(i64::from(self.stats.incomplete)), "interval" => ben_int!(i64::from(self.policy.interval)), "min interval" => ben_int!(i64::from(self.policy.interval_min)), "peers" => ben_bytes!(self.peers_v4_bytes()?), @@ -381,8 +382,11 @@ impl From for Compact { interval: domain_announce_response.interval, interval_min: domain_announce_response.interval_min, }, - complete: domain_announce_response.swarm_stats.seeders, - incomplete: domain_announce_response.swarm_stats.leechers, + stats: SwarmStats { + complete: domain_announce_response.swarm_stats.complete, + incomplete: domain_announce_response.swarm_stats.incomplete, + downloaded: 0, + }, peers, } } @@ -396,6 +400,7 @@ mod tests { use torrust_tracker_configuration::AnnouncePolicy; use super::{Normal, NormalPeer}; + use crate::core::torrent::SwarmStats; use crate::servers::http::v1::responses::announce::{Compact, CompactPeer}; // Some ascii values used in tests: @@ -417,8 +422,11 @@ mod tests { interval: 111, interval_min: 222, }, - complete: 333, - incomplete: 444, + stats: SwarmStats { + downloaded: 0, + complete: 333, + incomplete: 444, + }, peers: vec![ // IPV4 NormalPeer { @@ -453,8 +461,11 @@ mod tests { interval: 111, interval_min: 222, }, - complete: 333, - incomplete: 444, + stats: SwarmStats { + downloaded: 0, + complete: 333, + incomplete: 444, + }, peers: vec![ // IPV4 CompactPeer { diff --git a/src/servers/http/v1/services/announce.rs b/src/servers/http/v1/services/announce.rs index bdf8afc8..547dcd35 100644 --- a/src/servers/http/v1/services/announce.rs +++ b/src/servers/http/v1/services/announce.rs @@ -114,9 +114,9 @@ mod tests { let expected_announce_data = AnnounceData { peers: vec![], swarm_stats: SwarmStats { - completed: 0, - seeders: 1, - leechers: 0, + downloaded: 0, + complete: 1, + incomplete: 0, }, interval: tracker.config.announce_interval, interval_min: tracker.config.min_announce_interval, diff --git a/src/servers/udp/handlers.rs b/src/servers/udp/handlers.rs index 18a34141..4e3080b3 100644 --- a/src/servers/udp/handlers.rs +++ b/src/servers/udp/handlers.rs @@ -152,8 +152,8 @@ pub async fn handle_announce( let announce_response = AnnounceResponse { transaction_id: wrapped_announce_request.announce_request.transaction_id, announce_interval: AnnounceInterval(i64::from(tracker.config.announce_interval) as i32), - leechers: NumberOfPeers(i64::from(response.swarm_stats.leechers) as i32), - seeders: NumberOfPeers(i64::from(response.swarm_stats.seeders) as i32), + leechers: NumberOfPeers(i64::from(response.swarm_stats.incomplete) as i32), + seeders: NumberOfPeers(i64::from(response.swarm_stats.complete) as i32), peers: response .peers .iter() @@ -177,8 +177,8 @@ pub async fn handle_announce( let announce_response = AnnounceResponse { transaction_id: wrapped_announce_request.announce_request.transaction_id, announce_interval: AnnounceInterval(i64::from(tracker.config.announce_interval) as i32), - leechers: NumberOfPeers(i64::from(response.swarm_stats.leechers) as i32), - seeders: NumberOfPeers(i64::from(response.swarm_stats.seeders) as i32), + leechers: NumberOfPeers(i64::from(response.swarm_stats.incomplete) as i32), + seeders: NumberOfPeers(i64::from(response.swarm_stats.complete) as i32), peers: response .peers .iter()