Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@ serde_urlencoded = "0"
torrust-tracker-test-helpers = { version = "3.0.0-alpha.12-develop", path = "packages/test-helpers" }

[workspace]
members = ["contrib/bencode", "packages/configuration", "packages/located-error", "packages/primitives", "packages/test-helpers", "packages/torrent-repository-benchmarks"]
members = [
"contrib/bencode",
"packages/configuration",
"packages/located-error",
"packages/primitives",
"packages/test-helpers",
"packages/torrent-repository-benchmarks",
]

[profile.dev]
debug = 1
Expand Down
1 change: 1 addition & 0 deletions cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"Hydranode",
"Icelake",
"imdl",
"impls",
"incompletei",
"infohash",
"infohashes",
Expand Down
1 change: 1 addition & 0 deletions packages/configuration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ version.workspace = true

[dependencies]
config = "0"
derive_more = "0"
log = { version = "0", features = ["release_max_level_info"] }
serde = { version = "1", features = ["derive"] }
serde_with = "3"
Expand Down
69 changes: 46 additions & 23 deletions packages/configuration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ use std::sync::Arc;
use std::{env, fs};

use config::{Config, ConfigError, File, FileFormat};
use derive_more::Constructor;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, NoneAsEmptyString};
use thiserror::Error;
Expand Down Expand Up @@ -387,26 +388,9 @@ pub struct HealthCheckApi {
pub bind_address: String,
}

/// Core configuration for the tracker.
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct Configuration {
/// Logging level. Possible values are: `Off`, `Error`, `Warn`, `Info`,
/// `Debug` and `Trace`. Default is `Info`.
pub log_level: Option<String>,
/// Tracker mode. See [`TrackerMode`] for more information.
pub mode: TrackerMode,

// Database configuration
/// Database driver. Possible values are: `Sqlite3`, and `MySQL`.
pub db_driver: DatabaseDriver,
/// Database connection string. The format depends on the database driver.
/// For `Sqlite3`, the format is `path/to/database.db`, for example:
/// `./storage/tracker/lib/database/sqlite3.db`.
/// For `Mysql`, the format is `mysql://db_user:db_user_password:port/db_name`, for
/// example: `root:password@localhost:3306/torrust`.
pub db_path: String,

/// Announce policy
#[derive(PartialEq, Eq, Debug, Clone, Copy, Constructor)]
pub struct AnnouncePolicy {
/// Interval in seconds that the client should wait between sending regular
/// announce requests to the tracker.
///
Expand All @@ -418,7 +402,8 @@ pub struct Configuration {
/// client's initial request. It serves as a guideline for clients to know
/// how often they should contact the tracker for updates on the peer list,
/// while ensuring that the tracker is not overwhelmed with requests.
pub announce_interval: u32,
pub interval: u32,

/// Minimum announce interval. Clients must not reannounce more frequently
/// than this.
///
Expand All @@ -430,6 +415,42 @@ pub struct Configuration {
/// value to prevent sending too many requests in a short period, which
/// could lead to excessive load on the tracker or even getting banned by
/// the tracker for not adhering to the rules.
pub interval_min: u32,
}

impl Default for AnnouncePolicy {
fn default() -> Self {
Self {
interval: 120,
interval_min: 120,
}
}
}

/// Core configuration for the tracker.
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct Configuration {
/// Logging level. Possible values are: `Off`, `Error`, `Warn`, `Info`,
/// `Debug` and `Trace`. Default is `Info`.
pub log_level: Option<String>,
/// Tracker mode. See [`TrackerMode`] for more information.
pub mode: TrackerMode,

// Database configuration
/// Database driver. Possible values are: `Sqlite3`, and `MySQL`.
pub db_driver: DatabaseDriver,
/// Database connection string. The format depends on the database driver.
/// For `Sqlite3`, the format is `path/to/database.db`, for example:
/// `./storage/tracker/lib/database/sqlite3.db`.
/// For `Mysql`, the format is `mysql://db_user:db_user_password:port/db_name`, for
/// example: `root:password@localhost:3306/torrust`.
pub db_path: String,

/// See [`AnnouncePolicy::interval`]
pub announce_interval: u32,

/// See [`AnnouncePolicy::interval_min`]
pub min_announce_interval: u32,
/// Weather the tracker is behind a reverse proxy or not.
/// If the tracker is behind a reverse proxy, the `X-Forwarded-For` header
Expand Down Expand Up @@ -516,13 +537,15 @@ impl From<ConfigError> for Error {

impl Default for Configuration {
fn default() -> Self {
let announce_policy = AnnouncePolicy::default();

let mut configuration = Configuration {
log_level: Option::from(String::from("info")),
mode: TrackerMode::Public,
db_driver: DatabaseDriver::Sqlite3,
db_path: String::from("./storage/tracker/lib/database/sqlite3.db"),
announce_interval: 120,
min_announce_interval: 120,
announce_interval: announce_policy.interval,
min_announce_interval: announce_policy.interval_min,
max_peer_timeout: 900,
on_reverse_proxy: false,
external_ip: Some(String::from("0.0.0.0")),
Expand Down
2 changes: 1 addition & 1 deletion src/core/databases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ pub trait Database: Sync + Send {
/// # Errors
///
/// Will return `Err` if unable to save.
async fn save_persistent_torrent(&self, info_hash: &InfoHash, completed: u32) -> Result<(), Error>;
async fn save_persistent_torrent(&self, info_hash: &InfoHash, downloaded: u32) -> Result<(), Error>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// Whitelist

Expand Down
40 changes: 18 additions & 22 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@
//!
//! ```rust,no_run
//! use torrust_tracker::core::peer::Peer;
//! use torrust_tracker_configuration::AnnouncePolicy;
//!
//! pub struct AnnounceData {
//! pub peers: Vec<Peer>,
//! pub swarm_stats: SwarmStats,
//! pub interval: u32, // Option `announce_interval` from core tracker configuration
//! pub interval_min: u32, // Option `min_announce_interval` from core tracker configuration
//! pub policy: AnnouncePolicy, // the tracker announce policy.
//! }
//!
//! pub struct SwarmStats {
Expand Down Expand Up @@ -445,9 +445,10 @@ use std::panic::Location;
use std::sync::Arc;
use std::time::Duration;

use derive_more::Constructor;
use futures::future::join_all;
use tokio::sync::mpsc::error::SendError;
use torrust_tracker_configuration::Configuration;
use torrust_tracker_configuration::{AnnouncePolicy, Configuration};
use torrust_tracker_primitives::TrackerMode;

use self::auth::Key;
Expand Down Expand Up @@ -487,7 +488,7 @@ pub struct Tracker {
/// Structure that holds general `Tracker` torrents metrics.
///
/// Metrics are aggregate values for all torrents.
#[derive(Debug, PartialEq, Default)]
#[derive(Copy, Clone, Debug, PartialEq, Default)]
pub struct TorrentsMetrics {
/// Total number of seeders for all torrents
pub seeders: u64,
Expand All @@ -500,20 +501,14 @@ pub struct TorrentsMetrics {
}

/// Structure that holds the data returned by the `announce` request.
#[derive(Debug, PartialEq, Default)]
#[derive(Clone, Debug, PartialEq, Constructor, Default)]
pub struct AnnounceData {
/// The list of peers that are downloading the same torrent.
/// It excludes the peer that made the request.
pub peers: Vec<Peer>,
/// Swarm statistics
pub swarm_stats: SwarmStats,
/// The interval in seconds that the client should wait between sending
/// regular requests to the tracker.
/// Refer to [`announce_interval`](torrust_tracker_configuration::Configuration::announce_interval).
pub interval: u32,
/// The minimum announce interval in seconds that the client should wait.
/// Refer to [`min_announce_interval`](torrust_tracker_configuration::Configuration::min_announce_interval).
pub interval_min: u32,
pub stats: SwarmStats,
pub policy: AnnouncePolicy,
Comment on lines +510 to +511
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

}

/// Structure that holds the data returned by the `scrape` request.
Expand Down Expand Up @@ -628,11 +623,12 @@ impl Tracker {

let peers = self.get_torrent_peers_for_peer(info_hash, peer).await;

let policy = AnnouncePolicy::new(self.config.announce_interval, self.config.min_announce_interval);

AnnounceData {
peers,
swarm_stats,
interval: self.config.announce_interval,
interval_min: self.config.min_announce_interval,
stats: swarm_stats,
policy,
}
}

Expand Down Expand Up @@ -732,7 +728,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);
Expand Down Expand Up @@ -1390,7 +1386,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.stats.complete, 1);
}

#[tokio::test]
Expand All @@ -1401,7 +1397,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.stats.incomplete, 1);
}

#[tokio::test]
Expand All @@ -1415,7 +1411,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.stats.downloaded, 1);
}
}
}
Expand Down Expand Up @@ -1739,11 +1735,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);
Expand Down
80 changes: 78 additions & 2 deletions src/core/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,85 @@ impl Serialize for Id {
}
}

