1use crate::error::{Error, Result};
4use crate::server;
5use crate::App;
6use std::future::Future;
7use std::net::SocketAddr;
8use std::path::PathBuf;
9
10pub enum Bind {
12 Port(u16),
13 Addr(SocketAddr),
14 Str(String),
15 Env {
16 default_port: u16,
17 },
18 Listener(std::net::TcpListener),
19 #[cfg(unix)]
20 Uds(PathBuf),
21}
22
23impl From<u16> for Bind {
24 fn from(port: u16) -> Self {
25 Self::Port(port)
26 }
27}
28
29impl From<SocketAddr> for Bind {
30 fn from(addr: SocketAddr) -> Self {
31 Self::Addr(addr)
32 }
33}
34
35impl From<&str> for Bind {
36 fn from(s: &str) -> Self {
37 Self::Str(s.to_string())
38 }
39}
40
41impl From<String> for Bind {
42 fn from(s: String) -> Self {
43 Self::Str(s)
44 }
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51pub enum Http {
52 H1,
53 H1H2,
54 All,
55}
56
57impl Http {
58 pub const fn h1() -> Self {
59 Self::H1
60 }
61 pub const fn h1_h2() -> Self {
62 Self::H1H2
63 }
64 pub const fn all() -> Self {
65 Self::All
66 }
67}
68
69impl Bind {
70 pub fn str(s: impl Into<String>) -> Self {
71 Self::Str(s.into())
72 }
73}
74
75pub struct BoundApp {
77 app: App,
78 bind: Bind,
79 http: Http,
80 shutdown: Option<server::ExternalShutdown>,
81 #[cfg(feature = "tls")]
82 tls: Option<crate::tls::TlsRuntime>,
83}
84
85impl App {
86 pub fn bind(self, target: impl Into<Bind>) -> BoundApp {
88 BoundApp {
89 app: self,
90 bind: target.into(),
91 http: Http::h1_h2(),
92 shutdown: None,
93 #[cfg(feature = "tls")]
94 tls: None,
95 }
96 }
97
98 pub async fn listen(self, port: u16) -> Result<()> {
103 self.bind(port).run().await
104 }
105
106 #[cfg(feature = "tls")]
111 pub fn use_tls(&mut self, tls: crate::Tls) -> &mut Self {
112 self.tls = Some(tls);
113 self
114 }
115}
116
117impl BoundApp {
118 pub fn http(mut self, http: Http) -> Self {
122 self.http = http;
123 self
124 }
125
126 pub fn reuseport(mut self, enabled: bool) -> Self {
128 self.app.reuseport = enabled;
129 self
130 }
131
132 pub async fn run(self) -> Result<()> {
134 crate::tracing_init::ensure_tracing();
135 let args: Vec<String> = std::env::args().skip(1).collect();
136 if self.app.run_cli_command(&args).await? {
137 return Ok(());
138 }
139 self.serve_inner().await
140 }
141
142 fn apply_http_mode(mut app: App, http: Http, port: Option<u16>) -> App {
143 match http {
144 Http::All => {
145 if let Some(port) = port {
146 app.alt_svc = Some(format!("h3=\":{port}\"; ma=86400"));
147 }
148 }
149 _ => {
150 app.alt_svc = None;
151 }
152 }
153 app
154 }
155
156 pub fn shutdown<F>(mut self, f: F) -> Self
158 where
159 F: Future<Output = ()> + Send + 'static,
160 {
161 self.shutdown = Some(Box::pin(f));
162 self
163 }
164
165 #[cfg(feature = "tls")]
169 pub fn tls(mut self, config: crate::Tls) -> Result<Self> {
170 if matches!(self.bind, Bind::Uds(_)) {
171 return Err(Error::Internal(
172 "TLS cannot be combined with Bind::Uds".into(),
173 ));
174 }
175 self.app.tls = None;
176 self.tls = Some(config.into_runtime()?);
177 self.app.hsts = self.tls.as_ref().map(|t| t.hsts).unwrap_or(false);
178 Ok(self)
179 }
180
181 pub async fn serve(self) -> Result<()> {
183 crate::tracing_init::ensure_tracing();
184 self.serve_inner().await
185 }
186
187 #[cfg(feature = "tls")]
188 fn take_tls_runtime(&mut self) -> Result<Option<crate::tls::TlsRuntime>> {
189 if self.tls.is_some() {
190 return Ok(self.tls.take());
191 }
192 let Some(config) = self.app.tls.take() else {
193 return Ok(None);
194 };
195 if matches!(self.bind, Bind::Uds(_)) {
196 return Err(Error::Internal(
197 "TLS cannot be combined with Bind::Uds".into(),
198 ));
199 }
200 let runtime = config.into_runtime()?;
201 self.app.hsts = runtime.hsts;
202 Ok(Some(runtime))
203 }
204
205 #[cfg(feature = "tls")]
206 async fn serve_inner(mut self) -> Result<()> {
207 let tls = self.take_tls_runtime()?;
208 let http = self.http;
209 match self.bind {
210 Bind::Port(port) => {
211 let app = Self::apply_http_mode(self.app, http, Some(port));
212 server::listen(app, Some(port), None, self.shutdown, tls).await
213 }
214 Bind::Addr(addr) => {
215 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
216 server::listen(app, None, Some(addr), self.shutdown, tls).await
217 }
218 Bind::Str(s) => {
219 let addr: SocketAddr = s.parse().map_err(|e| {
220 Error::Internal(format!("bind str {s:?}: invalid address: {e}"))
221 })?;
222 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
223 server::listen(app, None, Some(addr), self.shutdown, tls).await
224 }
225 Bind::Env { default_port } => {
226 let addr = super::addr_from_env(default_port)?;
227 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
228 server::listen(app, None, Some(addr), self.shutdown, tls).await
229 }
230 Bind::Listener(listener) => {
231 let port = listener.local_addr().ok().map(|a| a.port());
232 let app = Self::apply_http_mode(self.app, http, port);
233 server::listen_with_listener(app, listener, self.shutdown, tls).await
234 }
235 #[cfg(unix)]
236 Bind::Uds(path) => {
237 let app = Self::apply_http_mode(self.app, http, None);
238 server::listen_uds(app, &path, self.shutdown).await
239 }
240 }
241 }
242
243 #[cfg(not(feature = "tls"))]
244 async fn serve_inner(self) -> Result<()> {
245 let http = self.http;
246 match self.bind {
247 Bind::Port(port) => {
248 let app = Self::apply_http_mode(self.app, http, Some(port));
249 server::listen(app, Some(port), None, self.shutdown).await
250 }
251 Bind::Addr(addr) => {
252 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
253 server::listen(app, None, Some(addr), self.shutdown).await
254 }
255 Bind::Str(s) => {
256 let addr: SocketAddr = s.parse().map_err(|e| {
257 Error::Internal(format!("bind str {s:?}: invalid address: {e}"))
258 })?;
259 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
260 server::listen(app, None, Some(addr), self.shutdown).await
261 }
262 Bind::Env { default_port } => {
263 let addr = super::addr_from_env(default_port)?;
264 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
265 server::listen(app, None, Some(addr), self.shutdown).await
266 }
267 Bind::Listener(listener) => {
268 let port = listener.local_addr().ok().map(|a| a.port());
269 let app = Self::apply_http_mode(self.app, http, port);
270 server::listen_with_listener(app, listener, self.shutdown).await
271 }
272 #[cfg(unix)]
273 Bind::Uds(path) => {
274 let app = Self::apply_http_mode(self.app, http, None);
275 server::listen_uds(app, &path, self.shutdown).await
276 }
277 }
278 }
279}