-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(api): [#174] Axum API scaffolding
Basic changes needed to run the Axum API implementation in parallel with the current one with ActixWeb. For the time being, we will be only useinf the Auxm implementation for testing until all endpoints are migrated.
- Loading branch information
1 parent
7bcf20e
commit d08f70e
Showing
20 changed files
with
398 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,21 @@ | ||
use torrust_index_backend::app; | ||
use torrust_index_backend::bootstrap::config::init_configuration; | ||
use torrust_index_backend::web::api::Implementation; | ||
|
||
#[actix_web::main] | ||
#[tokio::main] | ||
async fn main() -> Result<(), std::io::Error> { | ||
let configuration = init_configuration().await; | ||
|
||
let app = app::run(configuration).await; | ||
// todo: we are migrating from actix-web to axum, so we need to keep both | ||
// implementations for a while. For production we only use ActixWeb. | ||
// Once the Axum implementation is finished and stable, we can switch to it | ||
// and remove the ActixWeb implementation. | ||
let api_implementation = Implementation::ActixWeb; | ||
|
||
app.api_server.await | ||
let app = app::run(configuration, &api_implementation).await; | ||
|
||
match api_implementation { | ||
Implementation::ActixWeb => app.actix_web_api_server.unwrap().await.expect("the API server was dropped"), | ||
Implementation::Axum => app.axum_api_server.unwrap().await.expect("the Axum API server was dropped"), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use std::net::SocketAddr; | ||
use std::sync::Arc; | ||
|
||
use actix_cors::Cors; | ||
use actix_web::{middleware, web, App, HttpServer}; | ||
use log::info; | ||
use tokio::sync::oneshot::{self, Sender}; | ||
|
||
use super::Running; | ||
use crate::common::AppData; | ||
use crate::routes; | ||
use crate::web::api::ServerStartedMessage; | ||
|
||
/// Starts the API server with `ActixWeb`. | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if the API server can't be started. | ||
pub async fn start(app_data: Arc<AppData>, net_ip: &str, net_port: u16) -> Running { | ||
let config_socket_addr: SocketAddr = format!("{net_ip}:{net_port}") | ||
.parse() | ||
.expect("API server socket address to be valid."); | ||
|
||
let (tx, rx) = oneshot::channel::<ServerStartedMessage>(); | ||
|
||
// Run the API server | ||
let join_handle = tokio::spawn(async move { | ||
info!("Starting API server with net config: {} ...", config_socket_addr); | ||
|
||
let server_future = start_server(config_socket_addr, app_data.clone(), tx); | ||
|
||
let _ = server_future.await; | ||
|
||
Ok(()) | ||
}); | ||
|
||
// Wait until the API server is running | ||
let bound_addr = match rx.await { | ||
Ok(msg) => msg.socket_addr, | ||
Err(e) => panic!("API server start. The API server was dropped: {e}"), | ||
}; | ||
|
||
info!("API server started"); | ||
|
||
Running { | ||
socket_addr: bound_addr, | ||
actix_web_api_server: Some(join_handle), | ||
axum_api_server: None, | ||
} | ||
} | ||
|
||
fn start_server( | ||
config_socket_addr: SocketAddr, | ||
app_data: Arc<AppData>, | ||
tx: Sender<ServerStartedMessage>, | ||
) -> actix_web::dev::Server { | ||
let server = HttpServer::new(move || { | ||
App::new() | ||
.wrap(Cors::permissive()) | ||
.app_data(web::Data::new(app_data.clone())) | ||
.wrap(middleware::Logger::default()) | ||
.configure(routes::init) | ||
}) | ||
.bind(config_socket_addr) | ||
.expect("can't bind server to socket address"); | ||
|
||
let bound_addr = server.addrs()[0]; | ||
|
||
info!("API server listening on http://{}", bound_addr); | ||
|
||
tx.send(ServerStartedMessage { socket_addr: bound_addr }) | ||
.expect("the API server should not be dropped"); | ||
|
||
server.run() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
use std::net::SocketAddr; | ||
use std::sync::Arc; | ||
|
||
use futures::Future; | ||
use log::info; | ||
use tokio::sync::oneshot::{self, Sender}; | ||
|
||
use super::v1::routes::router; | ||
use super::{Running, ServerStartedMessage}; | ||
use crate::common::AppData; | ||
|
||
/// Starts the API server with `Axum`. | ||
/// | ||
/// # Panics | ||
/// | ||
/// Panics if the API server can't be started. | ||
pub async fn start(app_data: Arc<AppData>, net_ip: &str, net_port: u16) -> Running { | ||
let config_socket_addr: SocketAddr = format!("{net_ip}:{net_port}") | ||
.parse() | ||
.expect("API server socket address to be valid."); | ||
|
||
let (tx, rx) = oneshot::channel::<ServerStartedMessage>(); | ||
|
||
// Run the API server | ||
let join_handle = tokio::spawn(async move { | ||
info!("Starting API server with net config: {} ...", config_socket_addr); | ||
|
||
let handle = start_server(config_socket_addr, app_data.clone(), tx); | ||
|
||
if let Ok(()) = handle.await { | ||
info!("API server stopped"); | ||
} | ||
|
||
Ok(()) | ||
}); | ||
|
||
// Wait until the API server is running | ||
let bound_addr = match rx.await { | ||
Ok(msg) => msg.socket_addr, | ||
Err(e) => panic!("API server start. The API server was dropped: {e}"), | ||
}; | ||
|
||
Running { | ||
socket_addr: bound_addr, | ||
actix_web_api_server: None, | ||
axum_api_server: Some(join_handle), | ||
} | ||
} | ||
|
||
fn start_server( | ||
config_socket_addr: SocketAddr, | ||
app_data: Arc<AppData>, | ||
tx: Sender<ServerStartedMessage>, | ||
) -> impl Future<Output = hyper::Result<()>> { | ||
let tcp_listener = std::net::TcpListener::bind(config_socket_addr).expect("tcp listener to bind to a socket address"); | ||
|
||
let bound_addr = tcp_listener | ||
.local_addr() | ||
.expect("tcp listener to be bound to a socket address."); | ||
|
||
info!("API server listening on http://{}", bound_addr); | ||
|
||
let app = router(app_data); | ||
|
||
let server = axum::Server::from_tcp(tcp_listener) | ||
.expect("a new server from the previously created tcp listener.") | ||
.serve(app.into_make_service_with_connect_info::<SocketAddr>()); | ||
|
||
tx.send(ServerStartedMessage { socket_addr: bound_addr }) | ||
.expect("the API server should not be dropped"); | ||
|
||
server.with_graceful_shutdown(async move { | ||
tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal."); | ||
info!("Stopping API server on http://{} ...", bound_addr); | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//! API handlers for the the [`about`](crate::web::api::v1::contexts::about) API | ||
//! context. | ||
use std::sync::Arc; | ||
|
||
use axum::extract::State; | ||
use axum::http::{header, StatusCode}; | ||
use axum::response::{IntoResponse, Response}; | ||
|
||
use crate::common::AppData; | ||
use crate::services::about; | ||
|
||
#[allow(clippy::unused_async)] | ||
pub async fn about_page_handler(State(_app_data): State<Arc<AppData>>) -> Response { | ||
( | ||
StatusCode::OK, | ||
[(header::CONTENT_TYPE, "text/html; charset=utf-8")], | ||
about::page(), | ||
) | ||
.into_response() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,3 +84,5 @@ | |
//! </footer> | ||
//! </html> | ||
//! ``` | ||
pub mod handlers; | ||
pub mod routes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
//! API routes for the [`about`](crate::web::api::v1::contexts::about) API context. | ||
//! | ||
//! Refer to the [API endpoint documentation](crate::web::api::v1::contexts::about). | ||
use std::sync::Arc; | ||
|
||
use axum::routing::get; | ||
use axum::Router; | ||
|
||
use super::handlers::about_page_handler; | ||
use crate::common::AppData; | ||
|
||
/// It adds the routes to the router for the [`about`](crate::web::api::v1::contexts::about) API context. | ||
pub fn add(prefix: &str, router: Router, app_data: Arc<AppData>) -> Router { | ||
router.route(&format!("{prefix}/about"), get(about_page_handler).with_state(app_data)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ | |
//! information. | ||
pub mod auth; | ||
pub mod contexts; | ||
pub mod routes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
//! Route initialization for the v1 API. | ||
use std::sync::Arc; | ||
|
||
use axum::Router; | ||
|
||
use super::contexts::about; | ||
use crate::common::AppData; | ||
|
||
/// Add all API routes to the router. | ||
#[allow(clippy::needless_pass_by_value)] | ||
pub fn router(app_data: Arc<AppData>) -> Router { | ||
let router = Router::new(); | ||
|
||
add(router, app_data) | ||
} | ||
|
||
/// Add the routes for the v1 API. | ||
fn add(router: Router, app_data: Arc<AppData>) -> Router { | ||
let v1_prefix = "/v1".to_string(); | ||
|
||
about::routes::add(&v1_prefix, router, app_data) | ||
} |
Oops, something went wrong.