Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor API: replace Warp with Axum #152

Merged
merged 48 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
901bc34
feat: [#143] add axum dependency
josecelano Jan 2, 2023
cbf8837
feat(api): [#143] scaffolding for new API using Axum
josecelano Jan 2, 2023
5ee3f93
refactor(api): [#143] extract mods for API testing
josecelano Jan 2, 2023
6a9e2d5
feat(api): [#143] axum api, GET /stats endpoint
josecelano Jan 2, 2023
7331c82
refactor: [#143] replace unwrap with expect
josecelano Jan 3, 2023
0615c9f
refactor(api): [#143] remove duplicate code
josecelano Jan 3, 2023
0f99f7b
refactor: [#143] remove duplicate or unneeded code
josecelano Jan 3, 2023
1c6db6e
test: [#143] add tests for extracted functions
josecelano Jan 3, 2023
43dbed9
feat(api): [#143] authentication with GET param for Axum API
josecelano Jan 4, 2023
1395945
refactor(api): [#143] remove dummy api endpoint
josecelano Jan 4, 2023
af51f77
feat(api): [#143] add new cargo dependency: axum-server
josecelano Jan 4, 2023
fe4303c
feat(api): [#143] SSL support for the new Axum API
josecelano Jan 4, 2023
16d438d
feat(api): [#143] axum api, WIP. GET /api/torrent/:info_hash endpoint
josecelano Jan 5, 2023
2aebf9a
test(api): [#143] add test for torrent not known response in GET /api…
josecelano Jan 9, 2023
a649fe8
feat(api): [#143] axum api. GET /api/torrent/:info_hash endpoint. Not…
josecelano Jan 9, 2023
ded4d11
fix: clippy errors
josecelano Jan 9, 2023
a806179
refactor(api): [#143] use extracted service in the Warp handler
josecelano Jan 9, 2023
c36b121
refactor(api): [#143] extract service tracker::services::torrent::get…
josecelano Jan 9, 2023
1515753
feat(api): [#143] axum api. GET /api/torrents endpoint
josecelano Jan 10, 2023
e1ed929
test(api): [#143] add tests for database failure
josecelano Jan 10, 2023
5c5fcbd
feat(api): [#143] axum api. POST /api/whitelist/:info_hash endpoint
josecelano Jan 11, 2023
2ddf268
feat(api): [#143] axum api. DELETE /api/whitelist/:info_hash endpoint
josecelano Jan 11, 2023
a58d831
feat(api): [#143] axum api. GET /api/whitelist/reload endpoint
josecelano Jan 11, 2023
0282e33
feat(api): [#143] axum api. POST /api/key/:seconds_valid endpoint
josecelano Jan 11, 2023
6b2e3bc
feat(api): [#143] axum api. DELETE /api/key/:key endpoint
josecelano Jan 11, 2023
03ba166
feat(api): [#143] axum api. GET /api/keys/reload endpoint
josecelano Jan 11, 2023
5d9dd9d
refactor(api): extract asserts in tests
josecelano Jan 11, 2023
504cb9e
feat(api): the new Axum api uses the URL prefix /api too.
josecelano Jan 11, 2023
c502c1d
refactor(api): [#143] remove duplicate definition of axum router
josecelano Jan 11, 2023
517ffde
fix(api): [#143] fix new Axum API enpoint when URL params are invalid
josecelano Jan 12, 2023
2da0719
test(api): [#143] add tests for authenticaation
josecelano Jan 12, 2023
3bcbbc9
refactor(api): [#143] normalize test names for errrors
josecelano Jan 12, 2023
aa2a2ef
fix(api): [#143] do not fail trying to remove a whitelisted torrent t…
josecelano Jan 12, 2023
39c15c6
test(api): [#143] add test for invalid infohash URL path param
josecelano Jan 12, 2023
2c222ee
test(api): [#143] add test for invalid key duration URL path param
josecelano Jan 13, 2023
072f3d7
test(api): [#143] add more tests for invalid key id URL path param
josecelano Jan 13, 2023
8d32628
refactor(api): [#143] extract and rename functions
josecelano Jan 13, 2023
337e12e
feat(api): [#143] replace Warp API with Axum implementation
josecelano Jan 13, 2023
77ec521
refactor(api): [#143] remove Warp API implementation
josecelano Jan 13, 2023
6dd3c48
refactor(api): [#143] move API resources mod
josecelano Jan 13, 2023
2a92b0a
refactor(api): extract mod for API responses
josecelano Jan 13, 2023
0940463
refactor(api): [#143] extract api mods
josecelano Jan 13, 2023
6ddbdd9
docs(api): [#143] move comment
josecelano Jan 13, 2023
6955666
docs(api): [#143] remove comment
josecelano Jan 13, 2023
02dfe3e
refactor(api): [#143] rename response functions
josecelano Jan 13, 2023
b7c5144
refactor(api): [#143] change fn return type
josecelano Jan 13, 2023
1c72ac0
docs(api): [#143] remove deprecated comment
josecelano Jan 13, 2023
0c3ca87
refactor(api): [#143] remove duplicate code
josecelano Jan 13, 2023
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
125 changes: 125 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ async-trait = "0.1"

aquatic_udp_protocol = "0.2"
uuid = { version = "1", features = ["v4"] }
axum = "0.6.1"

[dev-dependencies]
mockall = "0.11"
Expand Down
2 changes: 2 additions & 0 deletions src/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod routes;
pub mod server;
65 changes: 65 additions & 0 deletions src/apis/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use std::sync::Arc;

use axum::extract::State;
use axum::response::Json;
use serde_json::{json, Value};

use crate::api::resource::stats::Stats;
use crate::tracker::Tracker;

#[allow(clippy::unused_async)]
pub async fn root() -> Json<Value> {
Json(json!({ "data": 42 }))
}

#[allow(clippy::unused_async)]
pub async fn get_stats(State(tracker): State<Arc<Tracker>>) -> Json<Value> {
let mut results = Stats {
torrents: 0,
seeders: 0,
completed: 0,
leechers: 0,
tcp4_connections_handled: 0,
tcp4_announces_handled: 0,
tcp4_scrapes_handled: 0,
tcp6_connections_handled: 0,
tcp6_announces_handled: 0,
tcp6_scrapes_handled: 0,
udp4_connections_handled: 0,
udp4_announces_handled: 0,
udp4_scrapes_handled: 0,
udp6_connections_handled: 0,
udp6_announces_handled: 0,
udp6_scrapes_handled: 0,
};

let db = tracker.get_torrents().await;

db.values().for_each(|torrent_entry| {
let (seeders, completed, leechers) = torrent_entry.get_stats();
results.seeders += seeders;
results.completed += completed;
results.leechers += leechers;
results.torrents += 1;
});

let stats = tracker.get_stats().await;

#[allow(clippy::cast_possible_truncation)]
{
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;
results.tcp6_connections_handled = stats.tcp6_connections_handled as u32;
results.tcp6_announces_handled = stats.tcp6_announces_handled as u32;
results.tcp6_scrapes_handled = stats.tcp6_scrapes_handled as u32;
results.udp4_connections_handled = stats.udp4_connections_handled as u32;
results.udp4_announces_handled = stats.udp4_announces_handled as u32;
results.udp4_scrapes_handled = stats.udp4_scrapes_handled as u32;
results.udp6_connections_handled = stats.udp6_connections_handled as u32;
results.udp6_announces_handled = stats.udp6_announces_handled as u32;
results.udp6_scrapes_handled = stats.udp6_scrapes_handled as u32;
}

Json(json!(results))
}
39 changes: 39 additions & 0 deletions src/apis/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::net::SocketAddr;
use std::sync::Arc;

use axum::routing::get;
use axum::Router;
use futures::Future;
use warp::hyper;

use super::routes::{get_stats, root};
use crate::tracker;

pub fn start(socket_addr: SocketAddr, tracker: &Arc<tracker::Tracker>) -> impl Future<Output = hyper::Result<()>> {
let app = Router::new()
.route("/", get(root))
.route("/stats", get(get_stats).with_state(tracker.clone()));

let server = axum::Server::bind(&socket_addr).serve(app.into_make_service());

server.with_graceful_shutdown(async move {
tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal.");
})
}

pub fn start_tls(
socket_addr: SocketAddr,
_ssl_cert_path: &str,
_ssl_key_path: &str,
_tracker: &Arc<tracker::Tracker>,
) -> impl Future<Output = hyper::Result<()>> {
// todo: for the time being, it's just a copy & paste from start(...).

let app = Router::new().route("/", get(root));

let server = axum::Server::bind(&socket_addr).serve(app.into_make_service());

server.with_graceful_shutdown(async move {
tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal.");
})
}
2 changes: 1 addition & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub struct HttpTracker {
}

#[serde_as]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct HttpApi {
pub enabled: bool,
pub bind_address: String,
Expand Down
1 change: 1 addition & 0 deletions src/jobs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod http_tracker;
pub mod torrent_cleanup;
pub mod tracker_api;
pub mod tracker_apis;
pub mod udp_tracker;
54 changes: 54 additions & 0 deletions src/jobs/tracker_apis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::sync::Arc;

use log::info;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;

use crate::apis::server;
use crate::config::HttpApi;
use crate::tracker;

#[derive(Debug)]
pub struct ApiServerJobStarted();

/// # Panics
///
/// It would panic if unable to send the `ApiServerJobStarted` notice.
pub async fn start_job(config: &HttpApi, tracker: Arc<tracker::Tracker>) -> JoinHandle<()> {
let bind_addr = config
.bind_address
.parse::<std::net::SocketAddr>()
.expect("Tracker API bind_address invalid.");
let ssl_enabled = config.ssl_enabled;
let ssl_cert_path = config.ssl_cert_path.clone();
let ssl_key_path = config.ssl_key_path.clone();

let (tx, rx) = oneshot::channel::<ApiServerJobStarted>();

// Run the API server
let join_handle = tokio::spawn(async move {
if !ssl_enabled {
info!("Starting Torrust APIs server on: http://{}", bind_addr);
let handle = server::start(bind_addr, &tracker);
tx.send(ApiServerJobStarted()).expect("the start job dropped");
if let Ok(()) = handle.await {
info!("Stopping Torrust APIs server on {} ...", bind_addr);
}
} else if ssl_enabled && ssl_cert_path.is_some() && ssl_key_path.is_some() {
info!("Starting Torrust APIs server on: https://{}", bind_addr);
let handle = server::start_tls(bind_addr, &ssl_cert_path.unwrap(), &ssl_key_path.unwrap(), &tracker);
tx.send(ApiServerJobStarted()).expect("the start job dropped");
if let Ok(()) = handle.await {
info!("Stopping Torrust APIs server on {} ...", bind_addr);
}
}
});

// Wait until the APIs server job is running
match rx.await {
Ok(_msg) => info!("Torrust APIs server started"),
Err(e) => panic!("the apis server was dropped: {e}"),
}

join_handle
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod api;
pub mod apis;
pub mod config;
pub mod databases;
pub mod http;
Expand Down
Loading