1
use std::marker::PhantomData;
2
#[cfg(any(feature = "websockets", feature = "acme"))]
3
use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
4
#[cfg(feature = "acme")]
5
use std::time::Duration;
6

            
7
use clap::Args;
8

            
9
use crate::{Backend, BackendError, BonsaiListenConfig, CustomServer, TcpService};
10

            
11
/// Execute the server
12
#[derive(Args, Debug)]
13
pub 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

            
40
impl<B: Backend> Serve<B> {
41
    /// Starts the server.
42
1
    pub async fn execute(&self, server: &CustomServer<B>) -> Result<(), BackendError<B::Error>> {
43
2
        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
1
    pub async fn execute_with<S: TcpService>(
52
1
        &self,
53
1
        server: &CustomServer<B>,
54
1
        service: S,
55
1
    ) -> Result<(), BackendError<B::Error>> {
56
1
        // Try to initialize a logger, but ignore it if it fails. This API is
57
1
        // public and another logger may already be installed.
58
1
        drop(env_logger::try_init());
59
1
        let mut config = BonsaiListenConfig::default();
60
1
        if let Some(address) = self.listen_on {
61
1
            config.address = address;
62
1
        }
63
1
        config.reuse_address = self.reuse_address.unwrap_or(false);
64
1

            
65
1
        #[cfg(any(feature = "websockets", feature = "acme"))]
66
1
        {
67
1
            let listen_address = self.http_port.unwrap_or_else(|| {
68
1
                SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 80, 0, 0))
69
1
            });
70
1
            let task_server = server.clone();
71
1
            let task_service = service.clone();
72
1
            tokio::task::spawn(async move {
73
1
                task_server
74
1
                    .listen_for_tcp_on(listen_address, task_service)
75
                    .await
76
1
            });
77
1

            
78
1
            let listen_address = self.https_port.unwrap_or_else(|| {
79
1
                SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 443, 0, 0))
80
1
            });
81
1
            let task_server = server.clone();
82
1
            tokio::task::spawn(async move {
83
1
                task_server
84
1
                    .listen_for_secure_tcp_on(listen_address, service)
85
3
                    .await
86
1
            });
87
1

            
88
1
            #[cfg(feature = "acme")]
89
2
            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
1
            }
96
        }
97

            
98
1
        let task_server = server.clone();
99
7
        tokio::task::spawn(async move { task_server.listen_on(config).await });
100
1

            
101
1
        server.listen_for_shutdown().await?;
102

            
103
        Ok(())
104
    }
105
}