credence_lib/server/
server.rs

1use super::{
2    super::{configuration::*, middleware::*},
3    routers::*,
4    site::*,
5};
6
7use {
8    ::axum::middleware::*,
9    axum_server::*,
10    kutil::{
11        http::{axum::*, tls::*},
12        std::{future::*, immutable::*, string::*},
13    },
14    std::net::*,
15};
16
17//
18// Server
19//
20
21/// Credence server.
22///
23/// Listens on a single [SocketAddr] with one or more [Router](::axum::Router).
24#[derive(Clone, Debug, Default)]
25pub struct Server {
26    /// Hosts.
27    pub hosts: Vec<ByteString>,
28
29    /// TLS container.
30    pub tls: TlsContainer,
31
32    /// Host router.
33    pub host_router: HostRouter,
34}
35
36impl Server {
37    /// Add the [Site] for all configured hosts.
38    ///
39    /// Also adds TLS keys, if configured, to the [TlsContainer].
40    pub fn add_router(&mut self, site: &Site, tcp_port: u16, port: &Port) -> Result<(), ConfigurationError> {
41        if port.hosts.is_empty() {
42            let host = ByteString::default();
43
44            // Add socket middleware
45            let router = site.router.clone().layer(map_request_with_state(
46                SocketMiddleware::new(Socket::new(tcp_port, false, host.clone())),
47                SocketMiddleware::function,
48            ));
49
50            self.host_router.add(host.clone(), router);
51            self.host_router.fallback_host = Some(host);
52
53            return Ok(());
54        }
55
56        let is_tls = port.is_tls();
57
58        for host in &port.hosts {
59            if self.hosts.contains(&host.name) {
60                return Err(format!("host used more than once for port {}: {}", tcp_port, host.name).into());
61            }
62
63            self.hosts.push(host.name.clone());
64
65            let host_and_optional_port = match tcp_port {
66                80 | 443 => host.name.clone(),
67                _ => format!("{}:{}", host.name, tcp_port).into(),
68            };
69
70            if let Some(to_tcp_port) = host.redirect_to {
71                match site.configuration.ports.get(&to_tcp_port) {
72                    Some(to_port) => {
73                        let mut has_to_host = false;
74                        for to_host in &to_port.hosts {
75                            if to_host.name == host.name {
76                                has_to_host = true;
77                                break;
78                            }
79                        }
80
81                        if !has_to_host {
82                            return Err(format!(
83                                "port {} host {:?} `redirect-to` port {} does not have the host",
84                                tcp_port, host.name, to_tcp_port
85                            )
86                            .into());
87                        }
88
89                        let router = new_redirecting_router(to_port.is_tls(), host.name.clone(), to_tcp_port);
90                        self.host_router.add(host_and_optional_port, router);
91                    }
92
93                    None => {
94                        return Err(format!(
95                            "port {} host {:?} `redirect-to` port is undefined: {}",
96                            tcp_port, host.name, to_tcp_port
97                        )
98                        .into());
99                    }
100                }
101            } else {
102                // Add socket middleware
103                let router = site.router.clone().layer(map_request_with_state(
104                    SocketMiddleware::new(Socket::new(tcp_port, is_tls, host.name.clone())),
105                    SocketMiddleware::function,
106                ));
107
108                self.host_router.add(host_and_optional_port, router);
109            }
110
111            if is_tls {
112                if let Some(key) = &host.key {
113                    let (certificates, private_key) = key.to_bytes()?;
114                    self.tls.add_key_from_pem(host.name.clone(), &certificates, &private_key)?;
115                } else if let Some(acme) = &host.acme {
116                    self.tls.add_resolver_from_acme(acme.provider(host.name.clone()))?;
117                } else {
118                    return Err(format!("listener {:?} has both TLS and non-TLS hosts", port.name).into());
119                }
120            }
121        }
122
123        Ok(())
124    }
125
126    /// Create a server task on a socket.
127    pub fn start(
128        self,
129        socket_address: SocketAddr,
130        server_handle: &Handle,
131    ) -> Result<Option<CapturedIoTask>, ConfigurationError> {
132        let router = match self.host_router.into_router() {
133            Some(router) => router,
134            None => return Ok(None),
135        };
136
137        if self.tls.is_empty() {
138            tracing::info!("starting server: {}{}", socket_address, display_hosts(&self.hosts));
139
140            let server = bind(socket_address).handle(server_handle.clone());
141            let task = server.serve(router.into_make_service());
142            return Ok(Some(Box::pin(task)));
143        } else {
144            tracing::info!("starting server: {} with TLS{}", socket_address, display_hosts(&self.hosts));
145
146            let acceptor = self.tls.axum_acceptor()?;
147            let server = bind(socket_address).handle(server_handle.clone()).acceptor(acceptor);
148            let task = server.serve(router.into_make_service());
149            return Ok(Some(Box::pin(task)));
150        }
151    }
152}
153
154fn display_hosts(hosts: &Vec<ByteString>) -> String {
155    if hosts.is_empty() {
156        "".into()
157    } else {
158        let hosts: Vec<_> = hosts.iter().map(|host| format!("{:?}", host)).collect();
159        String::from(" for ") + &hosts.join_conjunction("and")
160    }
161}