Skip to content

Commit 421f1fd

Browse files
authored
Merge pull request #44 from torrust/development
release: 2.3.0
2 parents 851ad77 + df87a9d commit 421f1fd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2962
-1544
lines changed

Cargo.lock

Lines changed: 1267 additions & 345 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
11
[package]
2+
edition = "2021"
23
name = "torrust-tracker"
3-
version = "2.2.0"
4+
version = "2.3.0"
45
license = "AGPL-3.0"
56
authors = ["Mick van Dijke <[email protected]>"]
67
description = "A feature rich BitTorrent tracker."
7-
edition = "2018"
8+
repository = "https://github.com/torrust/torrust-tracker"
9+
10+
[profile.dev]
11+
debug = 1
12+
opt-level = 1
13+
lto = "thin"
814

915
[profile.release]
16+
debug = 1
17+
opt-level = 3
1018
lto = "fat"
19+
strip = true
1120

1221
[dependencies]
13-
serde = {version = "1.0", features = ["derive"]}
22+
tokio = { version = "1.7", features = ["full"] }
23+
24+
serde = { version = "1.0", features = ["derive"] }
1425
serde_bencode = "^0.2.3"
15-
serde_bytes = "0.11"
1626
serde_json = "1.0.72"
1727
hex = "0.4.3"
1828
percent-encoding = "2.1.0"
19-
warp = {version = "0.3", features = ["tls"]}
20-
tokio = {version = "1.7", features = ["macros", "io-util", "net", "time", "rt-multi-thread", "fs", "sync", "signal"]}
2129
binascii = "0.1"
30+
31+
warp = { version = "0.3", features = ["tls"] }
32+
33+
config = "0.11"
2234
toml = "0.5"
23-
log = {version = "0.4", features = ["release_max_level_info"]}
35+
36+
log = { version = "0.4", features = ["release_max_level_info"] }
2437
fern = "0.6"
2538
chrono = "0.4"
26-
byteorder = "1"
27-
r2d2_sqlite = "0.16.0"
39+
2840
r2d2 = "0.8.8"
41+
r2d2_mysql = "21.0.0"
42+
r2d2_sqlite = "0.16.0"
43+
2944
rand = "0.8.4"
30-
config = "0.11"
3145
derive_more = "0.99"
3246
thiserror = "1.0"
33-
aquatic_udp_protocol = { git = "https://github.com/greatest-ape/aquatic" }
3447
futures = "0.3.21"
48+
async-trait = "0.1.52"
49+
50+
aquatic_udp_protocol = "0.2.0"

README.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Torrust Tracker is a lightweight but incredibly powerful and feature-rich BitTor
1414
* [X] Peer authentication using time-bound keys
1515
* [X] newTrackon check supported for both HTTP, UDP, where IPv4 and IPv6 is properly handled
1616
* [X] SQLite3 Persistent loading and saving of the torrent hashes and completed count
17+
* [X] MySQL support added as engine option
18+
* [X] Periodically saving added, interval can be configured
1719

