1use crate::error::{Error, Result};
4use crate::handler::{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 http_body_util::BodyExt;
11use hyper::server::conn::http1;
12use hyper::service::service_fn;
13use hyper_util::rt::TokioIo;
14use std::collections::HashMap;
15use std::convert::Infallible;
16use std::net::SocketAddr;
17use std::sync::Arc;
18use tokio::net::TcpListener;
19
20pub struct App {
22 router: Router,
23 middlewares: Vec<Arc<dyn Middleware>>,
24 max_body_size: Option<usize>, docs: HashMap<(String, String), Operation>,
26 openapi: Option<OpenApiInfo>,
27 state: StateMap,
28}
29
30struct Shared {
32 router: Router,
33 middlewares: Vec<Arc<dyn Middleware>>,
34 max_body_size: Option<usize>,
35 state: Arc<StateMap>,
36}
37
38pub struct RouteGroup {
40 prefix: String,
41 routes: Vec<(String, String, DynHandler)>, middlewares: Vec<Arc<dyn Middleware>>,
43 docs: Vec<(String, String, Operation)>, }
45
46impl App {
47 pub fn new() -> Self {
48 Self {
49 router: Router::new(),
50 middlewares: Vec::new(),
51 max_body_size: Some(10 * 1024 * 1024), docs: HashMap::new(),
53 openapi: None,
54 state: StateMap::default(),
55 }
56 }
57
58 pub fn max_body_size(mut self, size: Option<usize>) -> Self {
60 self.max_body_size = size;
61 self
62 }
63
64 pub fn state<T: Send + Sync + 'static>(mut self, value: T) -> Self {
86 self.state.insert(value);
87 self
88 }
89
90 pub fn group(mut self, prefix: &str, configure: impl FnOnce(RouteGroup) -> RouteGroup) -> Self {
92 let group = RouteGroup::new(prefix);
93 let group = configure(group);
94
95 for (method, path, op) in group.docs {
96 let full_path = format!("{}{}", group.prefix, path);
97 self.docs.insert((method, full_path), op);
98 }
99
100 let middlewares = group.middlewares;
102 for (method, path, handler) in group.routes {
103 let full_path = format!("{}{}", group.prefix, path);
104 let handler: DynHandler = if middlewares.is_empty() {
105 handler
106 } else {
107 Arc::new(Layered::new(middlewares.clone(), handler))
108 };
109 if let Err(e) = self.router.insert(&method, &full_path, handler) {
110 panic!("Failed to register {} route {}: {}", method, full_path, e);
111 }
112 }
113
114 self
115 }
116
117 pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
121 self.docs
122 .insert((method.to_ascii_uppercase(), path.to_string()), operation);
123 self
124 }
125
126 pub fn openapi(mut self, info: OpenApiInfo) -> Self {
130 self.openapi = Some(info);
131 self
132 }
133
134 pub fn get<H>(mut self, path: &str, handler: H) -> Self
136 where
137 H: Handler,
138 {
139 if let Err(e) = self
140 .router
141 .insert("GET", path, Arc::new(handler) as DynHandler)
142 {
143 panic!("Failed to register GET route {}: {}", path, e);
144 }
145 self
146 }
147
148 pub fn get_with_params<F, Fut>(mut self, path: &str, handler: F) -> Self
150 where
151 F: Fn(Params) -> Fut + Send + Sync + 'static,
152 Fut: std::future::Future<Output = Result<Response>> + Send + 'static,
153 {
154 if let Err(e) = self.router.insert(
155 "GET",
156 path,
157 Arc::new(HandlerWithParams::new(handler)) as DynHandler,
158 ) {
159 panic!("Failed to register GET route {}: {}", path, e);
160 }
161 self
162 }
163
164 pub fn post<H>(mut self, path: &str, handler: H) -> Self
166 where
167 H: Handler,
168 {
169 if let Err(e) = self
170 .router
171 .insert("POST", path, Arc::new(handler) as DynHandler)
172 {
173 panic!("Failed to register POST route {}: {}", path, e);
174 }
175 self
176 }
177
178 pub fn put<H>(mut self, path: &str, handler: H) -> Self
180 where
181 H: Handler,
182 {
183 if let Err(e) = self
184 .router
185 .insert("PUT", path, Arc::new(handler) as DynHandler)
186 {
187 panic!("Failed to register PUT route {}: {}", path, e);
188 }
189 self
190 }
191
192 pub fn delete<H>(mut self, path: &str, handler: H) -> Self
194 where
195 H: Handler,
196 {
197 if let Err(e) = self
198 .router
199 .insert("DELETE", path, Arc::new(handler) as DynHandler)
200 {
201 panic!("Failed to register DELETE route {}: {}", path, e);
202 }
203 self
204 }
205
206 pub fn patch<H>(mut self, path: &str, handler: H) -> Self
208 where
209 H: Handler,
210 {
211 if let Err(e) = self
212 .router
213 .insert("PATCH", path, Arc::new(handler) as DynHandler)
214 {
215 panic!("Failed to register PATCH route {}: {}", path, e);
216 }
217 self
218 }
219
220 #[cfg(feature = "websocket")]
226 pub fn ws<F, Fut>(self, path: &str, handler: F) -> Self
227 where
228 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
229 Fut: std::future::Future<Output = ()> + Send + 'static,
230 {
231 self.ws_with(path, crate::websocket::WsOptions::default(), handler)
232 }
233
234 #[cfg(feature = "websocket")]
236 pub fn ws_with<F, Fut>(
237 mut self,
238 path: &str,
239 options: crate::websocket::WsOptions,
240 handler: F,
241 ) -> Self
242 where
243 F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
244 Fut: std::future::Future<Output = ()> + Send + 'static,
245 {
246 let route = crate::websocket::WsRoute::new(handler, options);
247 if let Err(e) = self.router.insert_as(
248 RouteKind::WebSocket,
249 "GET",
250 path,
251 Arc::new(route) as DynHandler,
252 ) {
253 panic!("Failed to register WebSocket route {}: {}", path, e);
254 }
255 self
256 }
257
258 pub fn middleware<M>(mut self, middleware: M) -> Self
260 where
261 M: Middleware,
262 {
263 self.middlewares.push(Arc::new(middleware));
264 self
265 }
266
267 pub fn serve_static(mut self, route_prefix: &str, dir_path: &str) -> Self {
269 use crate::static_files::StaticFileHandler;
270 let handler = StaticFileHandler::new(dir_path);
271 let prefix = route_prefix.trim_end_matches('/');
273 let handler: DynHandler = Arc::new(handler);
274 let route = format!("{}/{{*filepath}}", prefix);
275 if let Err(e) = self
276 .router
277 .insert_as(RouteKind::Hidden, "GET", &route, handler.clone())
278 {
279 panic!("Failed to register static file route {}: {}", route, e);
280 }
281 let root = if prefix.is_empty() { "/" } else { prefix };
284 let _ = self
285 .router
286 .insert_as(RouteKind::Hidden, "GET", root, handler.clone());
287 if !prefix.is_empty() {
288 let _ =
289 self.router
290 .insert_as(RouteKind::Hidden, "GET", &format!("{}/", prefix), handler);
291 }
292 self
293 }
294
295 pub async fn run(self, addr: &str) -> Result<()> {
297 let this = self.finalize();
298 let addr: SocketAddr = addr
299 .parse()
300 .map_err(|e| Error::internal(format!("Invalid address: {}", e)))?;
301
302 let listener = TcpListener::bind(addr)
303 .await
304 .map_err(|e| Error::internal(format!("Failed to bind: {}", e)))?;
305
306 println!("š ullun-api listening on http://{}", addr);
307
308 let shared = Arc::new(this.into_shared());
309
310 let shutdown = async {
312 tokio::signal::ctrl_c()
313 .await
314 .expect("Failed to install CTRL+C handler");
315 println!("\nš Shutting down gracefully...");
316 };
317
318 tokio::select! {
319 _ = shutdown => {
320 println!("ā
Server stopped");
321 Ok(())
322 }
323 result = Self::serve_loop(listener, shared) => result
324 }
325 }
326
327 pub async fn serve(self, listener: TcpListener) -> Result<()> {
333 let this = self.finalize();
334 Self::serve_loop(listener, Arc::new(this.into_shared())).await
335 }
336
337 fn into_shared(self) -> Shared {
338 Shared {
339 router: self.router,
340 middlewares: self.middlewares,
341 max_body_size: self.max_body_size,
342 state: Arc::new(self.state),
343 }
344 }
345
346 fn finalize(mut self) -> Self {
349 let Some(info) = self.openapi.take() else {
350 return self;
351 };
352 let spec = openapi::build(&info, self.router.registered(), &self.docs);
353 let spec = bytes::Bytes::from(serde_json::to_vec(&spec).unwrap_or_default());
354
355 let spec_handler = {
356 let spec = spec.clone();
357 move |_req: Request| {
358 let spec = spec.clone();
359 async move {
360 Ok(Response::ok()
361 .with_header("content-type", "application/json")
362 .with_body(spec))
363 }
364 }
365 };
366 self.insert_hidden("GET", &info.spec_path, Arc::new(spec_handler));
367
368 if let Some(docs_path) = &info.docs_path {
369 let page = openapi::swagger_ui_html(&info.title, &info.spec_path);
370 let page_handler = move |_req: Request| {
371 let page = page.clone();
372 async move { Ok(Response::html(page)) }
373 };
374 self.insert_hidden("GET", docs_path, Arc::new(page_handler));
375 }
376 self
377 }
378
379 fn insert_hidden(&mut self, method: &str, path: &str, handler: DynHandler) {
380 if let Err(e) = self
381 .router
382 .insert_as(RouteKind::Hidden, method, path, handler)
383 {
384 panic!("Failed to register {} route {}: {}", method, path, e);
385 }
386 }
387
388 async fn serve_loop(listener: TcpListener, shared: Arc<Shared>) -> Result<()> {
389 loop {
390 let (stream, peer) = listener
391 .accept()
392 .await
393 .map_err(|e| Error::internal(format!("Failed to accept: {}", e)))?;
394
395 let io = TokioIo::new(stream);
396 let shared = shared.clone();
397
398 tokio::task::spawn(async move {
399 if let Err(err) =
400 http1::Builder::new()
401 .serve_connection(
402 io,
403 service_fn(|req| {
404 let shared = shared.clone();
405 async move {
406 Ok::<_, Infallible>(handle_request(req, peer, shared).await)
407 }
408 }),
409 )
410 .with_upgrades()
411 .await
412 {
413 eprintln!("Error serving connection: {:?}", err);
414 }
415 });
416 }
417 }
418}
419
420impl Default for App {
421 fn default() -> Self {
422 Self::new()
423 }
424}
425
426impl RouteGroup {
427 fn new(prefix: &str) -> Self {
428 let prefix = prefix.trim_end_matches('/');
429 Self {
430 prefix: prefix.to_string(),
431 routes: Vec::new(),
432 middlewares: Vec::new(),
433 docs: Vec::new(),
434 }
435 }
436
437 pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
440 self.docs
441 .push((method.to_ascii_uppercase(), path.to_string(), operation));
442 self
443 }
444
445 pub fn get<H>(mut self, path: &str, handler: H) -> Self
446 where
447 H: Handler,
448 {
449 self.routes.push((
450 "GET".to_string(),
451 path.to_string(),
452 Arc::new(handler) as DynHandler,
453 ));
454 self
455 }
456
457 pub fn post<H>(mut self, path: &str, handler: H) -> Self
458 where
459 H: Handler,
460 {
461 self.routes.push((
462 "POST".to_string(),
463 path.to_string(),
464 Arc::new(handler) as DynHandler,
465 ));
466 self
467 }
468
469 pub fn put<H>(mut self, path: &str, handler: H) -> Self
470 where
471 H: Handler,
472 {
473 self.routes.push((
474 "PUT".to_string(),
475 path.to_string(),
476 Arc::new(handler) as DynHandler,
477 ));
478 self
479 }
480
481 pub fn delete<H>(mut self, path: &str, handler: H) -> Self
482 where
483 H: Handler,
484 {
485 self.routes.push((
486 "DELETE".to_string(),
487 path.to_string(),
488 Arc::new(handler) as DynHandler,
489 ));
490 self
491 }
492
493 pub fn patch<H>(mut self, path: &str, handler: H) -> Self
494 where
495 H: Handler,
496 {
497 self.routes.push((
498 "PATCH".to_string(),
499 path.to_string(),
500 Arc::new(handler) as DynHandler,
501 ));
502 self
503 }
504
505 pub fn middleware<M>(mut self, middleware: M) -> Self
510 where
511 M: Middleware,
512 {
513 self.middlewares.push(Arc::new(middleware));
514 self
515 }
516}
517
518async fn handle_request(
519 req: hyper::Request<hyper::body::Incoming>,
520 peer: SocketAddr,
521 shared: Arc<Shared>,
522) -> hyper::Response<http_body_util::Full<bytes::Bytes>> {
523 let Shared {
524 router,
525 middlewares,
526 max_body_size,
527 state,
528 } = &*shared;
529 let max_body_size = *max_body_size;
530 let method = req.method().to_string();
531 let path = req.uri().path().to_string();
532
533 let (parts, body) = req.into_parts();
535
536 let body_bytes = if let Some(max_size) = max_body_size {
538 if let Some(content_length) = parts.headers.get(http::header::CONTENT_LENGTH) {
540 if let Ok(length_str) = content_length.to_str() {
541 if let Ok(length) = length_str.parse::<usize>() {
542 if length > max_size {
543 let error_response = Response::from_error(Error::custom(
544 413,
545 format!("Request body too large (max {} bytes)", max_size),
546 ));
547 return error_response.into_hyper();
548 }
549 }
550 }
551 }
552
553 let limited = http_body_util::Limited::new(body, max_size);
555 match limited.collect().await {
556 Ok(collected) => collected.to_bytes(),
557 Err(_) => {
558 let error_response = Response::from_error(Error::custom(
560 413,
561 format!("Request body too large (max {} bytes)", max_size),
562 ));
563 return error_response.into_hyper();
564 }
565 }
566 } else {
567 match body.collect().await {
568 Ok(collected) => collected.to_bytes(),
569 Err(_) => bytes::Bytes::new(),
570 }
571 };
572
573 let mut request = Request::new(parts.method, parts.uri, parts.headers, body_bytes);
574 request.extensions = parts.extensions;
575 request.remote_addr = Some(peer);
576 request.state = Some(state.clone());
577
578 let handler: Option<DynHandler> = match router.match_route(&method, &path) {
581 Ok((handler, params)) => {
582 request.params = params;
583 Some(handler)
584 }
585 Err(_) => None,
586 };
587
588 let result = if middlewares.is_empty() {
589 dispatch(handler, request).await
590 } else {
591 let mut chain = middlewares.to_vec();
593 let final_handler = Arc::new(move |req: Request, _next: Next| {
594 let handler = handler.clone();
595 async move { dispatch(handler, req).await }
596 });
597 chain.push(final_handler as Arc<dyn Middleware>);
598
599 Next::new(Arc::new(chain)).run(request).await
600 };
601
602 let response = match result {
604 Ok(resp) => resp,
605 Err(err) => Response::from_error(err),
606 };
607
608 response.into_hyper()
609}
610
611async fn dispatch(handler: Option<DynHandler>, req: Request) -> Result<Response> {
612 match handler {
613 Some(handler) => handler.call(req).await,
614 None => Err(Error::not_found("Route not found")),
615 }
616}