torrust_index_backend/web/api/
server.rs

1use std::net::SocketAddr;
2use std::sync::Arc;
3
4use futures::Future;
5use log::info;
6use tokio::sync::oneshot::{self, Sender};
7
8use super::v1::routes::router;
9use super::{Running, ServerStartedMessage};
10use crate::common::AppData;
11
12/// Starts the API server.
13///
14/// # Panics
15///
16/// Panics if the API server can't be started.
17pub async fn start(app_data: Arc<AppData>, net_ip: &str, net_port: u16) -> Running {
18    let config_socket_addr: SocketAddr = format!("{net_ip}:{net_port}")
19        .parse()
20        .expect("API server socket address to be valid.");
21
22    let (tx, rx) = oneshot::channel::<ServerStartedMessage>();
23
24    // Run the API server
25    let join_handle = tokio::spawn(async move {
26        info!("Starting API server with net config: {} ...", config_socket_addr);
27
28        let handle = start_server(config_socket_addr, app_data.clone(), tx);
29
30        if let Ok(()) = handle.await {
31            info!("API server stopped");
32        }
33
34        Ok(())
35    });
36
37    // Wait until the API server is running
38    let bound_addr = match rx.await {
39        Ok(msg) => msg.socket_addr,
40        Err(e) => panic!("API server start. The API server was dropped: {e}"),
41    };
42
43    Running {
44        socket_addr: bound_addr,
45        api_server: Some(join_handle),
46    }
47}
48
49fn start_server(
50    config_socket_addr: SocketAddr,
51    app_data: Arc<AppData>,
52    tx: Sender<ServerStartedMessage>,
53) -> impl Future<Output = hyper::Result<()>> {
54    let tcp_listener = std::net::TcpListener::bind(config_socket_addr).expect("tcp listener to bind to a socket address");
55
56    let bound_addr = tcp_listener
57        .local_addr()
58        .expect("tcp listener to be bound to a socket address.");
59
60    info!("API server listening on http://{}", bound_addr);
61
62    let app = router(app_data);
63
64    let server = axum::Server::from_tcp(tcp_listener)
65        .expect("a new server from the previously created tcp listener.")
66        .serve(app.into_make_service_with_connect_info::<SocketAddr>());
67
68    tx.send(ServerStartedMessage { socket_addr: bound_addr })
69        .expect("the API server should not be dropped");
70
71    server.with_graceful_shutdown(async move {
72        tokio::signal::ctrl_c().await.expect("Failed to listen to shutdown signal.");
73        info!("Stopping API server on http://{} ...", bound_addr);
74    })
75}