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

Crate docs for apis module #265

Merged
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 cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"byteorder",
"canonicalize",
"canonicalized",
"certbot",
"chrono",
"clippy",
"completei",
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@
//! Using `curl` you can create a 2-minute valid auth key:
//!
//! ```text
//! $ curl -X POST http://127.0.0.1:1212/api/v1/key/120?token=MyAccessToken
//! $ curl -X POST "http://127.0.0.1:1212/api/v1/key/120?token=MyAccessToken"
//! ```
//!
//! Response:
Expand All @@ -329,7 +329,7 @@
//! ```
//!
//! You can also use the Torrust Tracker together with the [Torrust Index](https://github.com/torrust/torrust-index). If that's the case,
//! the Index will create the keys by using the API.
//! the Index will create the keys by using the tracker [API](crate::servers::apis).
//!
//! ## UDP tracker usage
//!
Expand Down
166 changes: 166 additions & 0 deletions src/servers/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,174 @@
//! The tracker REST API with all its versions.
//!
//! > **NOTICE**: This API should not be exposed directly to the internet, it is
//! intended for internal use only.
//!
//! Endpoints for the latest API: [v1](crate::servers::apis::v1).
//!
//! All endpoints require an authorization token which must be set in the
//! configuration before running the tracker. The default configuration uses
//! `?token=MyAccessToken`. Refer to [Authentication](#authentication) for more
//! information.
//!
//! # Table of contents
//!
//! - [Configuration](#configuration)
//! - [Authentication](#authentication)
//! - [Versioning](#versioning)
//! - [Endpoints](#endpoints)
//! - [Documentation](#documentation)
//!
//! # Configuration
//!
//! The configuration file has a [`[http_api]`](torrust_tracker_configuration::HttpApi)
//! section that can be used to enable the API.
//!
//! ```toml
//! [http_api]
//! enabled = true
//! bind_address = "0.0.0.0:1212"
//! ssl_enabled = false
//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt"
//! ssl_key_path = "./storage/ssl_certificates/localhost.key"
//!
//! [http_api.access_tokens]
//! admin = "MyAccessToken"
//! ```
//!
//! Refer to [torrust-tracker-configuration](https://docs.rs/torrust-tracker-configuration>)
//! for more information about the API configuration.
//!
//! When you run the tracker with enabled API, you will see the following message:
//!
//! ```text
//! Loading configuration from config file ./config.toml
//! 023-03-28T12:19:24.963054069+01:00 [torrust_tracker::bootstrap::logging][INFO] logging initialized.
//! ...
//! 023-03-28T12:19:24.964138723+01:00 [torrust_tracker::bootstrap::jobs::tracker_apis][INFO] Starting Torrust APIs server on: http://0.0.0.0:1212
//! ```
//!
//! The API server will be available on the address specified in the configuration.
//!
//! You can test the API by loading the following URL on a browser:
//!
//! <http://0.0.0.0:1212/api/v1/stats?token=MyAccessToken>
//!
//! Or using `curl`:
//!
//! ```bash
//! $ curl -s "http://0.0.0.0:1212/api/v1/stats?token=MyAccessToken"
//! ```
//!
//! The response will be a JSON object. For example, the [tracker statistics
//! endpoint](crate::servers::apis::v1::context::stats#get-tracker-statistics):
//!
//! ```json
//! {
//! "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
//! }
//! ```
//!
//! # Authentication
//!
//! The API supports authentication using a GET parameter token.
//!
//! <http://0.0.0.0:1212/api/v1/stats?token=MyAccessToken>
//!
//! You can set as many tokens as you want in the configuration file:
//!
//! ```toml
//! [http_api.access_tokens]
//! admin = "MyAccessToken"
//! ```
//!
//! The token label is used to identify the token. All tokens have full access
//! to the API.
//!
//! Refer to [torrust-tracker-configuration](https://docs.rs/torrust-tracker-configuration>)
//! for more information about the API configuration and to the
//! [`auth`](crate::servers::apis::v1::middlewares::auth) middleware for more
//! information about the authentication process.
//!
//! # Setup SSL (optional)
//!
//! The API server supports SSL. You can enable it by setting the
//! [`ssl_enabled`](torrust_tracker_configuration::HttpApi::ssl_enabled) option
//! to `true` in the configuration file
//! ([`http_api`](torrust_tracker_configuration::HttpApi) section).
//!
//! ```toml
//! [http_api]
//! enabled = true
//! bind_address = "0.0.0.0:1212"
//! ssl_enabled = true
//! ssl_cert_path = "./storage/ssl_certificates/localhost.crt"
//! ssl_key_path = "./storage/ssl_certificates/localhost.key"
//!
//! [http_api.access_tokens]
//! admin = "MyAccessToken"
//! ```
//!
//! > **NOTICE**: If you are using a reverse proxy like NGINX, you can skip this
//! step and use NGINX for the SSL instead. See
//! [other alternatives to Nginx/certbot](https://github.com/torrust/torrust-tracker/discussions/131)
//!
//! > **NOTICE**: You can generate a self-signed certificate for localhost using
//! OpenSSL. See [Let's Encrypt](https://letsencrypt.org/docs/certificates-for-localhost/).
//! That's particularly useful for testing purposes. Once you have the certificate
//! you need to set the [`ssl_cert_path`](torrust_tracker_configuration::HttpApi::ssl_cert_path)
//! and [`ssl_key_path`](torrust_tracker_configuration::HttpApi::ssl_key_path)
//! options in the configuration file with the paths to the certificate
//! (`localhost.crt`) and key (`localhost.key`) files.
//!
//! # Versioning
//!
//! The API is versioned and each version has its own module.
//! The API server runs all the API versions on the same server using
//! the same port. Currently there is only one API version: [v1](crate::servers::apis::v1)
//! but a version [`v2`](https://github.com/torrust/torrust-tracker/issues/144)
//! is planned.
//!
//! # Endpoints
//!
//! Refer to the [v1](crate::servers::apis::v1) module for the list of available
//! API endpoints.
//!
//! # Documentation
//!
//! If you want to contribute to this documentation you can [open a new pull request](https://github.com/torrust/torrust-tracker/pulls).
//!
//! > **NOTICE**: we are using [curl](https://curl.se/) in the API examples.
//! And you have to use quotes around the URL in order to avoid unexpected
//! errors. For example: `curl "http://127.0.0.1:1212/api/v1/stats?token=MyAccessToken"`.
pub mod routes;
pub mod server;
pub mod v1;

