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}
33
34type BackgroundTask = Box<dyn FnOnce(Context) -> BoxFuture<'static, ()> + Send>;
35
36struct Shared {
38 router: Router,
39 middlewares: Vec<Arc<dyn Middleware>>,
40 max_body_size: Option<usize>,
41 state: Arc<StateMap>,
42 shutdown: Shutdown,
43}
44
45pub struct RouteGroup {
47 prefix: String,
48 routes: Vec<(String, String, DynHandler)>, middlewares: Vec<Arc<dyn Middleware>>,
50 docs: Vec<(String, String, Operation)>, }
52
53impl App {
54 pub fn new() -> Self {
55 Self {
56 router: Router::new(),
57 middlewares: Vec::new(),
58 max_body_size: Some(10 * 1024 * 1024), docs: HashMap::new(),
60 openapi: None,
61 state: StateMap::default(),
62 background: Vec::new(),
63 shutdown_grace: Duration::from_secs(10),
64 }
65 }
66
67 pub fn max_body_size(mut self, size: Option<usize>) -> Self {
69 self.max_body_size = size;
70 self
71 }
72
73 pub fn state<T: Send + Sync + 'static>(mut self, value: T) -> Self {
95 self.state.insert(value);
96 self
97 }
98
99 pub fn background<F, Fut>(mut self, task: F) -> Self
120 where
121 F: FnOnce(Context) -> Fut + Send + 'static,
122 Fut: std::future::Future<Output = ()> + Send + 'static,
123 {
124 self.background
125 .push(Box::new(move |ctx| Box::pin(task(ctx))));
126 self
127 }
128
129 pub fn shutdown_grace(mut self, grace: Duration) -> Self {
132 self.shutdown_grace = grace;
133 self
134 }
135
136 pub fn group(mut self, prefix: &str, configure: impl FnOnce(RouteGroup) -> RouteGroup) -> Self {
138 let group = RouteGroup::new(prefix);
139 let group = configure(group);
140
141 for (method, path, op) in group.docs {
142 let full_path = format!("{}{}", group.prefix, path);
143 self.docs.insert((method, full_path), op);
144 }
145
146 let middlewares = group.middlewares;
148 for (method, path, handler) in group.routes {
149 let full_path = format!("{}{}", group.prefix, path);
150 let handler: DynHandler = if middlewares.is_empty() {
151 handler
152 } else {
153 Arc::new(Layered::new(middlewares.clone(), handler))
154 };
155 if let Err(e) = self.router.insert(&method, &full_path, handler) {
156 panic!("Failed to register {} route {}: {}", method, full_path, e);
157 }
158 }
159
160 self
161 }
162
163 pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
167 self.docs
168 .insert((method.to_ascii_uppercase(), path.to_string()), operation);
169 self
170 }
171
172 pub fn openapi(mut self, info: OpenApiInfo) -> Self {
176 self.openapi = Some(info);
177 self
178 }
179
180 pub fn get<H>(mut self, path: &str, handler: H) -> Self
182 where
183 H: Handler,
184 {
185 if let Err(e) = self
186 .router
187 .insert("GET", path, Arc::new(handler) as DynHandler)
188 {
189 panic!("Failed to register GET route {}: {}", path, e);
190 }
191 self
192 }
193
194 pub fn get_with_params<F, Fut>(mut self, path: &str, handler: F) -> Self
196 where
197 F: Fn(Params) -> Fut + Send + Sync + 'static,
198 Fut: std::future::Future<Output = Result<Response>> + Send + 'static,
199 {
200 if let Err(e) = self.router.insert(
201 "GET",
202 path,
203 Arc::new(HandlerWithParams::new(handler)) as DynHandler,
204 ) {
205 panic!("Failed to register GET route {}: {}", path, e);
206 }
207 self
208 }
209
210 pub fn post<H>(mut self, path: &str, handler: H) -> Self
212 where
213 H: Handler,
214 {
215 if let Err(e) = self
216 .router
217 .insert("POST", path, Arc::new(handler) as DynHandler)
218 {
219 panic!("Failed to register POST route {}: {}", path, e);
220 }
221 self
222 }
223
224 pub fn put<H>(mut self, path: &str, handler: H) -> Self
226 where
227 H: Handler,
228 {
229 if let Err(e) = self
230 .router
231 .insert("PUT", path, Arc::new(handler) as DynHandler)
232 {
233 panic!("Failed to register PUT route {}: {}", path, e);
234 }
235 self
236 }
237
238 pub fn delete<H>(mut self, path: &str, handler: H) -> Self
240 where
241 H: Handler,
242 {
243 if let Err(e) = self
244 .router
245 .insert("DELETE", path, Arc::new(handler) as DynHandler)
246 {
247 panic!("Failed to register DELETE route {}: {}", path, e);
248 }
249 self
250 }
251
252 pub fn patch<H>(mut self, path: &str, handler: H) -> Self
254 where
255 H: Handler,
256 {
257 if let Err(e) = self
258 .router
259 .insert("PATCH", path, Arc::new(handler) as DynHandler)
260 {
261 panic!("Failed to register PATCH route {}: {}", path, e);
262 }
263 self
264 }
265
266 #[cfg(feature = "websocket")]
272 pub fn ws<F, Fut>(self, path: &str, handler: F) -> Self
273 where
274 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
275 Fut: std::future::Future<Output = ()> + Send + 'static,
276 {
277 self.ws_with(path, crate::websocket::WsOptions::default(), handler)
278 }
279
280 #[cfg(feature = "websocket")]
282 pub fn ws_with<F, Fut>(
283 mut self,
284 path: &str,
285 options: crate::websocket::WsOptions,
286 handler: F,
287 ) -> Self
288 where
289 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
290 Fut: std::future::Future<Output = ()> + Send + 'static,
291 {
292 let route = crate::websocket::WsRoute::new(handler, options);
293 if let Err(e) = self.router.insert_as(
294 RouteKind::WebSocket,
295 "GET",
296 path,
297 Arc::new(route) as DynHandler,
298 ) {
299 panic!("Failed to register WebSocket route {}: {}", path, e);
300 }
301 self
302 }
303
304 pub fn middleware<M>(mut self, middleware: M) -> Self
306 where
307 M: Middleware,
308 {
309 self.middlewares.push(Arc::new(middleware));
310 self
311 }
312
313 pub fn serve_static(mut self, route_prefix: &str, dir_path: &str) -> Self {
315 use crate::static_files::StaticFileHandler;
316 let handler = StaticFileHandler::new(dir_path);
317 let prefix = route_prefix.trim_end_matches('/');
319 let handler: DynHandler = Arc::new(handler);
320 let route = format!("{}/{{*filepath}}", prefix);
321 if let Err(e) = self
322 .router
323 .insert_as(RouteKind::Hidden, "GET", &route, handler.clone())
324 {
325 panic!("Failed to register static file route {}: {}", route, e);
326 }
327 let root = if prefix.is_empty() { "/" } else { prefix };
330 let _ = self
331 .router
332 .insert_as(RouteKind::Hidden, "GET", root, handler.clone());
333 if !prefix.is_empty() {
334 let _ =
335 self.router
336 .insert_as(RouteKind::Hidden, "GET", &format!("{}/", prefix), handler);
337 }
338 self
339 }
340
341 pub async fn run(self, addr: &str) -> Result<()> {
346 let addr: SocketAddr = addr
347 .parse()
348 .map_err(|e| Error::internal(format!("Invalid address: {}", e)))?;
349
350 let listener = TcpListener::bind(addr)
351 .await
352 .map_err(|e| Error::internal(format!("Failed to bind: {}", e)))?;
353
354 println!("š ullun-api listening on http://{}", addr);
355
356 let result = self
357 .serve_with_shutdown(listener, async {
358 termination_signal().await;
359 println!("\nš Shutting down gracefully...");
360 })
361 .await;
362 println!("ā
Server stopped");
363 result
364 }
365
366 pub async fn serve(self, listener: TcpListener) -> Result<()> {
373 self.serve_with_shutdown(listener, std::future::pending())
374 .await
375 }
376
377 pub async fn serve_with_shutdown(
384 mut self,
385 listener: TcpListener,
386 signal: impl std::future::Future<Output = ()>,
387 ) -> Result<()> {
388 self = self.finalize();
389 let grace = self.shutdown_grace;
390 let background = std::mem::take(&mut self.background);
391 let (trigger, rx) = tokio::sync::watch::channel(false);
392 let shutdown = Shutdown::new(rx);
393 let shared = Arc::new(self.into_shared(shutdown.clone()));
394
395 let tasks: Vec<_> = background
396 .into_iter()
397 .map(|task| {
398 let ctx = Context::new(shared.state.clone(), shutdown.clone());
399 tokio::spawn(task(ctx))
400 })
401 .collect();
402
403 let (done_tx, mut done_rx) = tokio::sync::mpsc::channel::<()>(1);
405 tokio::pin!(signal);
406 loop {
407 tokio::select! {
408 _ = &mut signal => break,
409 accepted = listener.accept() => match accepted {
410 Ok((stream, peer)) => {
411 tokio::spawn(serve_connection(
412 stream,
413 peer,
414 shared.clone(),
415 shutdown.clone(),
416 done_tx.clone(),
417 ));
418 }
419 Err(e) => {
420 eprintln!("Failed to accept connection: {}", e);
422 tokio::time::sleep(Duration::from_millis(100)).await;
423 }
424 },
425 }
426 }
427
428 let _ = trigger.send(true);
429 drop(done_tx);
430 drop(listener);
431 let drain = async {
432 let _ = done_rx.recv().await;
433 for task in tasks {
434 let _ = task.await;
435 }
436 };
437 if tokio::time::timeout(grace, drain).await.is_err() {
438 eprintln!(
439 "Shutdown grace period of {:?} elapsed, stopping anyway",
440 grace
441 );
442 }
443 Ok(())
444 }
445
446 fn into_shared(self, shutdown: Shutdown) -> Shared {
447 Shared {
448 router: self.router,
449 middlewares: self.middlewares,
450 max_body_size: self.max_body_size,
451 state: Arc::new(self.state),
452 shutdown,
453 }
454 }
455
456 fn finalize(mut self) -> Self {
459 let Some(info) = self.openapi.take() else {
460 return self;
461 };
462 let spec = openapi::build(&info, self.router.registered(), &self.docs);
463 let spec = bytes::Bytes::from(serde_json::to_vec(&spec).unwrap_or_default());
464
465 let spec_handler = {
466 let spec = spec.clone();
467 move |_req: Request| {
468 let spec = spec.clone();
469 async move {
470 Ok(Response::ok()
471 .with_header("content-type", "application/json")
472 .with_body(spec))
473 }
474 }
475 };
476 self.insert_hidden("GET", &info.spec_path, Arc::new(spec_handler));
477
478 if let Some(docs_path) = &info.docs_path {
479 let page = openapi::swagger_ui_html(&info.title, &info.spec_path);
480 let page_handler = move |_req: Request| {
481 let page = page.clone();
482 async move { Ok(Response::html(page)) }
483 };
484 self.insert_hidden("GET", docs_path, Arc::new(page_handler));
485 }
486 self
487 }
488
489 fn insert_hidden(&mut self, method: &str, path: &str, handler: DynHandler) {
490 if let Err(e) = self
491 .router
492 .insert_as(RouteKind::Hidden, method, path, handler)
493 {
494 panic!("Failed to register {} route {}: {}", method, path, e);
495 }
496 }
497}
498
499async fn serve_connection(
501 stream: tokio::net::TcpStream,
502 peer: SocketAddr,
503 shared: Arc<Shared>,
504 shutdown: Shutdown,
505 _done: tokio::sync::mpsc::Sender<()>,
506) {
507 let io = TokioIo::new(stream);
508 let service = service_fn(|req| {
509 let shared = shared.clone();
510 async move { Ok::<_, Infallible>(handle_request(req, peer, shared).await) }
511 });
512 let conn = http1::Builder::new()
513 .serve_connection(io, service)
514 .with_upgrades();
515 tokio::pin!(conn);
516
517 let mut draining = false;
518 loop {
519 tokio::select! {
520 result = conn.as_mut() => {
521 if let Err(err) = result {
522 eprintln!("Error serving connection: {:?}", err);
523 }
524 return;
525 }
526 _ = shutdown.wait(), if !draining => {
527 conn.as_mut().graceful_shutdown();
529 draining = true;
530 }
531 }
532 }
533}
534
535async fn termination_signal() {
537 #[cfg(unix)]
538 {
539 use tokio::signal::unix::{signal, SignalKind};
540 match signal(SignalKind::terminate()) {
541 Ok(mut term) => {
542 tokio::select! {
543 _ = tokio::signal::ctrl_c() => {}
544 _ = term.recv() => {}
545 }
546 return;
547 }
548 Err(e) => eprintln!("Cannot listen for SIGTERM: {}", e),
549 }
550 }
551 let _ = tokio::signal::ctrl_c().await;
552}
553
554impl Default for App {
555 fn default() -> Self {
556 Self::new()
557 }
558}
559
560impl RouteGroup {
561 fn new(prefix: &str) -> Self {
562 let prefix = prefix.trim_end_matches('/');
563 Self {
564 prefix: prefix.to_string(),
565 routes: Vec::new(),
566 middlewares: Vec::new(),
567 docs: Vec::new(),
568 }
569 }
570
571 pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
574 self.docs
575 .push((method.to_ascii_uppercase(), path.to_string(), operation));
576 self
577 }
578
579 pub fn get<H>(mut self, path: &str, handler: H) -> Self
580 where
581 H: Handler,
582 {
583 self.routes.push((
584 "GET".to_string(),
585 path.to_string(),
586 Arc::new(handler) as DynHandler,
587 ));
588 self
589 }
590
591 pub fn post<H>(mut self, path: &str, handler: H) -> Self
592 where
593 H: Handler,
594 {
595 self.routes.push((
596 "POST".to_string(),
597 path.to_string(),
598 Arc::new(handler) as DynHandler,
599 ));
600 self
601 }
602
603 pub fn put<H>(mut self, path: &str, handler: H) -> Self
604 where
605 H: Handler,
606 {
607 self.routes.push((
608 "PUT".to_string(),
609 path.to_string(),
610 Arc::new(handler) as DynHandler,
611 ));
612 self
613 }
614
615 pub fn delete<H>(mut self, path: &str, handler: H) -> Self
616 where
617 H: Handler,
618 {
619 self.routes.push((
620 "DELETE".to_string(),
621 path.to_string(),
622 Arc::new(handler) as DynHandler,
623 ));
624 self
625 }
626
627 pub fn patch<H>(mut self, path: &str, handler: H) -> Self
628 where
629 H: Handler,
630 {
631 self.routes.push((
632 "PATCH".to_string(),
633 path.to_string(),
634 Arc::new(handler) as DynHandler,
635 ));
636 self
637 }
638
639 pub fn middleware<M>(mut self, middleware: M) -> Self
644 where
645 M: Middleware,
646 {
647 self.middlewares.push(Arc::new(middleware));
648 self
649 }
650}
651
652async fn handle_request(
653 req: hyper::Request<hyper::body::Incoming>,
654 peer: SocketAddr,
655 shared: Arc<Shared>,
656) -> hyper::Response<crate::response::HyperBody> {
657 let Shared {
658 router,
659 middlewares,
660 max_body_size,
661 state,
662 shutdown,
663 } = &*shared;
664 let max_body_size = *max_body_size;
665 let method = req.method().to_string();
666 let path = req.uri().path().to_string();
667
668 let (parts, body) = req.into_parts();
670
671 let body_bytes = if let Some(max_size) = max_body_size {
673 if let Some(content_length) = parts.headers.get(http::header::CONTENT_LENGTH) {
675 if let Ok(length_str) = content_length.to_str() {
676 if let Ok(length) = length_str.parse::<usize>() {
677 if length > max_size {
678 let error_response = Response::from_error(Error::custom(
679 413,
680 format!("Request body too large (max {} bytes)", max_size),
681 ));
682 return error_response.into_hyper();
683 }
684 }
685 }
686 }
687
688 let limited = http_body_util::Limited::new(body, max_size);
690 match limited.collect().await {
691 Ok(collected) => collected.to_bytes(),
692 Err(_) => {
693 let error_response = Response::from_error(Error::custom(
695 413,
696 format!("Request body too large (max {} bytes)", max_size),
697 ));
698 return error_response.into_hyper();
699 }
700 }
701 } else {
702 match body.collect().await {
703 Ok(collected) => collected.to_bytes(),
704 Err(_) => bytes::Bytes::new(),
705 }
706 };
707
708 let mut request = Request::new(parts.method, parts.uri, parts.headers, body_bytes);
709 request.extensions = parts.extensions;
710 request.remote_addr = Some(peer);
711 request.state = Some(state.clone());
712 request.shutdown = Some(shutdown.clone());
713
714 let handler: Option<DynHandler> = match router.match_route(&method, &path) {
717 Ok((handler, params)) => {
718 request.params = params;
719 Some(handler)
720 }
721 Err(_) => None,
722 };
723
724 let result = if middlewares.is_empty() {
725 dispatch(handler, request).await
726 } else {
727 let mut chain = middlewares.to_vec();
729 let final_handler = Arc::new(move |req: Request, _next: Next| {
730 let handler = handler.clone();
731 async move { dispatch(handler, req).await }
732 });
733 chain.push(final_handler as Arc<dyn Middleware>);
734
735 Next::new(Arc::new(chain)).run(request).await
736 };
737
738 let response = match result {
740 Ok(resp) => resp,
741 Err(err) => Response::from_error(err),
742 };
743
744 response.into_hyper()
745}
746
747async fn dispatch(handler: Option<DynHandler>, req: Request) -> Result<Response> {
748 match handler {
749 Some(handler) => handler.call(req).await,
750 None => Err(Error::not_found("Route not found")),
751 }
752}