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