use serde::Deserialize;

/// The info hash URL path parameter.
///
/// Some API endpoints require an info hash as a path parameter.
///
/// For example: `http://localhost:1212/api/v1/torrent/{info_hash}`.
///
/// The info hash represents teh value collected from the URL path parameter.
/// It does not include validation as this is done by the API endpoint handler,
/// in order to provide a more specific error message.
#[derive(Deserialize)]
pub struct InfoHashParam(pub String);
14 changes: 12 additions & 2 deletions src/servers/apis/routes.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
//! API routes.
//!
//! It loads all the API routes for all API versions and adds the authentication
//! middleware to them.
//!
//! All the API routes have the `/api` prefix and the version number as the
//! first path segment. For example: `/api/v1/torrents`.
use std::sync::Arc;

use axum::{middleware, Router};

use super::v1;
use super::v1::middlewares::auth::auth;
use crate::tracker::Tracker;

/// Add all API routes to the router.
#[allow(clippy::needless_pass_by_value)]
pub fn router(tracker: Arc<Tracker>) -> Router {
let router = Router::new();
Expand All @@ -14,5 +21,8 @@ pub fn router(tracker: Arc<Tracker>) -> Router {

let router = v1::routes::add(prefix, router, tracker.clone());

router.layer(middleware::from_fn_with_state(tracker.config.clone(), auth))
router.layer(middleware::from_fn_with_state(
tracker.config.clone(),
v1::middlewares::auth::auth,
))
}
50 changes: 50 additions & 0 deletions src/servers/apis/server.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
//! Logic to run the HTTP API server.
//!
//! It contains two main structs: `ApiServer` and `Launcher`,
//! and two main functions: `start` and `start_tls`.
//!
//! The `ApiServer` struct is responsible for:
//! - Starting and stopping the server.
//! - Storing the configuration.
//!
//! `ApiServer` relies on a launcher to start the actual server.
///
/// 1. `ApiServer::start` -> spawns new asynchronous task.
/// 2. `Launcher::start` -> starts the server on the spawned task.
///
/// The `Launcher` struct is responsible for:
///
/// - Knowing how to start the server with graceful shutdown.
///
/// For the time being the `ApiServer` and `Launcher` are only used in tests
/// where we need to start and stop the server multiple times. In production
/// code and the main application uses the `start` and `start_tls` functions
/// to start the servers directly since we do not need to control the server
/// when it's running. In the future we might need to control the server,
/// for example, to restart it to apply new configuration changes, to remotely
/// shutdown the server, etc.
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
Expand All @@ -12,24 +37,35 @@ use super::routes::router;
use crate::servers::signals::shutdown_signal;
use crate::tracker::Tracker;

/// Errors that can occur when starting or stopping the API server.
#[derive(Debug)]
pub enum Error {
Error(String),
}

/// An alias for the `ApiServer` struct with the `Stopped` state.
#[allow(clippy::module_name_repetitions)]
pub type StoppedApiServer = ApiServer<Stopped>;

/// An alias for the `ApiServer` struct with the `Running` state.
#[allow(clippy::module_name_repetitions)]
pub type RunningApiServer = ApiServer<Running>;

/// A struct responsible for starting and stopping an API server with a
/// specific configuration and keeping track of the started server.
///
/// It's a state machine that can be in one of two
/// states: `Stopped` or `Running`.
#[allow(clippy::module_name_repetitions)]
pub struct ApiServer<S> {
pub cfg: torrust_tracker_configuration::HttpApi,
pub state: S,
}

/// The `Stopped` state of the `ApiServer` struct.
pub struct Stopped;

/// The `Running` state of the `ApiServer` struct.
pub struct Running {
pub bind_addr: SocketAddr,
task_killer: tokio::sync::oneshot::Sender<u8>,
Expand All @@ -42,6 +78,8 @@ impl ApiServer<Stopped> {
Self { cfg, state: Stopped {} }
}

/// Starts the API server with the given configuration.
///
/// # Errors
///
/// It would return an error if no `SocketAddr` is returned after launching the server.
Expand Down Expand Up @@ -75,6 +113,8 @@ impl ApiServer<Stopped> {
}

impl ApiServer<Running> {
/// Stops the API server.
///
/// # Errors
///
/// It would return an error if the channel for the task killer signal was closed.
Expand All @@ -93,9 +133,15 @@ impl ApiServer<Running> {
}
}

/// A struct responsible for starting the API server.
struct Launcher;

impl Launcher {
/// Starts the API server with graceful shutdown.
///
/// If TLS is enabled in the configuration, it will start the server with
/// TLS. See [torrust-tracker-configuration](https://docs.rs/torrust-tracker-configuration>)
/// for more information about configuration.
pub fn start<F>(
cfg: &torrust_tracker_configuration::HttpApi,
tracker: Arc<Tracker>,
Expand Down Expand Up @@ -126,6 +172,7 @@ impl Launcher {
}
}

/// Starts the API server with graceful shutdown.
pub fn start_with_graceful_shutdown<F>(
tcp_listener: std::net::TcpListener,
tracker: Arc<Tracker>,
Expand All @@ -146,6 +193,7 @@ impl Launcher {
})
}

/// Starts the API server with graceful shutdown and TLS.
pub fn start_tls_with_graceful_shutdown<F>(
tcp_listener: std::net::TcpListener,
(ssl_cert_path, ssl_key_path): (String, String),
Expand Down Expand Up @@ -180,6 +228,7 @@ impl Launcher {
}
}

/// Starts the API server with graceful shutdown on the current thread.
pub fn start(socket_addr: SocketAddr, tracker: Arc<Tracker>) -> impl Future<Output = hyper::Result<()>> {
let app = router(tracker);

Expand All @@ -191,6 +240,7 @@ pub fn start(socket_addr: SocketAddr, tracker: Arc<Tracker>) -> impl Future<Outp
})
}

/// Starts the API server with graceful shutdown and TLS on the current thread.
pub fn start_tls(
socket_addr: SocketAddr,
ssl_config: RustlsConfig,
Expand Down
Loading