1use crate::error::{Error, Result};
4use crate::handler::{BoxFuture, DynHandler, Handler, HandlerWithParams};
5use crate::middleware::{Layered, Middleware, Next};
6use crate::openapi::{self, OpenApiInfo, Operation, RouteKind};
7use crate::request::{Params, Request, StateMap};
8use crate::response::Response;
9use crate::router::Router;
10use crate::shutdown::{Context, Shutdown};
11use http_body_util::BodyExt;
12use hyper::server::conn::http1;
13use hyper::service::service_fn;
14use hyper_util::rt::TokioIo;
15use std::collections::HashMap;
16use std::convert::Infallible;
17use std::net::SocketAddr;
18use std::sync::Arc;
19use std::time::Duration;
20use tokio::net::TcpListener;
21
22pub struct App {
24 router: Router,
25 middlewares: Vec<Arc<dyn Middleware>>,
26 max_body_size: Option<usize>, docs: HashMap<(String, String), Operation>,
28 openapi: Option<OpenApiInfo>,
29 state: StateMap,
30 background: Vec<BackgroundTask>,
31 shutdown_grace: Duration,
32 body_limits: Vec<(String, Option<usize>)>,
33 #[cfg(feature = "tls")]
34 tls: Option<crate::tls::TlsConfig>,
35 #[cfg(feature = "tls")]
36 tls_reload: Option<Duration>,
37}
38
39struct ConnInfo {
41 peer: SocketAddr,
42 secure: bool,
43 client_cert: Option<Vec<u8>>,
44}
45
46type BackgroundTask = Box<dyn FnOnce(Context) -> BoxFuture<'static, ()> + Send>;
47
48struct Shared {
50 router: Router,
51 middlewares: Vec<Arc<dyn Middleware>>,
52 max_body_size: Option<usize>,
53 body_limits: Vec<(String, Option<usize>)>,
54 state: Arc<StateMap>,
55 shutdown: Shutdown,
56}
57
58pub struct RouteGroup {
60 prefix: String,
61 routes: Vec<(String, String, DynHandler)>, middlewares: Vec<Arc<dyn Middleware>>,
63 docs: Vec<(String, String, Operation)>, body_limit: Option<Option<usize>>,
65 ws_paths: Vec<String>,
67}
68
69impl App {
70 pub fn new() -> Self {
71 Self {
72 router: Router::new(),
73 middlewares: Vec::new(),
74 max_body_size: Some(10 * 1024 * 1024), docs: HashMap::new(),
76 openapi: None,
77 state: StateMap::default(),
78 background: Vec::new(),
79 shutdown_grace: Duration::from_secs(10),
80 body_limits: Vec::new(),
81 #[cfg(feature = "tls")]
82 tls: None,
83 #[cfg(feature = "tls")]
84 tls_reload: None,
85 }
86 }
87
88 pub fn max_body_size(mut self, size: Option<usize>) -> Self {
90 self.max_body_size = size;
91 self
92 }
93
94 pub fn body_limit(mut self, prefix: &str, limit: Option<usize>) -> Self {
101 self.body_limits
102 .push((prefix.trim_end_matches('/').to_string(), limit));
103 self
104 }
105
106 pub fn state<T: Send + Sync + 'static>(mut self, value: T) -> Self {
128 self.state.insert(value);
129 self
130 }
131
132 pub fn background<F, Fut>(mut self, task: F) -> Self
153 where
154 F: FnOnce(Context) -> Fut + Send + 'static,
155 Fut: std::future::Future<Output = ()> + Send + 'static,
156 {
157 self.background
158 .push(Box::new(move |ctx| Box::pin(task(ctx))));
159 self
160 }
161
162 #[cfg(feature = "tls")]
164 pub fn tls(mut self, config: crate::tls::TlsConfig) -> Self {
165 self.tls = Some(config);
166 self
167 }
168
169 #[cfg(feature = "tls")]
174 pub fn tls_reload_every(mut self, interval: Duration) -> Self {
175 self.tls_reload = Some(interval);
176 self
177 }
178
179 pub fn shutdown_grace(mut self, grace: Duration) -> Self {
182 self.shutdown_grace = grace;
183 self
184 }
185
186 pub fn group(mut self, prefix: &str, configure: impl FnOnce(RouteGroup) -> RouteGroup) -> Self {
188 let group = RouteGroup::new(prefix);
189 let group = configure(group);
190
191 if let Some(limit) = group.body_limit {
192 self.body_limits.push((group.prefix.clone(), limit));
193 }
194 for (method, path, op) in group.docs {
195 let full_path = format!("{}{}", group.prefix, path);
196 self.docs.insert((method, full_path), op);
197 }
198
199 let middlewares = group.middlewares;
201 for (method, path, handler) in group.routes {
202 let full_path = format!("{}{}", group.prefix, path);
203 let handler: DynHandler = if middlewares.is_empty() {
204 handler
205 } else {
206 Arc::new(Layered::new(middlewares.clone(), handler))
207 };
208 let kind = if method == "GET" && group.ws_paths.contains(&path) {
209 RouteKind::WebSocket
210 } else {
211 RouteKind::Http
212 };
213 if let Err(e) = self.router.insert_as(kind, &method, &full_path, handler) {
214 panic!("Failed to register {} route {}: {}", method, full_path, e);
215 }
216 }
217
218 self
219 }
220
221 pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
225 self.docs
226 .insert((method.to_ascii_uppercase(), path.to_string()), operation);
227 self
228 }
229
230 pub fn openapi(mut self, info: OpenApiInfo) -> Self {
234 self.openapi = Some(info);
235 self
236 }
237
238 pub fn get<H>(mut self, path: &str, handler: H) -> Self
240 where
241 H: Handler,
242 {
243 if let Err(e) = self
244 .router
245 .insert("GET", path, Arc::new(handler) as DynHandler)
246 {
247 panic!("Failed to register GET route {}: {}", path, e);
248 }
249 self
250 }
251
252 pub fn get_with_params<F, Fut>(mut self, path: &str, handler: F) -> Self
254 where
255 F: Fn(Params) -> Fut + Send + Sync + 'static,
256 Fut: std::future::Future<Output = Result<Response>> + Send + 'static,
257 {
258 if let Err(e) = self.router.insert(
259 "GET",
260 path,
261 Arc::new(HandlerWithParams::new(handler)) as DynHandler,
262 ) {
263 panic!("Failed to register GET route {}: {}", path, e);
264 }
265 self
266 }
267
268 pub fn post<H>(mut self, path: &str, handler: H) -> Self
270 where
271 H: Handler,
272 {
273 if let Err(e) = self
274 .router
275 .insert("POST", path, Arc::new(handler) as DynHandler)
276 {
277 panic!("Failed to register POST route {}: {}", path, e);
278 }
279 self
280 }
281
282 pub fn put<H>(mut self, path: &str, handler: H) -> Self
284 where
285 H: Handler,
286 {
287 if let Err(e) = self
288 .router
289 .insert("PUT", path, Arc::new(handler) as DynHandler)
290 {
291 panic!("Failed to register PUT route {}: {}", path, e);
292 }
293 self
294 }
295
296 pub fn delete<H>(mut self, path: &str, handler: H) -> Self
298 where
299 H: Handler,
300 {
301 if let Err(e) = self
302 .router
303 .insert("DELETE", path, Arc::new(handler) as DynHandler)
304 {
305 panic!("Failed to register DELETE route {}: {}", path, e);
306 }
307 self
308 }
309
310 pub fn patch<H>(mut self, path: &str, handler: H) -> Self
312 where
313 H: Handler,
314 {
315 if let Err(e) = self
316 .router
317 .insert("PATCH", path, Arc::new(handler) as DynHandler)
318 {
319 panic!("Failed to register PATCH route {}: {}", path, e);
320 }
321 self
322 }
323
324 #[cfg(feature = "websocket")]
330 pub fn ws<F, Fut>(self, path: &str, handler: F) -> Self
331 where
332 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
333 Fut: std::future::Future<Output = ()> + Send + 'static,
334 {
335 self.ws_with(path, crate::websocket::WsOptions::default(), handler)
336 }
337
338 #[cfg(feature = "websocket")]
340 pub fn ws_with<F, Fut>(
341 mut self,
342 path: &str,
343 options: crate::websocket::WsOptions,
344 handler: F,
345 ) -> Self
346 where
347 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
348 Fut: std::future::Future<Output = ()> + Send + 'static,
349 {
350 let route = crate::websocket::WsRoute::new(handler, options);
351 if let Err(e) = self.router.insert_as(
352 RouteKind::WebSocket,
353 "GET",
354 path,
355 Arc::new(route) as DynHandler,
356 ) {
357 panic!("Failed to register WebSocket route {}: {}", path, e);
358 }
359 self
360 }
361
362 pub fn middleware<M>(mut self, middleware: M) -> Self
364 where
365 M: Middleware,
366 {
367 self.middlewares.push(Arc::new(middleware));
368 self
369 }
370
371 pub fn serve_static(mut self, route_prefix: &str, dir_path: &str) -> Self {
373 use crate::static_files::StaticFileHandler;
374 let handler = StaticFileHandler::new(dir_path);
375 let prefix = route_prefix.trim_end_matches('/');
377 let handler: DynHandler = Arc::new(handler);
378 let route = format!("{}/{{*filepath}}", prefix);
379 if let Err(e) = self
380 .router
381 .insert_as(RouteKind::Hidden, "GET", &route, handler.clone())
382 {
383 panic!("Failed to register static file route {}: {}", route, e);
384 }
385 let root = if prefix.is_empty() { "/" } else { prefix };
388 let _ = self
389 .router
390 .insert_as(RouteKind::Hidden, "GET", root, handler.clone());
391 if !prefix.is_empty() {
392 let _ =
393 self.router
394 .insert_as(RouteKind::Hidden, "GET", &format!("{}/", prefix), handler);
395 }
396 self
397 }
398
399 pub async fn run(self, addr: &str) -> Result<()> {
404 let addr: SocketAddr = addr
405 .parse()
406 .map_err(|e| Error::internal(format!("Invalid address: {}", e)))?;
407
408 let listener = TcpListener::bind(addr)
409 .await
410 .map_err(|e| Error::internal(format!("Failed to bind: {}", e)))?;
411
412 #[cfg(feature = "tls")]
413 let scheme = if self.tls.is_some() { "https" } else { "http" };
414 #[cfg(not(feature = "tls"))]
415 let scheme = "http";
416 println!("š ullun-api listening on {}://{}", scheme, addr);
417
418 let result = self
419 .serve_with_shutdown(listener, async {
420 termination_signal().await;
421 println!("\nš Shutting down gracefully...");
422 })
423 .await;
424 println!("ā
Server stopped");
425 result
426 }
427
428 pub async fn serve(self, listener: TcpListener) -> Result<()> {
435 self.serve_with_shutdown(listener, std::future::pending())
436 .await
437 }
438
439 pub async fn serve_with_shutdown(
446 mut self,
447 listener: TcpListener,
448 signal: impl std::future::Future<Output = ()>,
449 ) -> Result<()> {
450 self = self.finalize();
451 let grace = self.shutdown_grace;
452 let background = std::mem::take(&mut self.background);
453 #[cfg(feature = "tls")]
454 let tls = self.tls.take();
455 #[cfg(feature = "tls")]
456 let tls_reload = self.tls_reload;
457 let (trigger, rx) = tokio::sync::watch::channel(false);
458 let shutdown = Shutdown::new(rx);
459 let shared = Arc::new(self.into_shared(shutdown.clone()));
460
461 #[allow(unused_mut)]
462 let mut tasks: Vec<_> = background
463 .into_iter()
464 .map(|task| {
465 let ctx = Context::new(shared.state.clone(), shutdown.clone());
466 tokio::spawn(task(ctx))
467 })
468 .collect();
469
470 #[cfg(feature = "tls")]
471 if let (Some(config), Some(every)) = (tls.clone(), tls_reload) {
472 let shutdown = shutdown.clone();
473 tasks.push(tokio::spawn(async move {
474 while shutdown.sleep(every).await {
475 match config.reload() {
476 Ok(true) => println!("š TLS certificate reloaded"),
477 Ok(false) => {}
478 Err(e) => eprintln!(
479 "TLS certificate reload failed, keeping the previous one: {}",
480 e.message()
481 ),
482 }
483 }
484 }));
485 }
486
487 let (done_tx, mut done_rx) = tokio::sync::mpsc::channel::<()>(1);
489 tokio::pin!(signal);
490 loop {
491 tokio::select! {
492 _ = &mut signal => break,
493 accepted = listener.accept() => match accepted {
494 Ok((stream, peer)) => {
495 #[cfg(feature = "tls")]
496 if let Some(config) = tls.clone() {
497 let (shared, shutdown, done) = (shared.clone(), shutdown.clone(), done_tx.clone());
498 tokio::spawn(async move {
499 if let Ok((tls_stream, client_cert)) = config.accept(stream).await {
503 let info = ConnInfo { peer, secure: true, client_cert };
504 serve_connection(tls_stream, info, shared, shutdown, done).await;
505 }
506 });
507 continue;
508 }
509 let info = ConnInfo { peer, secure: false, client_cert: None };
510 tokio::spawn(serve_connection(
511 stream,
512 info,
513 shared.clone(),
514 shutdown.clone(),
515 done_tx.clone(),
516 ));
517 }
518 Err(e) => {
519 eprintln!("Failed to accept connection: {}", e);
521 tokio::time::sleep(Duration::from_millis(100)).await;
522 }
523 },
524 }
525 }
526
527 let _ = trigger.send(true);
528 drop(done_tx);
529 drop(listener);
530 let drain = async {
531 let _ = done_rx.recv().await;
532 for task in tasks {
533 let _ = task.await;
534 }
535 };
536 if tokio::time::timeout(grace, drain).await.is_err() {
537 eprintln!(
538 "Shutdown grace period of {:?} elapsed, stopping anyway",
539 grace
540 );
541 }
542 Ok(())
543 }
544
545 fn into_shared(self, shutdown: Shutdown) -> Shared {
546 Shared {
547 router: self.router,
548 middlewares: self.middlewares,
549 max_body_size: self.max_body_size,
550 body_limits: self.body_limits,
551 state: Arc::new(self.state),
552 shutdown,
553 }
554 }
555
556 fn finalize(mut self) -> Self {
559 let Some(info) = self.openapi.take() else {
560 return self;
561 };
562 let spec = openapi::build(&info, self.router.registered(), &self.docs);
563 let spec = bytes::Bytes::from(serde_json::to_vec(&spec).unwrap_or_default());
564
565 let spec_handler = {
566 let spec = spec.clone();
567 move |_req: Request| {
568 let spec = spec.clone();
569 async move {
570 Ok(Response::ok()
571 .with_header("content-type", "application/json")
572 .with_body(spec))
573 }
574 }
575 };
576 self.insert_hidden("GET", &info.spec_path, Arc::new(spec_handler));
577
578 if let Some(docs_path) = &info.docs_path {
579 let page = openapi::swagger_ui_html(&info.title, &info.spec_path);
580 let page_handler = move |_req: Request| {
581 let page = page.clone();
582 async move { Ok(Response::html(page)) }
583 };
584 self.insert_hidden("GET", docs_path, Arc::new(page_handler));
585 }
586 self
587 }
588
589 fn insert_hidden(&mut self, method: &str, path: &str, handler: DynHandler) {
590 if let Err(e) = self
591 .router
592 .insert_as(RouteKind::Hidden, method, path, handler)
593 {
594 panic!("Failed to register {} route {}: {}", method, path, e);
595 }
596 }
597}
598
599async fn serve_connection<S>(
601 stream: S,
602 info: ConnInfo,
603 shared: Arc<Shared>,
604 shutdown: Shutdown,
605 _done: tokio::sync::mpsc::Sender<()>,
606) where
607 S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
608{
609 let io = TokioIo::new(stream);
610 let info = Arc::new(info);
611 let service = service_fn(|req| {
612 let (shared, info) = (shared.clone(), info.clone());
613 async move { Ok::<_, Infallible>(handle_request(req, &info, shared).await) }
614 });
615 let conn = http1::Builder::new()
616 .serve_connection(io, service)
617 .with_upgrades();
618 tokio::pin!(conn);
619
620 let mut draining = false;
621 loop {
622 tokio::select! {
623 result = conn.as_mut() => {
624 if let Err(err) = result {
625 eprintln!("Error serving connection: {:?}", err);
626 }
627 return;
628 }
629 _ = shutdown.wait(), if !draining => {
630 conn.as_mut().graceful_shutdown();
632 draining = true;
633 }
634 }
635 }
636}
637
638async fn termination_signal() {
640 #[cfg(unix)]
641 {
642 use tokio::signal::unix::{signal, SignalKind};
643 match signal(SignalKind::terminate()) {
644 Ok(mut term) => {
645 tokio::select! {
646 _ = tokio::signal::ctrl_c() => {}
647 _ = term.recv() => {}
648 }
649 return;
650 }
651 Err(e) => eprintln!("Cannot listen for SIGTERM: {}", e),
652 }
653 }
654 let _ = tokio::signal::ctrl_c().await;
655}
656
657impl Default for App {
658 fn default() -> Self {
659 Self::new()
660 }
661}
662
663impl RouteGroup {
664 fn new(prefix: &str) -> Self {
665 let prefix = prefix.trim_end_matches('/');
666 Self {
667 prefix: prefix.to_string(),
668 routes: Vec::new(),
669 middlewares: Vec::new(),
670 docs: Vec::new(),
671 body_limit: None,
672 ws_paths: Vec::new(),
673 }
674 }
675
676 #[cfg(feature = "websocket")]
680 pub fn ws_with<F, Fut>(
681 mut self,
682 path: &str,
683 options: crate::websocket::WsOptions,
684 handler: F,
685 ) -> Self
686 where
687 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
688 Fut: std::future::Future<Output = ()> + Send + 'static,
689 {
690 let route = crate::websocket::WsRoute::new(handler, options);
691 self.routes.push((
692 "GET".to_string(),
693 path.to_string(),
694 Arc::new(route) as DynHandler,
695 ));
696 self.ws_paths.push(path.to_string());
697 self
698 }
699
700 pub fn max_body_size(mut self, limit: Option<usize>) -> Self {
703 self.body_limit = Some(limit);
704 self
705 }
706
707 pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
710 self.docs
711 .push((method.to_ascii_uppercase(), path.to_string(), operation));
712 self
713 }
714
715 pub fn get<H>(mut self, path: &str, handler: H) -> Self
716 where
717 H: Handler,
718 {
719 self.routes.push((
720 "GET".to_string(),
721 path.to_string(),
722 Arc::new(handler) as DynHandler,
723 ));
724 self
725 }
726
727 pub fn post<H>(mut self, path: &str, handler: H) -> Self
728 where
729 H: Handler,
730 {
731 self.routes.push((
732 "POST".to_string(),
733 path.to_string(),
734 Arc::new(handler) as DynHandler,
735 ));
736 self
737 }
738
739 pub fn put<H>(mut self, path: &str, handler: H) -> Self
740 where
741 H: Handler,
742 {
743 self.routes.push((
744 "PUT".to_string(),
745 path.to_string(),
746 Arc::new(handler) as DynHandler,
747 ));
748 self
749 }
750
751 pub fn delete<H>(mut self, path: &str, handler: H) -> Self
752 where
753 H: Handler,
754 {
755 self.routes.push((
756 "DELETE".to_string(),
757 path.to_string(),
758 Arc::new(handler) as DynHandler,
759 ));
760 self
761 }
762
763 pub fn patch<H>(mut self, path: &str, handler: H) -> Self
764 where
765 H: Handler,
766 {
767 self.routes.push((
768 "PATCH".to_string(),
769 path.to_string(),
770 Arc::new(handler) as DynHandler,
771 ));
772 self
773 }
774
775 pub fn middleware<M>(mut self, middleware: M) -> Self
780 where
781 M: Middleware,
782 {
783 self.middlewares.push(Arc::new(middleware));
784 self
785 }
786}
787
788async fn handle_request(
789 req: hyper::Request<hyper::body::Incoming>,
790 conn: &ConnInfo,
791 shared: Arc<Shared>,
792) -> hyper::Response<crate::response::HyperBody> {
793 let Shared {
794 router,
795 middlewares,
796 max_body_size,
797 body_limits,
798 state,
799 shutdown,
800 } = &*shared;
801 let method = req.method().to_string();
802 let is_head = req.method() == http::Method::HEAD;
803 let path = req.uri().path().to_string();
804 let max_body_size = body_limit_for(body_limits, &path, *max_body_size);
805
806 let (parts, body) = req.into_parts();
808
809 let body_bytes = match max_body_size {
812 Some(max_size) => match read_limited(body, max_size).await {
813 Ok(bytes) => bytes,
814 Err(too_large) => return too_large.into_hyper(max_size),
815 },
816 None => match body.collect().await {
817 Ok(collected) => collected.to_bytes(),
818 Err(_) => bytes::Bytes::new(),
819 },
820 };
821
822 let mut request = Request::new(parts.method, parts.uri, parts.headers, body_bytes);
823 request.extensions = parts.extensions;
824 request.remote_addr = Some(conn.peer);
825 request.secure = conn.secure;
826 if let Some(cert) = &conn.client_cert {
827 request
828 .extensions
829 .insert(crate::request::ClientCertificate(cert.clone()));
830 }
831 request.state = Some(state.clone());
832 request.shutdown = Some(shutdown.clone());
833
834 let route = match router.match_route_with_pattern(&method, &path) {
837 Ok((handler, params, pattern)) => {
838 request.params = params;
839 request.matched_path = Some(pattern);
840 Route::Found(handler)
841 }
842 Err(_) => Route::Miss {
843 allow: router.allowed_methods(&path),
844 },
845 };
846
847 let result = if middlewares.is_empty() {
848 dispatch(route, request).await
849 } else {
850 let mut chain = middlewares.to_vec();
852 let final_handler = Arc::new(move |req: Request, _next: Next| {
853 let route = route.clone();
854 async move { dispatch(route, req).await }
855 });
856 chain.push(final_handler as Arc<dyn Middleware>);
857
858 Next::new(Arc::new(chain)).run(request).await
859 };
860
861 let response = match result {
863 Ok(resp) => resp,
864 Err(err) => Response::from_error(err),
865 };
866
867 let response = if is_head {
869 response.into_head()
870 } else {
871 response
872 };
873
874 response.into_hyper()
875}
876
877const DRAIN_CAP: u64 = 8 * 1024 * 1024;
881const DRAIN_TIME: Duration = Duration::from_secs(3);
882
883struct BodyTooLarge;
884
885impl BodyTooLarge {
886 fn into_hyper(self, max_size: usize) -> hyper::Response<crate::response::HyperBody> {
887 Response::from_error(Error::custom(
888 413,
889 format!("Request body too large (max {} bytes)", max_size),
890 ))
891 .with_header("connection", "close")
893 .into_hyper()
894 }
895}
896
897async fn read_limited(
899 mut body: hyper::body::Incoming,
900 max: usize,
901) -> std::result::Result<bytes::Bytes, BodyTooLarge> {
902 use bytes::BufMut;
903 use hyper::body::Body;
904
905 if body.size_hint().lower() > max as u64 {
907 drain(&mut body, 0).await;
908 return Err(BodyTooLarge);
909 }
910
911 let mut buf = bytes::BytesMut::new();
912 while let Some(frame) = body.frame().await {
913 let Ok(frame) = frame else {
914 return Ok(bytes::Bytes::new());
915 };
916 if let Ok(data) = frame.into_data() {
917 if buf.len() + data.len() > max {
918 drain(&mut body, (buf.len() + data.len()) as u64).await;
919 return Err(BodyTooLarge);
920 }
921 buf.put(data);
922 }
923 }
924 Ok(buf.freeze())
925}
926
927async fn drain(body: &mut hyper::body::Incoming, already: u64) {
929 let _ = tokio::time::timeout(DRAIN_TIME, async {
930 let mut seen = already;
931 while seen < DRAIN_CAP {
932 match body.frame().await {
933 Some(Ok(frame)) => {
934 if let Ok(data) = frame.into_data() {
935 seen += data.len() as u64;
936 }
937 }
938 _ => return,
939 }
940 }
941 })
942 .await;
943}
944
945fn body_limit_for(
947 rules: &[(String, Option<usize>)],
948 path: &str,
949 default: Option<usize>,
950) -> Option<usize> {
951 rules
952 .iter()
953 .filter(|(prefix, _)| {
954 prefix.is_empty()
955 || path == prefix
956 || path
957 .strip_prefix(prefix.as_str())
958 .is_some_and(|rest| rest.starts_with('/'))
959 })
960 .max_by_key(|(prefix, _)| prefix.len())
961 .map(|(_, limit)| *limit)
962 .unwrap_or(default)
963}
964
965#[derive(Clone)]
967enum Route {
968 Found(DynHandler),
969 Miss {
971 allow: Vec<&'static str>,
972 },
973}
974
975async fn dispatch(route: Route, req: Request) -> Result<Response> {
976 match route {
977 Route::Found(handler) => handler.call(req).await,
978 Route::Miss { allow } if allow.is_empty() => Err(Error::not_found("Route not found")),
979 Route::Miss { allow } => {
980 let allow = allow.join(", ");
981 if req.method == http::Method::OPTIONS {
982 Ok(Response::new(http::StatusCode::NO_CONTENT).with_header("allow", allow))
985 } else {
986 Ok(
987 Response::from_error(Error::custom(405, "Method not allowed"))
988 .with_header("allow", allow),
989 )
990 }
991 }
992 }
993}