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