Skip to content

Commit

Permalink
Merge pull request #44 from torrust/development
Browse files Browse the repository at this point in the history
release: 2.3.0
  • Loading branch information
mickvandijke authored May 9, 2022
2 parents 851ad77 + df87a9d commit 421f1fd
Show file tree
Hide file tree
Showing 46 changed files with 2,962 additions and 1,544 deletions.
1,612 changes: 1,267 additions & 345 deletions Cargo.lock

Large diffs are not rendered by default.

38 changes: 27 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
[package]
edition = "2021"
name = "torrust-tracker"
version = "2.2.0"
version = "2.3.0"
license = "AGPL-3.0"
authors = ["Mick van Dijke <[email protected]>"]
description = "A feature rich BitTorrent tracker."
edition = "2018"
repository = "https://github.com/torrust/torrust-tracker"

[profile.dev]
debug = 1
opt-level = 1
lto = "thin"

[profile.release]
debug = 1
opt-level = 3
lto = "fat"
strip = true

[dependencies]
serde = {version = "1.0", features = ["derive"]}
tokio = { version = "1.7", features = ["full"] }

serde = { version = "1.0", features = ["derive"] }
serde_bencode = "^0.2.3"
serde_bytes = "0.11"
serde_json = "1.0.72"
hex = "0.4.3"
percent-encoding = "2.1.0"
warp = {version = "0.3", features = ["tls"]}
tokio = {version = "1.7", features = ["macros", "io-util", "net", "time", "rt-multi-thread", "fs", "sync", "signal"]}
binascii = "0.1"

warp = { version = "0.3", features = ["tls"] }

config = "0.11"
toml = "0.5"
log = {version = "0.4", features = ["release_max_level_info"]}

log = { version = "0.4", features = ["release_max_level_info"] }
fern = "0.6"
chrono = "0.4"
byteorder = "1"
r2d2_sqlite = "0.16.0"

r2d2 = "0.8.8"
r2d2_mysql = "21.0.0"
r2d2_sqlite = "0.16.0"

rand = "0.8.4"
config = "0.11"
derive_more = "0.99"
thiserror = "1.0"
aquatic_udp_protocol = { git = "https://github.com/greatest-ape/aquatic" }
futures = "0.3.21"
async-trait = "0.1.52"

aquatic_udp_protocol = "0.2.0"
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Torrust Tracker is a lightweight but incredibly powerful and feature-rich BitTor
* [X] Peer authentication using time-bound keys
* [X] newTrackon check supported for both HTTP, UDP, where IPv4 and IPv6 is properly handled
* [X] SQLite3 Persistent loading and saving of the torrent hashes and completed count
* [X] MySQL support added as engine option
* [X] Periodically saving added, interval can be configured

