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<()> {
102 self.bind(port).run().await
103 }
104}
105
106impl BoundApp {
107 pub fn http(mut self, http: Http) -> Self {
111 self.http = http;
112 self
113 }
114
115 pub fn reuseport(mut self, enabled: bool) -> Self {
117 self.app.reuseport = enabled;
118 self
119 }
120
121 pub async fn run(self) -> Result<()> {
123 crate::tracing_init::ensure_tracing();
124 let args: Vec<String> = std::env::args().skip(1).collect();
125 if self.app.run_cli_command(&args).await? {
126 return Ok(());
127 }
128 self.serve_inner().await
129 }
130
131 fn apply_http_mode(mut app: App, http: Http, port: Option<u16>) -> App {
132 match http {
133 Http::All => {
134 if let Some(port) = port {
135 app.alt_svc = Some(format!("h3=\":{port}\"; ma=86400"));
136 }
137 }
138 _ => {
139 app.alt_svc = None;
140 }
141 }
142 app
143 }
144
145 pub fn shutdown<F>(mut self, f: F) -> Self
147 where
148 F: Future<Output = ()> + Send + 'static,
149 {
150 self.shutdown = Some(Box::pin(f));
151 self
152 }
153
154 #[cfg(feature = "tls")]
156 pub fn tls(mut self, config: crate::Tls) -> Result<Self> {
157 if matches!(self.bind, Bind::Uds(_)) {
158 return Err(Error::Internal(
159 "TLS cannot be combined with Bind::Uds".into(),
160 ));
161 }
162 self.tls = Some(config.into_runtime()?);
163 self.app.hsts = self.tls.as_ref().map(|t| t.hsts).unwrap_or(false);
164 Ok(self)
165 }
166
167 pub async fn serve(self) -> Result<()> {
169 crate::tracing_init::ensure_tracing();
170 self.serve_inner().await
171 }
172
173 #[cfg(feature = "tls")]
174 async fn serve_inner(self) -> Result<()> {
175 let http = self.http;
176 match self.bind {
177 Bind::Port(port) => {
178 let app = Self::apply_http_mode(self.app, http, Some(port));
179 server::listen(app, Some(port), None, self.shutdown, self.tls).await
180 }
181 Bind::Addr(addr) => {
182 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
183 server::listen(app, None, Some(addr), self.shutdown, self.tls).await
184 }
185 Bind::Str(s) => {
186 let addr: SocketAddr = s.parse().map_err(|e| {
187 Error::Internal(format!("bind str {s:?}: invalid address: {e}"))
188 })?;
189 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
190 server::listen(app, None, Some(addr), self.shutdown, self.tls).await
191 }
192 Bind::Env { default_port } => {
193 let addr = super::addr_from_env(default_port)?;
194 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
195 server::listen(app, None, Some(addr), self.shutdown, self.tls).await
196 }
197 Bind::Listener(listener) => {
198 let port = listener.local_addr().ok().map(|a| a.port());
199 let app = Self::apply_http_mode(self.app, http, port);
200 server::listen_with_listener(app, listener, self.shutdown, self.tls).await
201 }
202 #[cfg(unix)]
203 Bind::Uds(path) => {
204 let app = Self::apply_http_mode(self.app, http, None);
205 server::listen_uds(app, &path, self.shutdown).await
206 }
207 }
208 }
209
210 #[cfg(not(feature = "tls"))]
211 async fn serve_inner(self) -> Result<()> {
212 let http = self.http;
213 match self.bind {
214 Bind::Port(port) => {
215 let app = Self::apply_http_mode(self.app, http, Some(port));
216 server::listen(app, Some(port), None, self.shutdown).await
217 }
218 Bind::Addr(addr) => {
219 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
220 server::listen(app, None, Some(addr), self.shutdown).await
221 }
222 Bind::Str(s) => {
223 let addr: SocketAddr = s.parse().map_err(|e| {
224 Error::Internal(format!("bind str {s:?}: invalid address: {e}"))
225 })?;
226 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
227 server::listen(app, None, Some(addr), self.shutdown).await
228 }
229 Bind::Env { default_port } => {
230 let addr = super::addr_from_env(default_port)?;
231 let app = Self::apply_http_mode(self.app, http, Some(addr.port()));
232 server::listen(app, None, Some(addr), self.shutdown).await
233 }
234 Bind::Listener(listener) => {
235 let port = listener.local_addr().ok().map(|a| a.port());
236 let app = Self::apply_http_mode(self.app, http, port);
237 server::listen_with_listener(app, listener, self.shutdown).await
238 }
239 #[cfg(unix)]
240 Bind::Uds(path) => {
241 let app = Self::apply_http_mode(self.app, http, None);
242 server::listen_uds(app, &path, self.shutdown).await
243 }
244 }
245 }
246}