torrust_index/web/api/server/
mod.rs1pub mod custom_axum;
2pub mod signals;
3pub mod v1;
4
5use std::net::SocketAddr;
6use std::panic::Location;
7use std::sync::Arc;
8
9use axum_server::tls_rustls::RustlsConfig;
10use axum_server::Handle;
11use thiserror::Error;
12use tokio::sync::oneshot::{Receiver, Sender};
13use torrust_index_located_error::LocatedError;
14use tracing::{error, info};
15use v1::routes::router;
16
17use self::signals::{Halted, Started};
18use super::Running;
19use crate::common::AppData;
20use crate::config::Tsl;
21use crate::web::api::server::custom_axum::TimeoutAcceptor;
22use crate::web::api::server::signals::graceful_shutdown;
23
24pub type DynError = Arc<dyn std::error::Error + Send + Sync>;
25
26pub async fn start(app_data: Arc<AppData>, config_bind_address: SocketAddr, opt_tsl: Option<Tsl>) -> Running {
32 let opt_rust_tls_config = make_rust_tls(&opt_tsl)
33 .await
34 .map(|tls| tls.expect("it should have a valid net tls configuration"));
35
36 let (tx_start, rx) = tokio::sync::oneshot::channel::<Started>();
37 let (tx_halt, rx_halt) = tokio::sync::oneshot::channel::<Halted>();
38
39 let join_handle = tokio::spawn(async move {
41 info!("Starting API server with net config: {} ...", config_bind_address);
42
43 start_server(config_bind_address, app_data.clone(), tx_start, rx_halt, opt_rust_tls_config).await;
44
45 info!("API server stopped");
46
47 Ok(())
48 });
49
50 let bound_addr = match rx.await {
52 Ok(started) => started.socket_addr,
53 Err(err) => {
54 let msg = format!("Unable to start API server: {err}");
55 error!("{}", msg);
56 panic!("{}", msg);
57 }
58 };
59
60 Running {
61 socket_addr: bound_addr,
62 halt_task: tx_halt,
63 task: join_handle,
64 }
65}
66
67async fn start_server(
68 config_socket_addr: SocketAddr,
69 app_data: Arc<AppData>,
70 tx_start: Sender<Started>,
71 rx_halt: Receiver<Halted>,
72 rust_tls_config: Option<RustlsConfig>,
73) {
74 let router = router(app_data);
75 let socket = std::net::TcpListener::bind(config_socket_addr).expect("Could not bind tcp_listener to address.");
76 let address = socket.local_addr().expect("Could not get local_addr from tcp_listener.");
77
78 let handle = Handle::new();
79
80 tokio::task::spawn(graceful_shutdown(
81 handle.clone(),
82 rx_halt,
83 format!("Shutting down API server on socket address: {address}"),
84 ));
85
86 let tls = rust_tls_config.clone();
87 let protocol = if tls.is_some() { "https" } else { "http" };
88
89 info!("API server listening on {}://{}", protocol, address); tx_start
92 .send(Started { socket_addr: address })
93 .expect("the API server should not be dropped");
94
95 match tls {
96 Some(tls) => custom_axum::from_tcp_rustls_with_timeouts(socket, tls)
97 .handle(handle)
98 .serve(router.into_make_service_with_connect_info::<std::net::SocketAddr>())
102 .await
103 .expect("API server should be running"),
104 None => custom_axum::from_tcp_with_timeouts(socket)
105 .handle(handle)
106 .acceptor(TimeoutAcceptor)
107 .serve(router.into_make_service_with_connect_info::<std::net::SocketAddr>())
108 .await
109 .expect("API server should be running"),
110 };
111}
112
113#[derive(Error, Debug)]
114pub enum Error {
115 #[error("tls config missing")]
117 MissingTlsConfig { location: &'static Location<'static> },
118
119 #[error("bad tls config: {source}")]
121 BadTlsConfig {
122 source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
123 ssl_cert_path: String,
124 ssl_key_path: String,
125 },
126}
127
128pub async fn make_rust_tls(tsl_config: &Option<Tsl>) -> Option<Result<RustlsConfig, Error>> {
129 if let Some(tsl) = tsl_config {
130 if let (Some(cert), Some(key)) = (tsl.ssl_cert_path.clone(), tsl.ssl_key_path.clone()) {
131 info!("Using https. Cert path: {cert}.");
132 info!("Using https. Key path: {key}.");
133
134 let ssl_cert_path = cert.clone().to_string();
135 let ssl_key_path = key.clone().to_string();
136
137 Some(
138 RustlsConfig::from_pem_file(cert, key)
139 .await
140 .map_err(|err| Error::BadTlsConfig {
141 source: (Arc::new(err) as DynError).into(),
142 ssl_cert_path,
143 ssl_key_path,
144 }),
145 )
146 } else {
147 Some(Err(Error::MissingTlsConfig {
148 location: Location::caller(),
149 }))
150 }
151 } else {
152 info!("TLS not enabled");
153 None
154 }
155}