Skip to main content

reinhardt_dispatch/
handler.rs

1//! Base HTTP request handler
2//!
3//! This module provides the base handler for processing HTTP requests,
4//! similar to Django's `django.core.handlers.base.BaseHandler`.
5
6use hyper::StatusCode;
7use reinhardt_core::signals::{
8	RequestFinishedEvent, RequestStartedEvent, request_finished, request_started,
9};
10use reinhardt_http::Handler;
11use reinhardt_http::{Request, Response};
12use reinhardt_urls::routers::DefaultRouter;
13use std::sync::Arc;
14use tracing::{debug, error, trace, warn};
15
16use crate::{DispatchError, exception::exception_to_dispatch_error};
17
18/// Base HTTP request handler
19///
20/// Handles the complete request lifecycle including URL resolution,
21/// view execution, and signal emission.
22pub struct BaseHandler {
23	/// Whether the handler operates in async mode.
24	///
25	/// This flag mirrors Django's `BaseHandler._is_async` and is read by
26	/// `Dispatcher` to choose between sync and async code paths. When
27	/// `false`, async dispatch still works but callers may opt for a
28	/// blocking wrapper.
29	// Allow dead_code: read via is_async() accessor; behavioral branching planned
30	#[allow(dead_code)]
31	is_async: bool,
32	router: Option<Arc<DefaultRouter>>,
33}
34
35impl BaseHandler {
36	/// Create a new base handler
37	pub fn new() -> Self {
38		Self {
39			is_async: true,
40			router: None,
41		}
42	}
43
44	/// Create a handler with a router
45	///
46	/// # Examples
47	///
48	/// ```
49	/// use reinhardt_dispatch::BaseHandler;
50	/// use reinhardt_urls::routers::DefaultRouter;
51	/// use std::sync::Arc;
52	///
53	/// let router = DefaultRouter::new();
54	/// let handler = BaseHandler::with_router(Arc::new(router));
55	/// assert!(handler.is_async());
56	/// ```
57	pub fn with_router(router: Arc<DefaultRouter>) -> Self {
58		Self {
59			is_async: true,
60			router: Some(router),
61		}
62	}
63
64	/// Handle an HTTP request
65	///
66	/// This is the main entry point for request processing. It:
67	/// 1. Emits `request_started` signal
68	/// 2. Resolves URL and dispatches to view
69	/// 3. Emits `request_finished` signal
70	/// 4. Converts an unmatched route into a 404 response
71	pub async fn handle_request(
72		&self,
73		request: Request,
74	) -> std::result::Result<Response, DispatchError> {
75		match self.handle_request_with_errors(request).await {
76			Err(DispatchError::UrlResolution(_)) => Ok(Response::new(StatusCode::NOT_FOUND)),
77			response => response,
78		}
79	}
80
81	/// Handle a request while preserving routing and view failures for an outer
82	/// exception handler.
83	async fn handle_request_with_errors(
84		&self,
85		request: Request,
86	) -> std::result::Result<Response, DispatchError> {
87		self.handle_request_with_framework_errors(request)
88			.await
89			.map_err(exception_to_dispatch_error)
90	}
91
92	/// Handle a request while preserving the original framework error variants.
93	///
94	/// The HTTP exception-handler adapter uses this path so an endpoint's
95	/// authentication, authorization, validation, or conflict error reaches the
96	/// application handler without being reclassified as a generic view error.
97	async fn handle_request_with_framework_errors(
98		&self,
99		request: Request,
100	) -> reinhardt_core::exception::Result<Response> {
101		trace!("Handling request: {:?}", request.uri);
102
103		// Emit request_started signal
104		let event = RequestStartedEvent::new();
105		if let Err(e) = request_started().send(event).await {
106			warn!("Failed to send request_started signal: {}", e);
107		}
108
109		// Get response with router
110		let response = Self::get_response_async(request, self.router.as_ref()).await;
111
112		// Emit request_finished signal
113		let event = RequestFinishedEvent::new();
114		if let Err(e) = request_finished().send(event).await {
115			warn!("Failed to send request_finished signal: {}", e);
116		}
117
118		response
119	}
120
121	/// Get response for a request (async version) with URL resolution
122	///
123	/// This is the core request processing logic that:
124	/// - Resolves the URL using the router
125	/// - Dispatches to the matched handler
126	/// - Returns a routing error if no route matches
127	/// - Returns an error for handler failures
128	async fn get_response_async(
129		request: Request,
130		router: Option<&Arc<DefaultRouter>>,
131	) -> reinhardt_core::exception::Result<Response> {
132		debug!("Getting response for: {}", request.uri.path());
133
134		// URL resolution with router
135		if let Some(router) = router {
136			trace!("Attempting to route request through router");
137
138			// Use the router to handle the request
139			match router.handle(request).await {
140				Ok(response) => {
141					trace!("Route handled successfully");
142					return Ok(response);
143				}
144				Err(reinhardt_core::exception::Error::NotFound(msg)) => {
145					debug!("No route matched: {}", msg);
146					return Err(reinhardt_core::exception::Error::NotFound(msg));
147				}
148				Err(e) => {
149					error!("Handler error: {}", e);
150					// Return the original error so an installed exception handler can
151					// preserve its status and variant.
152					return Err(e);
153				}
154			}
155		}
156
157		// Fallback: router not configured, so no routes can match.
158		debug!("No router configured, returning a URL resolution error");
159		Err(reinhardt_core::exception::Error::NotFound(
160			"No router configured".to_owned(),
161		))
162	}
163
164	/// Process an exception and convert it to a response.
165	///
166	/// Error details are logged server-side but not included in the response
167	/// body to prevent information disclosure.
168	pub async fn handle_exception(&self, _request: &Request, error: DispatchError) -> Response {
169		error!("Handling exception: {}", error);
170
171		crate::build_error_response(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error")
172	}
173
174	/// Check if handler is configured for async mode
175	pub fn is_async(&self) -> bool {
176		self.is_async
177	}
178
179	/// Set async mode for the handler
180	pub fn set_async(&mut self, is_async: bool) {
181		self.is_async = is_async;
182	}
183}
184
185impl Default for BaseHandler {
186	fn default() -> Self {
187		Self::new()
188	}
189}
190
191#[async_trait::async_trait]
192impl Handler for BaseHandler {
193	async fn handle(&self, request: Request) -> reinhardt_core::exception::Result<Response> {
194		let has_exception_handler = request
195			.extensions
196			.contains::<Arc<dyn reinhardt_http::ExceptionHandler>>();
197		if has_exception_handler {
198			return self.handle_request_with_framework_errors(request).await;
199		}
200		match self.handle_request_with_errors(request).await {
201			Ok(response) => Ok(response),
202			Err(DispatchError::UrlResolution(_)) => Ok(Response::new(StatusCode::NOT_FOUND)),
203			Err(e) => {
204				// Log the detailed error server-side; return generic message to client
205				error!("Handler error in BaseHandler::handle: {}", e);
206				Ok(crate::build_error_response(
207					StatusCode::INTERNAL_SERVER_ERROR,
208					"Internal Server Error",
209				))
210			}
211		}
212	}
213}
214
215#[cfg(test)]
216mod tests {
217	use std::sync::Mutex;
218
219	use super::*;
220	use async_trait::async_trait;
221	use bytes::Bytes;
222	use hyper::{HeaderMap, Method, Version};
223	use reinhardt_http::{ExceptionHandler, ExceptionHandlingHandler, Middleware, MiddlewareChain};
224	use reinhardt_urls::routers::{DefaultRouter, Router, path};
225	use rstest::rstest;
226
227	// Test handler for routing tests
228	struct TestHandler {
229		response_body: String,
230	}
231
232	#[async_trait]
233	impl Handler for TestHandler {
234		async fn handle(&self, _req: Request) -> reinhardt_core::exception::Result<Response> {
235			Ok(Response::ok().with_body(self.response_body.clone()))
236		}
237	}
238
239	#[tokio::test]
240	async fn test_base_handler_new() {
241		let handler = BaseHandler::new();
242		assert!(handler.is_async());
243	}
244
245	#[tokio::test]
246	async fn test_base_handler_handle_request() {
247		let handler = BaseHandler::new();
248		let request = Request::builder()
249			.method(Method::GET)
250			.uri("/")
251			.version(Version::HTTP_11)
252			.headers(HeaderMap::new())
253			.body(Bytes::new())
254			.build()
255			.unwrap();
256
257		let response = handler.handle_request(request).await;
258		let resp = response.unwrap();
259		// Handler without router should return 404 Not Found
260		assert_eq!(resp.status, StatusCode::NOT_FOUND);
261	}
262
263	#[tokio::test]
264	async fn test_base_handler_handle_exception() {
265		let handler = BaseHandler::new();
266		let request = Request::builder()
267			.method(Method::GET)
268			.uri("/")
269			.version(Version::HTTP_11)
270			.headers(HeaderMap::new())
271			.body(Bytes::new())
272			.build()
273			.unwrap();
274		let error = DispatchError::View("Test error".to_string());
275
276		let response = handler.handle_exception(&request, error).await;
277		assert_eq!(response.status, StatusCode::INTERNAL_SERVER_ERROR);
278	}
279
280	// ==========================================================================
281	// Information Disclosure Prevention Tests (#439)
282	// ==========================================================================
283
284	#[tokio::test]
285	async fn test_handle_exception_does_not_expose_internal_details() {
286		// Arrange
287		let handler = BaseHandler::new();
288		let request = Request::builder()
289			.method(Method::GET)
290			.uri("/")
291			.version(Version::HTTP_11)
292			.headers(HeaderMap::new())
293			.body(Bytes::new())
294			.build()
295			.unwrap();
296		let sensitive_detail = "database connection refused at postgres://admin:secret@db:5432";
297		let error = DispatchError::Internal(sensitive_detail.to_string());
298
299		// Act
300		let response = handler.handle_exception(&request, error).await;
301
302		// Assert: response must not contain the sensitive detail
303		let body = String::from_utf8(response.body.to_vec()).unwrap();
304		assert_eq!(response.status, StatusCode::INTERNAL_SERVER_ERROR);
305		assert!(!body.contains("database"));
306		assert!(!body.contains("postgres"));
307		assert!(!body.contains("secret"));
308		assert_eq!(body, "Internal Server Error");
309	}
310
311	#[tokio::test]
312	async fn test_handler_impl_does_not_expose_error_in_body() {
313		// Arrange: create a handler that returns a view error with internal paths
314		struct FailingHandler;
315
316		#[async_trait]
317		impl Handler for FailingHandler {
318			async fn handle(&self, _req: Request) -> reinhardt_core::exception::Result<Response> {
319				Err(reinhardt_core::exception::Error::Internal(
320					"module::secret_handler panicked at /src/app/handlers.rs:42".to_string(),
321				))
322			}
323		}
324
325		let mut router = DefaultRouter::new();
326		let failing = Arc::new(FailingHandler);
327		let mut route = path("/fail", failing);
328		route.name = Some("fail".to_string());
329		router.add_route(route);
330		let handler = BaseHandler::with_router(Arc::new(router));
331
332		let request = Request::builder()
333			.method(Method::GET)
334			.uri("/fail")
335			.version(Version::HTTP_11)
336			.headers(HeaderMap::new())
337			.body(Bytes::new())
338			.build()
339			.unwrap();
340
341		// Act
342		let response = handler.handle(request).await.unwrap();
343
344		// Assert: internal details must not leak
345		let body = String::from_utf8(response.body.to_vec()).unwrap();
346		assert_eq!(response.status, StatusCode::INTERNAL_SERVER_ERROR);
347		assert!(!body.contains("panicked"));
348		assert!(!body.contains("handlers.rs"));
349		assert!(!body.contains("secret_handler"));
350		assert_eq!(body, "Internal Server Error");
351	}
352
353	struct TeapotExceptionHandler;
354
355	#[async_trait]
356	impl ExceptionHandler for TeapotExceptionHandler {
357		async fn handle_exception(
358			&self,
359			_request: &Request,
360			_error: reinhardt_core::exception::Error,
361		) -> Response {
362			Response::new(StatusCode::IM_A_TEAPOT).with_body("teapot")
363		}
364	}
365
366	struct StatusExceptionHandler;
367
368	#[async_trait]
369	impl ExceptionHandler for StatusExceptionHandler {
370		async fn handle_exception(
371			&self,
372			_request: &Request,
373			error: reinhardt_core::exception::Error,
374		) -> Response {
375			let status = StatusCode::from_u16(error.status_code())
376				.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
377			Response::new(status)
378		}
379	}
380
381	#[rstest]
382	#[tokio::test]
383	async fn base_handler_routing_errors_reach_http_exception_handler() {
384		// Arrange
385		let base = Arc::new(BaseHandler::with_router(Arc::new(DefaultRouter::new())));
386		let handler = ExceptionHandlingHandler::new(base, Arc::new(TeapotExceptionHandler));
387		let request = Request::builder()
388			.method(Method::GET)
389			.uri("/missing")
390			.version(Version::HTTP_11)
391			.headers(HeaderMap::new())
392			.body(Bytes::new())
393			.build()
394			.unwrap();
395
396		// Act
397		let response = handler.handle(request).await.unwrap();
398
399		// Assert
400		assert_eq!(response.status, StatusCode::IM_A_TEAPOT);
401		assert_eq!(response.body, Bytes::from_static(b"teapot"));
402	}
403
404	#[rstest]
405	#[tokio::test]
406	async fn base_handler_view_errors_reach_http_exception_handler() {
407		struct FailingHandler;
408
409		#[async_trait]
410		impl Handler for FailingHandler {
411			async fn handle(
412				&self,
413				_request: Request,
414			) -> reinhardt_core::exception::Result<Response> {
415				Err(reinhardt_core::exception::Error::Internal(
416					"view failed".to_owned(),
417				))
418			}
419		}
420
421		// Arrange
422		let mut router = DefaultRouter::new();
423		router.add_route(path("/fail", Arc::new(FailingHandler)));
424		let base = Arc::new(BaseHandler::with_router(Arc::new(router)));
425		let handler = ExceptionHandlingHandler::new(base, Arc::new(TeapotExceptionHandler));
426		let request = Request::builder()
427			.method(Method::GET)
428			.uri("/fail")
429			.version(Version::HTTP_11)
430			.headers(HeaderMap::new())
431			.body(Bytes::new())
432			.build()
433			.unwrap();
434
435		// Act
436		let response = handler.handle(request).await.unwrap();
437
438		// Assert
439		assert_eq!(response.status, StatusCode::IM_A_TEAPOT);
440		assert_eq!(response.body, Bytes::from_static(b"teapot"));
441	}
442
443	#[rstest]
444	#[tokio::test]
445	async fn base_handler_preserves_endpoint_error_status_for_http_exception_handler() {
446		struct AuthenticationHandler;
447
448		#[async_trait]
449		impl Handler for AuthenticationHandler {
450			async fn handle(
451				&self,
452				_request: Request,
453			) -> reinhardt_core::exception::Result<Response> {
454				Err(reinhardt_core::exception::Error::Authentication(
455					"credentials rejected".to_owned(),
456				))
457			}
458		}
459
460		// Arrange
461		let mut router = DefaultRouter::new();
462		router.add_route(path("/private", Arc::new(AuthenticationHandler)));
463		let base = Arc::new(BaseHandler::with_router(Arc::new(router)));
464		let handler = ExceptionHandlingHandler::new(base, Arc::new(StatusExceptionHandler));
465		let request = Request::builder()
466			.method(Method::GET)
467			.uri("/private")
468			.version(Version::HTTP_11)
469			.headers(HeaderMap::new())
470			.body(Bytes::new())
471			.build()
472			.unwrap();
473
474		// Act
475		let response = handler.handle(request).await.unwrap();
476
477		// Assert: the endpoint's authentication error is not reclassified as 500.
478		assert_eq!(response.status, StatusCode::UNAUTHORIZED);
479	}
480
481	#[rstest]
482	#[tokio::test]
483	async fn base_handler_exception_handler_receives_resolved_path_params() {
484		struct FailingHandler;
485
486		#[async_trait]
487		impl Handler for FailingHandler {
488			async fn handle(
489				&self,
490				_request: Request,
491			) -> reinhardt_core::exception::Result<Response> {
492				Err(reinhardt_core::exception::Error::Internal(
493					"view failed".to_owned(),
494				))
495			}
496		}
497
498		struct PassthroughMiddleware;
499
500		#[async_trait]
501		impl Middleware for PassthroughMiddleware {
502			async fn process(
503				&self,
504				request: Request,
505				next: Arc<dyn Handler>,
506			) -> reinhardt_core::exception::Result<Response> {
507				next.handle(request).await
508			}
509		}
510
511		struct PathParamExceptionHandler {
512			observed: Arc<Mutex<Option<String>>>,
513		}
514
515		#[async_trait]
516		impl ExceptionHandler for PathParamExceptionHandler {
517			async fn handle_exception(
518				&self,
519				request: &Request,
520				_error: reinhardt_core::exception::Error,
521			) -> Response {
522				*self.observed.lock().unwrap() = request.path_params.get("id").cloned();
523				Response::new(StatusCode::IM_A_TEAPOT)
524			}
525		}
526
527		// Arrange
528		let observed = Arc::new(Mutex::new(None));
529		let mut router = DefaultRouter::new();
530		let mut route = path("/items/{id}", Arc::new(FailingHandler));
531		route.name = Some("item".to_owned());
532		router.add_route(route);
533		let base = Arc::new(BaseHandler::with_router(Arc::new(router)));
534		let handler = MiddlewareChain::new(base)
535			.with_middleware(Arc::new(PassthroughMiddleware))
536			.with_exception_handler(Arc::new(PathParamExceptionHandler {
537				observed: Arc::clone(&observed),
538			}));
539		let request = Request::builder()
540			.method(Method::GET)
541			.uri("/items/42")
542			.version(Version::HTTP_11)
543			.headers(HeaderMap::new())
544			.body(Bytes::new())
545			.build()
546			.unwrap();
547
548		// Act
549		let response = handler.handle(request).await.unwrap();
550
551		// Assert
552		assert_eq!(response.status, StatusCode::IM_A_TEAPOT);
553		assert_eq!(*observed.lock().unwrap(), Some("42".to_owned()));
554	}
555
556	#[rstest]
557	#[tokio::test]
558	async fn middleware_chain_exception_handler_refreshes_path_params_for_all_error_paths() {
559		struct Endpoint {
560			fails: bool,
561		}
562
563		#[async_trait]
564		impl Handler for Endpoint {
565			async fn handle(
566				&self,
567				_request: Request,
568			) -> reinhardt_core::exception::Result<Response> {
569				if self.fails {
570					Err(reinhardt_core::exception::Error::Internal(
571						"endpoint failed".to_owned(),
572					))
573				} else {
574					Ok(Response::ok())
575				}
576			}
577		}
578
579		struct RecordingExceptionHandler {
580			observed: Arc<Mutex<Vec<Option<String>>>>,
581		}
582
583		#[async_trait]
584		impl ExceptionHandler for RecordingExceptionHandler {
585			async fn handle_exception(
586				&self,
587				request: &Request,
588				_error: reinhardt_core::exception::Error,
589			) -> Response {
590				self.observed
591					.lock()
592					.unwrap()
593					.push(request.path_params.get("id").cloned());
594				Response::new(StatusCode::IM_A_TEAPOT)
595			}
596		}
597
598		struct PostProcessingMiddleware {
599			fails: bool,
600		}
601
602		#[async_trait]
603		impl Middleware for PostProcessingMiddleware {
604			async fn process(
605				&self,
606				request: Request,
607				next: Arc<dyn Handler>,
608			) -> reinhardt_core::exception::Result<Response> {
609				let response = next.handle(request).await?;
610				if self.fails {
611					Err(reinhardt_core::exception::Error::Internal(
612						"middleware failed after next".to_owned(),
613					))
614				} else {
615					Ok(response)
616				}
617			}
618		}
619
620		let build_base = |fails| {
621			let mut router = DefaultRouter::new();
622			router.add_route(path("/items/{id}", Arc::new(Endpoint { fails })));
623			Arc::new(BaseHandler::with_router(Arc::new(router))) as Arc<dyn Handler>
624		};
625		let request = || {
626			Request::builder()
627				.method(Method::GET)
628				.uri("/items/42")
629				.version(Version::HTTP_11)
630				.headers(HeaderMap::new())
631				.body(Bytes::new())
632				.build()
633				.unwrap()
634		};
635		let observed = Arc::new(Mutex::new(Vec::new()));
636		let exception_handler = || {
637			Arc::new(RecordingExceptionHandler {
638				observed: Arc::clone(&observed),
639			})
640		};
641
642		// Arrange and act: the bare chain refreshes the context after routing.
643		let bare =
644			MiddlewareChain::new(build_base(true)).with_exception_handler(exception_handler());
645		let bare_response = bare.handle(request()).await.unwrap();
646
647		// Arrange and act: a single middleware can fail during post-processing.
648		let single = MiddlewareChain::new(build_base(false))
649			.with_middleware(Arc::new(PostProcessingMiddleware { fails: true }))
650			.with_exception_handler(exception_handler());
651		let single_response = single.handle(request()).await.unwrap();
652
653		// Arrange and act: the composed multi-middleware path has the same contract.
654		let composed = MiddlewareChain::new(build_base(false))
655			.with_middleware(Arc::new(PostProcessingMiddleware { fails: true }))
656			.with_middleware(Arc::new(PostProcessingMiddleware { fails: false }))
657			.with_exception_handler(exception_handler());
658		let composed_response = composed.handle(request()).await.unwrap();
659
660		// Assert
661		assert_eq!(bare_response.status, StatusCode::IM_A_TEAPOT);
662		assert_eq!(single_response.status, StatusCode::IM_A_TEAPOT);
663		assert_eq!(composed_response.status, StatusCode::IM_A_TEAPOT);
664		assert_eq!(
665			*observed.lock().unwrap(),
666			vec![
667				Some("42".to_owned()),
668				Some("42".to_owned()),
669				Some("42".to_owned())
670			]
671		);
672	}
673
674	#[test]
675	fn test_base_handler_async_mode() {
676		let mut handler = BaseHandler::new();
677		assert!(handler.is_async());
678
679		handler.set_async(false);
680		assert!(!handler.is_async());
681	}
682
683	#[tokio::test]
684	async fn test_base_handler_different_methods() {
685		let handler = BaseHandler::new();
686
687		for method in [Method::GET, Method::POST, Method::PUT, Method::DELETE] {
688			let request = Request::builder()
689				.method(method)
690				.uri("/")
691				.version(Version::HTTP_11)
692				.headers(HeaderMap::new())
693				.body(Bytes::new())
694				.build()
695				.unwrap();
696
697			let response = handler.handle_request(request).await;
698			assert!(response.is_ok());
699		}
700	}
701
702	#[tokio::test]
703	async fn test_base_handler_different_uris() {
704		let handler = BaseHandler::new();
705
706		for path in ["/", "/test", "/api/v1/users", "/admin/login"] {
707			let request = Request::builder()
708				.method(Method::GET)
709				.uri(path)
710				.version(Version::HTTP_11)
711				.headers(HeaderMap::new())
712				.body(Bytes::new())
713				.build()
714				.unwrap();
715
716			let response = handler.handle_request(request).await;
717			assert!(response.is_ok());
718		}
719	}
720
721	#[tokio::test]
722	async fn test_handler_with_router() {
723		// Create a router with a test route
724		let mut router = DefaultRouter::new();
725		let test_handler = Arc::new(TestHandler {
726			response_body: "Test response".to_string(),
727		});
728		let mut route = path("/test", test_handler);
729		route.name = Some("test".to_string());
730		router.add_route(route);
731
732		// Create BaseHandler with router
733		let handler = BaseHandler::with_router(Arc::new(router));
734
735		// Test matching route
736		let request = Request::builder()
737			.method(Method::GET)
738			.uri("/test")
739			.version(Version::HTTP_11)
740			.headers(HeaderMap::new())
741			.body(Bytes::new())
742			.build()
743			.unwrap();
744
745		let response = handler.handle_request(request).await;
746		let resp = response.unwrap();
747		assert_eq!(resp.status, StatusCode::OK);
748
749		let body = String::from_utf8(resp.body.to_vec()).unwrap();
750		assert_eq!(body, "Test response");
751	}
752
753	#[tokio::test]
754	async fn test_handler_404_not_found() {
755		// Create empty router
756		let router = DefaultRouter::new();
757
758		// Create BaseHandler with router
759		let handler = BaseHandler::with_router(Arc::new(router));
760
761		// Test non-existent route
762		let request = Request::builder()
763			.method(Method::GET)
764			.uri("/nonexistent")
765			.version(Version::HTTP_11)
766			.headers(HeaderMap::new())
767			.body(Bytes::new())
768			.build()
769			.unwrap();
770
771		let response = handler.handle_request(request).await;
772		let resp = response.unwrap();
773		assert_eq!(resp.status, StatusCode::NOT_FOUND);
774	}
775
776	#[tokio::test]
777	async fn test_handler_multiple_routes() {
778		// Create router with multiple routes
779		let mut router = DefaultRouter::new();
780
781		let hello_handler = Arc::new(TestHandler {
782			response_body: "Hello".to_string(),
783		});
784		let mut hello_route = path("/hello", hello_handler);
785		hello_route.name = Some("hello".to_string());
786		router.add_route(hello_route);
787
788		let world_handler = Arc::new(TestHandler {
789			response_body: "World".to_string(),
790		});
791		let mut world_route = path("/world", world_handler);
792		world_route.name = Some("world".to_string());
793		router.add_route(world_route);
794
795		let handler = BaseHandler::with_router(Arc::new(router));
796
797		// Test first route
798		let request = Request::builder()
799			.method(Method::GET)
800			.uri("/hello")
801			.version(Version::HTTP_11)
802			.headers(HeaderMap::new())
803			.body(Bytes::new())
804			.build()
805			.unwrap();
806		let response = handler.handle_request(request).await.unwrap();
807		assert_eq!(response.status, StatusCode::OK);
808		assert_eq!(String::from_utf8(response.body.to_vec()).unwrap(), "Hello");
809
810		// Test second route
811		let request = Request::builder()
812			.method(Method::GET)
813			.uri("/world")
814			.version(Version::HTTP_11)
815			.headers(HeaderMap::new())
816			.body(Bytes::new())
817			.build()
818			.unwrap();
819		let response = handler.handle_request(request).await.unwrap();
820		assert_eq!(response.status, StatusCode::OK);
821		assert_eq!(String::from_utf8(response.body.to_vec()).unwrap(), "World");
822	}
823}