objectstore_server/web/
server.rs1use 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
12const TCP_LISTEN_BACKLOG: u32 = 1024;
14
15const SHUTDOWN_JOIN_TIMEOUT: Duration = Duration::from_secs(5);
17
18pub 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}