objectstore_server/web/
server.rs

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