1820
### Implemented BEPs
1921
* [BEP 3](https://www.bittorrent.org/beps/bep_0003.html): The BitTorrent Protocol
@@ -50,29 +52,26 @@ cargo build --release
5052
```toml
5153
log_level = "info"
5254
mode = "public"
55+
db_driver = "Sqlite3"
5356
db_path = "data.db"
54-
persistence = false
55-
cleanup_interval = 600
56-
cleanup_peerless = true
57-
external_ip = "0.0.0.0"
5857
announce_interval = 120
59-
announce_interval_min = 900
60-
peer_timeout = 900
58+
min_announce_interval = 120
59+
max_peer_timeout = 900
6160
on_reverse_proxy = false
61+
external_ip = "0.0.0.0"
62+
tracker_usage_statistics = true
63+
persistent_torrent_completed_stat = false
64+
inactive_peer_cleanup_interval = 600
65+
remove_peerless_torrents = true
6266

6367
[[udp_trackers]]
6468
enabled = false
6569
bind_address = "0.0.0.0:6969"
6670

67-
[[udp_trackers]]
68-
enabled = true
69-
bind_address = "[::]:6969"
70-
7171
[[http_trackers]]
7272
enabled = true
7373
bind_address = "0.0.0.0:6969"
7474
ssl_enabled = false
75-
ssl_bind_address = "0.0.0.0:6868"
7675
ssl_cert_path = ""
7776
ssl_key_path = ""
7877

src/api/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod server;

src/http_api_server.rs renamed to src/api/server.rs

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
use crate::tracker::{TorrentTracker};
2-
use serde::{Deserialize, Serialize};
31
use std::cmp::min;
42
use std::collections::{HashMap, HashSet};
3+
use std::net::SocketAddr;
54
use std::sync::Arc;
6-
use warp::{filters, reply, reply::Reply, serve, Filter, Server};
7-
use crate::TorrentPeer;
8-
use super::common::*;
5+
6+
use serde::{Deserialize, Serialize};
7+
use warp::{Filter, filters, reply, serve};
8+
9+
use crate::protocol::common::*;
10+
use crate::peer::TorrentPeer;
11+
use crate::tracker::tracker::TorrentTracker;
912

1013
#[derive(Deserialize, Debug)]
1114
struct TorrentInfoQuery {
@@ -20,7 +23,7 @@ struct Torrent<'a> {
2023
completed: u32,
2124
leechers: u32,
2225
#[serde(skip_serializing_if = "Option::is_none")]
23-
peers: Option<Vec<TorrentPeer>>,
26+
peers: Option<Vec<&'a TorrentPeer>>,
2427
}
2528

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

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

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

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

84-
pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract = impl Reply> + Clone + Send + Sync + 'static> {
87+
pub fn start(socket_addr: SocketAddr, tracker: Arc<TorrentTracker>) -> impl warp::Future<Output = ()> {
8588
// GET /api/torrents?offset=:u32&limit=:u32
8689
// View torrent list
8790
let api_torrents = tracker.clone();
@@ -131,7 +134,7 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
131134
})
132135
.and_then(|tracker: Arc<TorrentTracker>| {
133136
async move {
134-
let mut results = Stats{
137+
let mut results = Stats {
135138
torrents: 0,
136139
seeders: 0,
137140
completed: 0,
@@ -147,9 +150,11 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
147150
udp4_scrapes_handled: 0,
148151
udp6_connections_handled: 0,
149152
udp6_announces_handled: 0,
150-
udp6_scrapes_handled: 0
153+
udp6_scrapes_handled: 0,
151154
};
155+
152156
let db = tracker.get_torrents().await;
157+
153158
let _: Vec<_> = db
154159
.iter()
155160
.map(|(_info_hash, torrent_entry)| {
@@ -160,7 +165,9 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
160165
results.torrents += 1;
161166
})
162167
.collect();
168+
163169
let stats = tracker.get_stats().await;
170+
164171
results.tcp4_connections_handled = stats.tcp4_connections_handled as u32;
165172
results.tcp4_announces_handled = stats.tcp4_announces_handled as u32;
166173
results.tcp4_scrapes_handled = stats.tcp4_scrapes_handled as u32;
@@ -195,7 +202,7 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
195202
let torrent_entry_option = db.get(&info_hash);
196203

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

201208
let torrent_entry = torrent_entry_option.unwrap();
@@ -226,10 +233,10 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
226233
})
227234
.and_then(|(info_hash, tracker): (InfoHash, Arc<TorrentTracker>)| {
228235
async move {
229-
match tracker.remove_torrent_from_whitelist(&info_hash).await {
230-
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
231-
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to remove torrent from whitelist".into() }))
232-
}
236+
match tracker.remove_torrent_from_whitelist(&info_hash).await {
237+
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
238+
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to remove torrent from whitelist".into() }))
239+
}
233240
}
234241
});
235242

@@ -286,13 +293,53 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
286293
})
287294
.and_then(|(key, tracker): (String, Arc<TorrentTracker>)| {
288295
async move {
289-
match tracker.remove_auth_key(key).await {
296+
match tracker.remove_auth_key(&key).await {
290297
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
291298
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to delete key".into() }))
292299
}
293300
}
294301
});
295302

303+
// GET /api/whitelist/reload
304+
// Reload whitelist
305+
let t7 = tracker.clone();
306+
let reload_whitelist = filters::method::get()
307+
.and(filters::path::path("whitelist"))
308+
.and(filters::path::path("reload"))
309+
.and(filters::path::end())
310+
.map(move || {
311+
let tracker = t7.clone();
312+
tracker
313+
})
314+
.and_then(|tracker: Arc<TorrentTracker>| {
315+
async move {
316+
match tracker.load_whitelist().await {
317+
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
318+
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to reload whitelist".into() }))
319+
}
320+
}
321+
});
322+
323+
// GET /api/keys/reload
324+
// Reload whitelist
325+
let t8 = tracker.clone();
326+
let reload_keys = filters::method::get()
327+
.and(filters::path::path("keys"))
328+
.and(filters::path::path("reload"))
329+
.and(filters::path::end())
330+
.map(move || {
331+
let tracker = t8.clone();
332+
tracker
333+
})
334+
.and_then(|tracker: Arc<TorrentTracker>| {
335+
async move {
336+
match tracker.load_keys().await {
337+
Ok(_) => Ok(warp::reply::json(&ActionStatus::Ok)),
338+
Err(_) => Err(warp::reject::custom(ActionStatus::Err { reason: "failed to reload keys".into() }))
339+
}
340+
}
341+
});
342+
296343
let api_routes =
297344
filters::path::path("api")
298345
.and(view_torrent_list
@@ -302,9 +349,17 @@ pub fn build_server(tracker: Arc<TorrentTracker>) -> Server<impl Filter<Extract
302349
.or(add_torrent)
303350
.or(create_key)
304351
.or(delete_key)
352+
.or(reload_whitelist)
353+
.or(reload_keys)
305354
);
306355

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

309-
serve(server)
358+
let (_addr, api_server) = serve(server).bind_with_graceful_shutdown(socket_addr, async move {
359+
tokio::signal::ctrl_c()
360+
.await
361+
.expect("Failed to listen to shutdown signal.");
362+
});
363+
364+
api_server
310365
}

0 commit comments

Comments
 (0)