#[cfg(test)]
mod test {
pub mod fixture {
use std::net::SocketAddr;

use aquatic_udp_protocol::NumberOfBytes;

use super::{Id, Peer};

#[derive(PartialEq, Debug)]

pub struct PeerBuilder {
peer: Peer,
}

#[allow(clippy::derivable_impls)]
impl Default for PeerBuilder {
fn default() -> Self {
Self { peer: Peer::default() }
}
}

impl PeerBuilder {
#[allow(dead_code)]
#[must_use]
pub fn with_peer_id(mut self, peer_id: &Id) -> Self {
self.peer.peer_id = *peer_id;
self
}

#[allow(dead_code)]
#[must_use]
pub fn with_peer_addr(mut self, peer_addr: &SocketAddr) -> Self {
self.peer.peer_addr = *peer_addr;
self
}

#[allow(dead_code)]
#[must_use]
pub fn with_bytes_pending_to_download(mut self, left: i64) -> Self {
self.peer.left = NumberOfBytes(left);
self
}

#[allow(dead_code)]
#[must_use]
pub fn with_no_bytes_pending_to_download(mut self) -> Self {
self.peer.left = NumberOfBytes(0);
self
}

#[allow(dead_code)]
#[must_use]
pub fn build(self) -> Peer {
self.into()
}

#[allow(dead_code)]
#[must_use]
pub fn into(self) -> Peer {
self.peer
}
}

impl Default for Peer {
fn default() -> Self {
Self {
peer_id: Id(*b"-qB00000000000000000"),
peer_addr: std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(126, 0, 0, 1)), 8080),
updated: crate::shared::clock::DurationSinceUnixEpoch::new(1_669_397_478_934, 0),
uploaded: NumberOfBytes(0),
downloaded: NumberOfBytes(0),
left: NumberOfBytes(0),
event: aquatic_udp_protocol::AnnounceEvent::Started,
}
}
}
}

#[cfg(test)]
pub mod test {
mod torrent_peer_id {
use crate::core::peer;

Expand Down
Loading