objectstore_server/web/
server.rs

1use std::net::SocketAddr;
2
3use anyhow::{Context, Result};
4use tokio::net::{TcpListener, TcpSocket};
5use tokio::signal::unix::SignalKind;
6
7use crate::config::Config;
8use crate::state::Services;
9use crate::web::app::App;
10
11/// The maximum backlog for TCP listen sockets before refusing connections.
12const TCP_LISTEN_BACKLOG: u32 = 1024;
13
14/// Runs the objectstore HTTP server.
15///
16/// This function initializes the server, binds to the configured address, and runs until
17/// termination is requested.
18pub async fn server(config: Config) -> Result<()> {
19    tracing::info!("Starting server");
20    merni::counter!("server.start": 1);
21
22    let listener = listen(&config).context("failed to start TCP listener")?;
23    let state = Services::spawn(config).await?;
24
25    let server_handle = tokio::spawn(async move {
26        App::new(state)
27            .graceful_shutdown(true)
28            .serve(listener)
29            .await
30    });
31
32    tokio::spawn(async move {
33        elegant_departure::get_shutdown_guard().wait().await;
34        tracing::info!("Shutting down ...");
35    });
36
37    elegant_departure::tokio::depart()
38        .on_termination()
39        .on_sigint()
40        .on_signal(SignalKind::hangup())
41        .on_signal(SignalKind::quit())
42        .await;
43
44    let server_result = server_handle.await.map_err(From::from).flatten();
45    tracing::info!("Shutdown complete");
46    server_result
47}
48
49fn listen(config: &Config) -> Result<TcpListener> {
50    let addr = config.http_addr;
51    let socket = match addr {
52        SocketAddr::V4(_) => TcpSocket::new_v4(),
53        SocketAddr::V6(_) => TcpSocket::new_v6(),
54    }?;
55
56    #[cfg(all(unix, not(target_os = "solaris"), not(target_os = "illumos")))]
57    socket.set_reuseport(true)?;
58    socket.bind(addr)?;
59
60    let listener = socket.listen(TCP_LISTEN_BACKLOG)?;
61    tracing::info!("HTTP server listening on {addr}");
62
63    Ok(listener)
64}