Skip to content

Commit 849633d

Browse files
committed
feat(http): [#160] scaffolding for HTTP tracker using Axum
We are going to migrate the HTTP tracker from Warp to Axum. This is the basic scaffolding for Axum. Tests have been duplicated to test the new Axum implementation. The setup allows executing both versions: the Warp version on production and both versions (Warp and Axum) on testing env.
1 parent 3970ef9 commit 849633d

File tree

12 files changed

+1507
-67
lines changed

12 files changed

+1507
-67
lines changed

src/http/axum/handlers.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use axum::response::Json;
2+
3+
use super::resources::ok::Ok;
4+
use super::responses::ok_response;
5+
6+
#[allow(clippy::unused_async)]
7+
pub async fn get_status_handler() -> Json<Ok> {
8+
ok_response()
9+
}

src/http/axum/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod handlers;
2+
pub mod resources;
3+
pub mod responses;
4+
pub mod routes;
5+
pub mod server;

src/http/axum/resources/mod.rs

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

src/http/axum/resources/ok.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
4+
pub struct Ok {}

src/http/axum/responses.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Resource responses
2+
3+
use axum::Json;
4+
5+
use super::resources::ok::Ok;
6+
7+
#[must_use]
8+
pub fn ok_response() -> Json<Ok> {
9+
Json(Ok {})
10+
}

src/http/axum/routes.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::sync::Arc;
2+
3+
use axum::routing::get;
4+
use axum::Router;
5+
6+
use super::handlers::get_status_handler;
7+
use crate::tracker::Tracker;
8+
9+
pub fn router(_tracker: &Arc<Tracker>) -> Router {
10+
Router::new()
11+
// Status
12+
.route("/status", get(get_status_handler))
13+
}

src/http/axum/server.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use std::net::SocketAddr;
2+
use std::sync::Arc;
3+
4+
use axum_server::tls_rustls::RustlsConfig;
5+
use axum_server::Handle;
6+
use futures::Future;
7+
use log::info;
8+
use warp::hyper;
9+
10+
use super::routes::router;
11+
use crate::tracker::Tracker;
12+
13+
pub fn start(socket_addr: SocketAddr, tracker: &Arc<Tracker>) -> impl Future<Output = hyper::Result<()>> {
14+
let app = router(tracker);
15+
16+
let server = axum::Server::bind(&socket_addr).serve(app.into_make_service());
17+
18+
server.with_graceful_shutdown(async move {
19+
tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal.");
20+
info!("Stopping Torrust HTTP tracker server on http://{} ...", socket_addr);
21+
})
22+
}
23+
24+
pub fn start_tls(
25+
socket_addr: SocketAddr,
26+
ssl_config: RustlsConfig,
27+
tracker: &Arc<Tracker>,
28+
) -> impl Future<Output = Result<(), std::io::Error>> {
29+
let app = router(tracker);
30+
31+
let handle = Handle::new();
32+
let shutdown_handle = handle.clone();
33+
34+
tokio::spawn(async move {
35+
tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal.");
36+
info!("Stopping Torrust HTTP tracker server on https://{} ...", socket_addr);
37+
shutdown_handle.shutdown();
38+
});
39+
40+
axum_server::bind_rustls(socket_addr, ssl_config)
41+
.handle(handle)
42+
.serve(app.into_make_service())
43+
}

src/http/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
//! - <https://wiki.theory.org/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol>
1010
//! - <https://wiki.theory.org/BitTorrent_Tracker_Protocol>
1111
//!
12+
13+
use serde::{Deserialize, Serialize};
14+
pub mod axum;
1215
pub mod error;
1316
pub mod filters;
1417
pub mod handlers;
@@ -19,3 +22,9 @@ pub mod server;
1922

2023
pub type Bytes = u64;
2124
pub type WebResult<T> = std::result::Result<T, warp::Rejection>;
25+
26+
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
27+
pub enum Version {
28+
Warp,
29+
Axum,
30+
}

src/jobs/http_tracker.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
use std::net::SocketAddr;
22
use std::sync::Arc;
33

4+
use axum_server::tls_rustls::RustlsConfig;
45
use log::{info, warn};
56
use tokio::sync::oneshot;
67
use tokio::task::JoinHandle;
78

89
use crate::config::HttpTracker;
10+
use crate::http::axum::server;
911
use crate::http::server::Http;
12+
use crate::http::Version;
1013
use crate::tracker;
1114

1215
#[derive(Debug)]
1316
pub struct ServerJobStarted();
1417

18+
pub async fn start_job(config: &HttpTracker, tracker: Arc<tracker::Tracker>, version: Version) -> JoinHandle<()> {
19+
match version {
20+
Version::Warp => start_warp(config, tracker.clone()).await,
21+
Version::Axum => start_axum(config, tracker.clone()).await,
22+
}
23+
}
24+
1525
/// # Panics
1626
///
17-
/// It would panic if the `config::HttpTracker` struct would contain an inappropriate values.
18-
pub async fn start_job(config: &HttpTracker, tracker: Arc<tracker::Tracker>) -> JoinHandle<()> {
27+
/// It would panic if the `config::HttpTracker` struct would contain inappropriate values.
28+
async fn start_warp(config: &HttpTracker, tracker: Arc<tracker::Tracker>) -> JoinHandle<()> {
1929
let bind_addr = config
2030
.bind_address
2131
.parse::<SocketAddr>()
@@ -68,3 +78,57 @@ pub async fn start_job(config: &HttpTracker, tracker: Arc<tracker::Tracker>) ->
6878

6979
join_handle
7080
}
81+
82+
/// # Panics
83+
///
84+
/// It would panic if the `config::HttpTracker` struct would contain inappropriate values.
85+
async fn start_axum(config: &HttpTracker, tracker: Arc<tracker::Tracker>) -> JoinHandle<()> {
86+
let bind_addr = config
87+
.bind_address
88+
.parse::<std::net::SocketAddr>()
89+
.expect("Tracker API bind_address invalid.");
90+
let ssl_enabled = config.ssl_enabled;
91+
let ssl_cert_path = config.ssl_cert_path.clone();
92+
let ssl_key_path = config.ssl_key_path.clone();
93+
94+
let (tx, rx) = oneshot::channel::<ServerJobStarted>();
95+
96+
// Run the API server
97+
let join_handle = tokio::spawn(async move {
98+
if !ssl_enabled {
99+
info!("Starting Torrust HTTP tracker server on: http://{}", bind_addr);
100+
101+
let handle = server::start(bind_addr, &tracker);
102+
103+
tx.send(ServerJobStarted())
104+
.expect("the HTTP tracker server should not be dropped");
105+
106+
if let Ok(()) = handle.await {
107+
info!("Torrust HTTP tracker server on http://{} stopped", bind_addr);
108+
}
109+
} else if ssl_enabled && ssl_cert_path.is_some() && ssl_key_path.is_some() {
110+
info!("Starting Torrust HTTP tracker server on: https://{}", bind_addr);
111+
112+
let ssl_config = RustlsConfig::from_pem_file(ssl_cert_path.unwrap(), ssl_key_path.unwrap())
113+
.await
114+
.unwrap();
115+
116+
let handle = server::start_tls(bind_addr, ssl_config, &tracker);
117+
118+
tx.send(ServerJobStarted())
119+
.expect("the HTTP tracker server should not be dropped");
120+
121+
if let Ok(()) = handle.await {
122+
info!("Torrust HTTP tracker server on https://{} stopped", bind_addr);
123+
}
124+
}
125+
});
126+
127+
// Wait until the HTTP tracker server job is running
128+
match rx.await {
129+
Ok(_msg) => info!("Torrust HTTP tracker server started"),
130+
Err(e) => panic!("the HTTP tracker server was dropped: {e}"),
131+
}
132+
133+
join_handle
134+
}

src/setup.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use log::warn;
44
use tokio::task::JoinHandle;
55

66
use crate::config::Configuration;
7+
use crate::http::Version;
78
use crate::jobs::{http_tracker, torrent_cleanup, tracker_apis, udp_tracker};
89
use crate::tracker;
910

@@ -47,7 +48,7 @@ pub async fn setup(config: &Configuration, tracker: Arc<tracker::Tracker>) -> Ve
4748
if !http_tracker_config.enabled {
4849
continue;
4950
}
50-
jobs.push(http_tracker::start_job(http_tracker_config, tracker.clone()).await);
51+
jobs.push(http_tracker::start_job(http_tracker_config, tracker.clone(), Version::Warp).await);
5152
}
5253

5354
// Start HTTP API

0 commit comments

Comments
 (0)