### Implemented BEPs
* [BEP 3](https://www.bittorrent.org/beps/bep_0003.html): The BitTorrent Protocol
Expand Down Expand Up @@ -50,29 +52,26 @@ cargo build --release
```toml
log_level = "info"
mode = "public"
db_driver = "Sqlite3"
db_path = "data.db"
persistence = false
cleanup_interval = 600
cleanup_peerless = true
external_ip = "0.0.0.0"
announce_interval = 120
announce_interval_min = 900
peer_timeout = 900
min_announce_interval = 120
max_peer_timeout = 900
on_reverse_proxy = false
external_ip = "0.0.0.0"
tracker_usage_statistics = true
persistent_torrent_completed_stat = false
inactive_peer_cleanup_interval = 600
remove_peerless_torrents = true

[[udp_trackers]]
enabled = false
bind_address = "0.0.0.0:6969"

[[udp_trackers]]
enabled = true
bind_address = "[::]:6969"

[[http_trackers]]
enabled = true
bind_address = "0.0.0.0:6969"
ssl_enabled = false
ssl_bind_address = "0.0.0.0:6868"
ssl_cert_path = ""
ssl_key_path = ""

Expand Down
1 change: 1 addition & 0 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod server;
91 changes: 73 additions & 18 deletions src/http_api_server.rs → src/api/server.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::tracker::{TorrentTracker};
use serde::{Deserialize, Serialize};
use std::cmp::min;
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;
use std::sync::Arc;
use warp::{filters, reply, reply::Reply, serve, Filter, Server};
use crate::TorrentPeer;
use super::common::*;

use serde::{Deserialize, Serialize};
use warp::{Filter, filters, reply, serve};

use crate::protocol::common::*;
use crate::peer::TorrentPeer;
use crate::tracker::tracker::TorrentTracker;

#[derive(Deserialize, Debug)]
struct TorrentInfoQuery {
Expand All @@ -20,7 +23,7 @@ struct Torrent<'a> {
completed: u32,
leechers: u32,
#[serde(skip_serializing_if = "Option::is_none")]
peers: Option<Vec<TorrentPeer>>,
peers: Option<Vec<&'a TorrentPeer>>,
}

#[derive(Serialize)]
Expand Down Expand Up @@ -52,7 +55,7 @@ enum ActionStatus<'a> {

impl warp::reject::Reject for ActionStatus<'static> {}

fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract = (), Error = warp::reject::Rejection> + Clone {
fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract=(), Error=warp::reject::Rejection> + Clone {
#[derive(Deserialize)]
struct AuthToken {
token: Option<String>,
Expand All @@ -69,7 +72,7 @@ fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract = (), Er
match token.token {
Some(token) => {
if !tokens.contains(&token) {
return Err(warp::reject::custom(ActionStatus::Err { reason: "token not valid".into() }))
return Err(warp::reject::custom(ActionStatus::Err { reason: "token not valid".into() }));
}

Ok(())
Expand All @@ -81,7 +84,7 @@ fn authenticate(tokens: HashMap<String, String>) -> impl Filter<Extract = (), Er
.untuple_one()
}

pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract = impl Reply> + Clone + Send + Sync + 'static> {
pub fn start(socket_addr: SocketAddr, tracker: Arc<TorrentTracker>) -> impl warp::Future<Output = ()> {
// GET /api/torrents?offset=:u32&limit=:u32
// View torrent list
let api_torrents = tracker.clone();
Expand Down Expand Up @@ -131,7 +134,7 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
})
.and_then(|tracker: Arc<TorrentTracker>| {
async move {
let mut results = Stats{
let mut results = Stats {
torrents: 0,
seeders: 0,
completed: 0,
Expand All @@ -147,9 +150,11 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
udp4_scrapes_handled: 0,
udp6_connections_handled: 0,
udp6_announces_handled: 0,
udp6_scrapes_handled: 0
udp6_scrapes_handled: 0,
};

let db = tracker.get_torrents().await;

let _: Vec<_> = db
.iter()
.map(|(_info_hash, torrent_entry)| {
Expand All @@ -160,7 +165,9 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
results.torrents += 1;
})
.collect();

let stats = tracker.get_stats().await;

results.tcp4_connections_handled = stats.tcp4_connections_handled as u32;
results.tcp4_announces_handled = stats.tcp4_announces_handled as u32;
results.tcp4_scrapes_handled = stats.tcp4_scrapes_handled as u32;
Expand Down Expand Up @@ -195,7 +202,7 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
let torrent_entry_option = db.get(&info_hash);

if torrent_entry_option.is_none() {
return Err(warp::reject::custom(ActionStatus::Err { reason: "torrent does not exist".into() }))
return Result::<_, warp::reject::Rejection>::Ok(reply::json(&"torrent not known"))
}

let torrent_entry = torrent_entry_option.unwrap();
Expand Down Expand Up @@ -226,10 +233,10 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
})
.and_then(|(info_hash, tracker): (InfoHash, Arc<TorrentTracker>)| {
async move {
match tracker.remove_torrent_from_whitelist(&info_hash).await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to remove torrent from whitelist".into() }))
}
match tracker.remove_torrent_from_whitelist(&info_hash).await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to remove torrent from whitelist".into() }))
}
}
});

Expand Down Expand Up @@ -286,13 +293,53 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
})
.and_then(|(key, tracker): (String, Arc<TorrentTracker>)| {
async move {
match tracker.remove_auth_key(key).await {
match tracker.remove_auth_key(&key).await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to delete key".into() }))
}
}
});

// GET /api/whitelist/reload
// Reload whitelist
let t7 = tracker.clone();
let reload_whitelist = filters::method::get()
.and(filters::path::path("whitelist"))
.and(filters::path::path("reload"))
.and(filters::path::end())
.map(move || {
let tracker = t7.clone();
tracker
})
.and_then(|tracker: Arc<TorrentTracker>| {
async move {
match tracker.load_whitelist().await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to reload whitelist".into() }))
}
}
});

// GET /api/keys/reload
// Reload whitelist
let t8 = tracker.clone();
let reload_keys = filters::method::get()
.and(filters::path::path("keys"))
.and(filters::path::path("reload"))
.and(filters::path::end())
.map(move || {
let tracker = t8.clone();
tracker
})
.and_then(|tracker: Arc<TorrentTracker>| {
async move {
match tracker.load_keys().await {
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to reload keys".into() }))
}
}
});

let api_routes =
filters::path::path("api")
.and(view_torrent_list
Expand All @@ -302,9 +349,17 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
.or(add_torrent)
.or(create_key)
.or(delete_key)
.or(reload_whitelist)
.or(reload_keys)
);

let server = api_routes.and(authenticate(tracker.config.http_api.access_tokens.clone()));

serve(server)
let (_addr, api_server) = serve(server).bind_with_graceful_shutdown(socket_addr, async move {
tokio::signal::ctrl_c()
.await
.expect("Failed to listen to shutdown signal.");
});

api_server
}
Loading

0 comments on commit 421f1fd

Please sign in to comment.