bonsaidb_server/cli/
serve.rs

1use std::marker::PhantomData;
2#[cfg(any(feature = "websockets", feature = "acme"))]
3use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
4#[cfg(feature = "acme")]
5use std::time::Duration;
6
7use clap::Args;
8
9use crate::{Backend, BackendError, BonsaiListenConfig, CustomServer, TcpService};
10
11/// Execute the server
12#[derive(Args, Debug)]
13pub struct Serve<B: Backend> {
14    /// The socket address to listen for the BonsaiDb protocol. Defaults to a
15    /// localhost IP address for UDP port 5645 (not an [officially registered
16    /// port](https://github.com/khonsulabs/bonsaidb/issues/48)).
17    #[clap(short = 'l', long = "listen-on")]
18    pub listen_on: Option<SocketAddr>,
19
20    /// If this option is specified, the `SO_REUSEADDR` flag will be set on the
21    /// underlying socket. See [`BonsaiListenConfig::reuse_address`] for more
22    /// information.
23    #[clap(long = "reuse-address")]
24    pub reuse_address: Option<bool>,
25
26    #[cfg(any(feature = "websockets", feature = "acme"))]
27    /// The bind port and address for HTTP traffic. Defaults to TCP port 80.
28    #[clap(long = "http")]
29    pub http_port: Option<SocketAddr>,
30
31    #[cfg(any(feature = "websockets", feature = "acme"))]
32    /// The bind port and address for HTTPS traffic. Defaults to TCP port 443.
33    #[clap(long = "https")]
34    pub https_port: Option<SocketAddr>,
35
36    #[clap(skip)]
37    _backend: PhantomData<B>,
38}
39
40impl<B: Backend> Serve<B> {
41    /// Starts the server.
42    pub async fn execute(&self, server: &CustomServer<B>) -> Result<(), BackendError<B::Error>> {
43        self.execute_with(server, ()).await
44    }
45
46    /// Starts the server using `service` for websocket connections, if enabled.
47    #[cfg_attr(
48        not(any(feature = "websockets", feature = "acme")),
49        allow(unused_variables)
50    )]
51    pub async fn execute_with<S: TcpService>(
52        &self,
53        server: &CustomServer<B>,
54        service: S,
55    ) -> Result<(), BackendError<B::Error>> {
56        // Try to initialize a logger, but ignore it if it fails. This API is
57        // public and another logger may already be installed.
58        drop(env_logger::try_init());
59        let mut config = BonsaiListenConfig::default();
60        if let Some(address) = self.listen_on {
61            config.address = address;
62        }
63        config.reuse_address = self.reuse_address.unwrap_or(false);
64
65        #[cfg(any(feature = "websockets", feature = "acme"))]
66        {
67            let listen_address = self.http_port.unwrap_or_else(|| {
68                SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 80, 0, 0))
69            });
70            let task_server = server.clone();
71            let task_service = service.clone();
72            tokio::task::spawn(async move {
73                task_server
74                    .listen_for_tcp_on(listen_address, task_service)
75                    .await
76            });
77
78            let listen_address = self.https_port.unwrap_or_else(|| {
79                SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 443, 0, 0))
80            });
81            let task_server = server.clone();
82            tokio::task::spawn(async move {
83                task_server
84                    .listen_for_secure_tcp_on(listen_address, service)
85                    .await
86            });
87
88            #[cfg(feature = "acme")]
89            if server.certificate_chain().await.is_err() {
90                log::warn!("Server has no certificate chain. Because acme is enabled, waiting for certificate to be acquired.");
91                while server.certificate_chain().await.is_err() {
92                    tokio::time::sleep(Duration::from_secs(1)).await;
93                }
94                log::info!("Server certificate acquired. Listening for certificate");
95            }
96        }
97
98        let task_server = server.clone();
99        tokio::task::spawn(async move { task_server.listen_on(config).await });
100
101        server.listen_for_shutdown().await?;
102
103        Ok(())
104    }
105}