Skip to main content

reinhardt_http/
request.rs

1mod body;
2mod methods;
3mod params;
4
5use crate::extensions::Extensions;
6use bytes::Bytes;
7use hyper::{HeaderMap, Method, Uri, Version};
8#[cfg(feature = "parsers")]
9use reinhardt_core::parsers::parser::{ParsedData, Parser};
10use std::collections::HashMap;
11use std::collections::HashSet;
12use std::net::{IpAddr, SocketAddr};
13use std::sync::atomic::AtomicBool;
14use std::sync::{Arc, Mutex};
15
16/// Configuration for trusted proxy IPs.
17///
18/// Only proxy headers (X-Forwarded-For, X-Real-IP, X-Forwarded-Proto) from
19/// these IP addresses will be trusted. By default, no proxies are trusted
20/// and the actual connection information is used.
21#[derive(Debug, Clone, Default)]
22pub struct TrustedProxies {
23	/// Set of trusted proxy IP addresses.
24	/// Only requests originating from these IPs will have their proxy headers honored.
25	trusted_ips: HashSet<IpAddr>,
26}
27
28impl TrustedProxies {
29	/// Create with no trusted proxies (default, most secure).
30	pub fn none() -> Self {
31		Self {
32			trusted_ips: HashSet::new(),
33		}
34	}
35
36	/// Create with a set of trusted proxy IPs.
37	pub fn new(ips: impl IntoIterator<Item = IpAddr>) -> Self {
38		Self {
39			trusted_ips: ips.into_iter().collect(),
40		}
41	}
42
43	/// Check if the given address is a trusted proxy.
44	pub fn is_trusted(&self, addr: &IpAddr) -> bool {
45		self.trusted_ips.contains(addr)
46	}
47
48	/// Check if any proxies are configured.
49	pub fn has_trusted_proxies(&self) -> bool {
50		!self.trusted_ips.is_empty()
51	}
52}
53
54/// HTTP Request representation
55pub struct Request {
56	pub method: Method,
57	pub uri: Uri,
58	pub version: Version,
59	pub headers: HeaderMap,
60	body: Bytes,
61	pub path_params: HashMap<String, String>,
62	pub query_params: HashMap<String, String>,
63	/// Indicates if this request came over HTTPS
64	pub is_secure: bool,
65	/// Remote address of the client (if available)
66	pub remote_addr: Option<SocketAddr>,
67	/// Parsers for request body
68	#[cfg(feature = "parsers")]
69	parsers: Vec<Box<dyn Parser>>,
70	/// Cached parsed data (lazy parsing)
71	#[cfg(feature = "parsers")]
72	parsed_data: Arc<Mutex<Option<ParsedData>>>,
73	/// Whether the body has been consumed
74	body_consumed: Arc<AtomicBool>,
75	/// Extensions for storing arbitrary typed data
76	pub extensions: Extensions,
77}
78
79/// Builder for constructing `Request` instances.
80///
81/// Provides a fluent API for building HTTP requests with optional parameters.
82///
83/// # Examples
84///
85/// ```
86/// use reinhardt_http::Request;
87/// use hyper::Method;
88///
89/// let request = Request::builder()
90///     .method(Method::GET)
91///     .uri("/api/users?page=1")
92///     .build()
93///     .unwrap();
94///
95/// assert_eq!(request.method, Method::GET);
96/// assert_eq!(request.path(), "/api/users");
97/// assert_eq!(request.query_params.get("page"), Some(&"1".to_string()));
98/// ```
99pub struct RequestBuilder {
100	method: Method,
101	uri: Option<Uri>,
102	version: Version,
103	headers: HeaderMap,
104	body: Bytes,
105	is_secure: bool,
106	remote_addr: Option<SocketAddr>,
107	path_params: HashMap<String, String>,
108	/// Captured error from invalid URI
109	uri_error: Option<String>,
110	/// Captured error from invalid header value
111	header_error: Option<String>,
112	#[cfg(feature = "parsers")]
113	parsers: Vec<Box<dyn Parser>>,
114}
115
116impl Default for RequestBuilder {
117	fn default() -> Self {
118		Self {
119			method: Method::GET,
120			uri: None,
121			version: Version::HTTP_11,
122			headers: HeaderMap::new(),
123			body: Bytes::new(),
124			is_secure: false,
125			remote_addr: None,
126			path_params: HashMap::new(),
127			uri_error: None,
128			header_error: None,
129			#[cfg(feature = "parsers")]
130			parsers: Vec::new(),
131		}
132	}
133}
134
135impl RequestBuilder {
136	/// Set the HTTP method.
137	///
138	/// # Examples
139	///
140	/// ```
141	/// use reinhardt_http::Request;
142	/// use hyper::Method;
143	///
144	/// let request = Request::builder()
145	///     .method(Method::POST)
146	///     .uri("/api/users")
147	///     .build()
148	///     .unwrap();
149	///
150	/// assert_eq!(request.method, Method::POST);
151	/// ```
152	pub fn method(mut self, method: Method) -> Self {
153		self.method = method;
154		self
155	}
156
157	/// Set the request URI.
158	///
159	/// Accepts either a `&str` or `Uri`. Query parameters will be automatically parsed.
160	///
161	/// # Examples
162	///
163	/// ```
164	/// use reinhardt_http::Request;
165	/// use hyper::Method;
166	///
167	/// let request = Request::builder()
168	///     .method(Method::GET)
169	///     .uri("/api/users?page=1&limit=10")
170	///     .build()
171	///     .unwrap();
172	///
173	/// assert_eq!(request.path(), "/api/users");
174	/// assert_eq!(request.query_params.get("page"), Some(&"1".to_string()));
175	/// assert_eq!(request.query_params.get("limit"), Some(&"10".to_string()));
176	/// ```
177	pub fn uri<T>(mut self, uri: T) -> Self
178	where
179		T: TryInto<Uri>,
180		T::Error: std::fmt::Display,
181	{
182		match uri.try_into() {
183			Ok(uri) => {
184				self.uri = Some(uri);
185			}
186			Err(e) => {
187				self.uri_error = Some(format!("Invalid URI: {}", e));
188			}
189		}
190		self
191	}
192
193	/// Set the HTTP version.
194	///
195	/// Defaults to HTTP/1.1 if not specified.
196	///
197	/// # Examples
198	///
199	/// ```
200	/// use reinhardt_http::Request;
201	/// use hyper::{Method, Version};
202	///
203	/// let request = Request::builder()
204	///     .method(Method::GET)
205	///     .uri("/api/users")
206	///     .version(Version::HTTP_2)
207	///     .build()
208	///     .unwrap();
209	///
210	/// assert_eq!(request.version, Version::HTTP_2);
211	/// ```
212	pub fn version(mut self, version: Version) -> Self {
213		self.version = version;
214		self
215	}
216
217	/// Set the request headers.
218	///
219	/// Replaces all existing headers.
220	///
221	/// # Examples
222	///
223	/// ```
224	/// use reinhardt_http::Request;
225	/// use hyper::{Method, HeaderMap, header};
226	///
227	/// let mut headers = HeaderMap::new();
228	/// headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
229	///
230	/// let request = Request::builder()
231	///     .method(Method::POST)
232	///     .uri("/api/users")
233	///     .headers(headers.clone())
234	///     .build()
235	///     .unwrap();
236	///
237	/// assert_eq!(request.headers.get(header::CONTENT_TYPE).unwrap(), "application/json");
238	/// ```
239	pub fn headers(mut self, headers: HeaderMap) -> Self {
240		self.headers = headers;
241		self
242	}
243
244	/// Add a single header to the request.
245	///
246	/// # Examples
247	///
248	/// ```
249	/// use reinhardt_http::Request;
250	/// use hyper::{Method, header};
251	///
252	/// let request = Request::builder()
253	///     .method(Method::POST)
254	///     .uri("/api/users")
255	///     .header(header::CONTENT_TYPE, "application/json")
256	///     .header(header::AUTHORIZATION, "Bearer token123")
257	///     .build()
258	///     .unwrap();
259	///
260	/// assert_eq!(request.headers.get(header::CONTENT_TYPE).unwrap(), "application/json");
261	/// assert_eq!(request.headers.get(header::AUTHORIZATION).unwrap(), "Bearer token123");
262	/// ```
263	pub fn header<K, V>(mut self, key: K, value: V) -> Self
264	where
265		K: hyper::header::IntoHeaderName,
266		V: TryInto<hyper::header::HeaderValue>,
267		V::Error: std::fmt::Display,
268	{
269		match value.try_into() {
270			Ok(val) => {
271				self.headers.insert(key, val);
272			}
273			Err(e) => {
274				self.header_error = Some(format!("Invalid header value: {}", e));
275			}
276		}
277		self
278	}
279
280	/// Set the request body.
281	///
282	/// # Examples
283	///
284	/// ```
285	/// use reinhardt_http::Request;
286	/// use hyper::Method;
287	/// use bytes::Bytes;
288	///
289	/// let body = Bytes::from(r#"{"name":"Alice"}"#);
290	/// let request = Request::builder()
291	///     .method(Method::POST)
292	///     .uri("/api/users")
293	///     .body(body.clone())
294	///     .build()
295	///     .unwrap();
296	///
297	/// assert_eq!(request.body(), &body);
298	/// ```
299	pub fn body(mut self, body: Bytes) -> Self {
300		self.body = body;
301		self
302	}
303
304	/// Set whether the request is secure (HTTPS).
305	///
306	/// Defaults to `false` if not specified.
307	///
308	/// # Examples
309	///
310	/// ```
311	/// use reinhardt_http::Request;
312	/// use hyper::Method;
313	///
314	/// let request = Request::builder()
315	///     .method(Method::GET)
316	///     .uri("/")
317	///     .secure(true)
318	///     .build()
319	///     .unwrap();
320	///
321	/// assert!(request.is_secure());
322	/// assert_eq!(request.scheme(), "https");
323	/// ```
324	pub fn secure(mut self, is_secure: bool) -> Self {
325		self.is_secure = is_secure;
326		self
327	}
328
329	/// Set the remote address of the client.
330	///
331	/// # Examples
332	///
333	/// ```
334	/// use reinhardt_http::Request;
335	/// use hyper::Method;
336	/// use std::net::{SocketAddr, IpAddr, Ipv4Addr};
337	///
338	/// let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
339	/// let request = Request::builder()
340	///     .method(Method::GET)
341	///     .uri("/")
342	///     .remote_addr(addr)
343	///     .build()
344	///     .unwrap();
345	///
346	/// assert_eq!(request.remote_addr, Some(addr));
347	/// ```
348	pub fn remote_addr(mut self, addr: SocketAddr) -> Self {
349		self.remote_addr = Some(addr);
350		self
351	}
352
353	/// Add a parser to the request.
354	///
355	/// Parsers are used to parse the request body into specific formats.
356	/// The parser will be boxed internally.
357	///
358	/// # Examples
359	///
360	/// ```ignore
361	/// use reinhardt_http::Request;
362	/// use hyper::Method;
363	///
364	/// let request = Request::builder()
365	///     .method(Method::POST)
366	///     .uri("/api/users")
367	///     .parser(JsonParser::new())
368	///     .build()
369	///     .unwrap();
370	/// ```
371	#[cfg(feature = "parsers")]
372	pub fn parser<P: Parser + 'static>(mut self, parser: P) -> Self {
373		self.parsers.push(Box::new(parser));
374		self
375	}
376
377	/// Set path parameters (used for testing views without router).
378	///
379	/// This is primarily useful in test environments where you need to simulate
380	/// path parameters that would normally be extracted by the router.
381	///
382	/// # Examples
383	///
384	/// ```
385	/// use reinhardt_http::Request;
386	/// use hyper::Method;
387	/// use std::collections::HashMap;
388	///
389	/// let mut params = HashMap::new();
390	/// params.insert("id".to_string(), "42".to_string());
391	///
392	/// let request = Request::builder()
393	///     .method(Method::GET)
394	///     .uri("/api/users/42")
395	///     .path_params(params)
396	///     .build()
397	///     .unwrap();
398	///
399	/// assert_eq!(request.path_params.get("id"), Some(&"42".to_string()));
400	/// ```
401	pub fn path_params(mut self, params: HashMap<String, String>) -> Self {
402		self.path_params = params;
403		self
404	}
405
406	/// Build the final `Request` instance.
407	///
408	/// Returns an error if the URI is missing.
409	///
410	/// # Examples
411	///
412	/// ```
413	/// use reinhardt_http::Request;
414	/// use hyper::Method;
415	///
416	/// let request = Request::builder()
417	///     .method(Method::GET)
418	///     .uri("/api/users")
419	///     .build()
420	///     .unwrap();
421	///
422	/// assert_eq!(request.method, Method::GET);
423	/// assert_eq!(request.path(), "/api/users");
424	/// ```
425	pub fn build(self) -> Result<Request, String> {
426		// Report captured errors from builder methods
427		if let Some(err) = self.uri_error {
428			return Err(err);
429		}
430		if let Some(err) = self.header_error {
431			return Err(err);
432		}
433		let uri = self.uri.ok_or_else(|| "URI is required".to_string())?;
434		let query_params = Request::parse_query_params(&uri);
435
436		Ok(Request {
437			method: self.method,
438			uri,
439			version: self.version,
440			headers: self.headers,
441			body: self.body,
442			path_params: self.path_params,
443			query_params,
444			is_secure: self.is_secure,
445			remote_addr: self.remote_addr,
446			#[cfg(feature = "parsers")]
447			parsers: self.parsers,
448			#[cfg(feature = "parsers")]
449			parsed_data: Arc::new(Mutex::new(None)),
450			body_consumed: Arc::new(AtomicBool::new(false)),
451			extensions: Extensions::new(),
452		})
453	}
454}
455
456impl Request {
457	/// Create a new `RequestBuilder`.
458	///
459	/// # Examples
460	///
461	/// ```
462	/// use reinhardt_http::Request;
463	/// use hyper::Method;
464	///
465	/// let request = Request::builder()
466	///     .method(Method::GET)
467	///     .uri("/api/users")
468	///     .build()
469	///     .unwrap();
470	///
471	/// assert_eq!(request.method, Method::GET);
472	/// ```
473	pub fn builder() -> RequestBuilder {
474		RequestBuilder::default()
475	}
476
477	/// Set the DI context for this request (used by routers with dependency injection)
478	///
479	/// This method stores the DI context in the request's extensions,
480	/// allowing handlers to access dependency injection services.
481	///
482	/// The context will be wrapped in an Arc internally for efficient sharing.
483	/// The DI context type is generic to avoid circular dependencies.
484	///
485	/// # Examples
486	///
487	/// ```rust,no_run
488	/// use reinhardt_http::Request;
489	/// use hyper::Method;
490	///
491	/// # struct DummyDiContext;
492	/// let mut request = Request::builder()
493	///     .method(Method::GET)
494	///     .uri("/")
495	///     .build()
496	///     .unwrap();
497	///
498	/// let di_ctx = DummyDiContext;
499	/// request.set_di_context(di_ctx);
500	/// ```
501	pub fn set_di_context<T: Send + Sync + 'static>(&mut self, ctx: T) {
502		self.extensions.insert(Arc::new(ctx));
503	}
504
505	/// Get the DI context from this request
506	///
507	/// Returns `None` if no DI context was set.
508	///
509	/// The DI context type is generic to avoid circular dependencies.
510	/// Returns a reference to the context.
511	///
512	/// # Examples
513	///
514	/// ```rust,no_run
515	/// use reinhardt_http::Request;
516	/// use hyper::Method;
517	///
518	/// # struct DummyDiContext;
519	/// let mut request = Request::builder()
520	///     .method(Method::GET)
521	///     .uri("/")
522	///     .build()
523	///     .unwrap();
524	///
525	/// let di_ctx = DummyDiContext;
526	/// request.set_di_context(di_ctx);
527	///
528	/// let ctx = request.get_di_context::<DummyDiContext>();
529	/// assert!(ctx.is_some());
530	/// ```
531	pub fn get_di_context<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
532		self.extensions.get::<Arc<T>>()
533	}
534
535	/// Extract Bearer token from Authorization header
536	///
537	/// Extracts JWT or other bearer tokens from the Authorization header.
538	/// Returns `None` if the header is missing or not in "Bearer `<token>`" format.
539	///
540	/// # Examples
541	///
542	/// ```
543	/// use reinhardt_http::Request;
544	/// use hyper::{Method, Version, HeaderMap, header};
545	/// use bytes::Bytes;
546	///
547	/// let mut headers = HeaderMap::new();
548	/// headers.insert(
549	///     header::AUTHORIZATION,
550	///     "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9".parse().unwrap()
551	/// );
552	///
553	/// let request = Request::builder()
554	///     .method(Method::GET)
555	///     .uri("/")
556	///     .version(Version::HTTP_11)
557	///     .headers(headers)
558	///     .body(Bytes::new())
559	///     .build()
560	///     .unwrap();
561	///
562	/// let token = request.extract_bearer_token();
563	/// assert_eq!(token, Some("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9".to_string()));
564	/// ```
565	///
566	/// # Missing or invalid header
567	///
568	/// ```
569	/// use reinhardt_http::Request;
570	/// use hyper::{Method, Version, HeaderMap};
571	/// use bytes::Bytes;
572	///
573	/// let request = Request::builder()
574	///     .method(Method::GET)
575	///     .uri("/")
576	///     .version(Version::HTTP_11)
577	///     .headers(HeaderMap::new())
578	///     .body(Bytes::new())
579	///     .build()
580	///     .unwrap();
581	///
582	/// let token = request.extract_bearer_token();
583	/// assert_eq!(token, None);
584	/// ```
585	pub fn extract_bearer_token(&self) -> Option<String> {
586		self.headers
587			.get(hyper::header::AUTHORIZATION)
588			.and_then(|value| value.to_str().ok())
589			.and_then(|auth_str| auth_str.strip_prefix("Bearer ").map(|s| s.to_string()))
590	}
591
592	/// Get a specific header value from the request
593	///
594	/// Returns `None` if the header is missing or cannot be converted to a string.
595	///
596	/// # Examples
597	///
598	/// ```
599	/// use reinhardt_http::Request;
600	/// use hyper::{Method, Version, HeaderMap, header};
601	/// use bytes::Bytes;
602	///
603	/// let mut headers = HeaderMap::new();
604	/// headers.insert(
605	///     header::USER_AGENT,
606	///     "Mozilla/5.0".parse().unwrap()
607	/// );
608	///
609	/// let request = Request::builder()
610	///     .method(Method::GET)
611	///     .uri("/")
612	///     .version(Version::HTTP_11)
613	///     .headers(headers)
614	///     .body(Bytes::new())
615	///     .build()
616	///     .unwrap();
617	///
618	/// let user_agent = request.get_header("user-agent");
619	/// assert_eq!(user_agent, Some("Mozilla/5.0".to_string()));
620	/// ```
621	///
622	/// # Missing header
623	///
624	/// ```
625	/// use reinhardt_http::Request;
626	/// use hyper::{Method, Version, HeaderMap};
627	/// use bytes::Bytes;
628	///
629	/// let request = Request::builder()
630	///     .method(Method::GET)
631	///     .uri("/")
632	///     .version(Version::HTTP_11)
633	///     .headers(HeaderMap::new())
634	///     .body(Bytes::new())
635	///     .build()
636	///     .unwrap();
637	///
638	/// let header = request.get_header("x-custom-header");
639	/// assert_eq!(header, None);
640	/// ```
641	pub fn get_header(&self, name: &str) -> Option<String> {
642		self.headers
643			.get(name)
644			.and_then(|value| value.to_str().ok())
645			.map(|s| s.to_string())
646	}
647
648	/// Extract client IP address from the request
649	///
650	/// Only trusts proxy headers (X-Forwarded-For, X-Real-IP) when the request
651	/// originates from a configured trusted proxy. Without trusted proxies,
652	/// falls back to the actual connection address.
653	///
654	/// # Examples
655	///
656	/// ```
657	/// use reinhardt_http::{Request, TrustedProxies};
658	/// use hyper::{Method, Version, HeaderMap, header};
659	/// use bytes::Bytes;
660	/// use std::net::{SocketAddr, IpAddr, Ipv4Addr};
661	///
662	/// let proxy_ip = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1));
663	/// let mut headers = HeaderMap::new();
664	/// headers.insert(
665	///     header::HeaderName::from_static("x-forwarded-for"),
666	///     "203.0.113.1, 198.51.100.1".parse().unwrap()
667	/// );
668	///
669	/// let request = Request::builder()
670	///     .method(Method::GET)
671	///     .uri("/")
672	///     .version(Version::HTTP_11)
673	///     .headers(headers)
674	///     .remote_addr(SocketAddr::new(proxy_ip, 8080))
675	///     .body(Bytes::new())
676	///     .build()
677	///     .unwrap();
678	///
679	/// // Configure trusted proxies to honor X-Forwarded-For
680	/// request.set_trusted_proxies(TrustedProxies::new(vec![proxy_ip]));
681	///
682	/// let ip = request.get_client_ip();
683	/// assert_eq!(ip, Some("203.0.113.1".parse().unwrap()));
684	/// ```
685	///
686	/// # No trusted proxy, fallback to remote_addr
687	///
688	/// ```
689	/// use reinhardt_http::Request;
690	/// use hyper::{Method, Version, HeaderMap};
691	/// use bytes::Bytes;
692	/// use std::net::{SocketAddr, IpAddr, Ipv4Addr};
693	///
694	/// let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
695	/// let request = Request::builder()
696	///     .method(Method::GET)
697	///     .uri("/")
698	///     .version(Version::HTTP_11)
699	///     .headers(HeaderMap::new())
700	///     .remote_addr(addr)
701	///     .body(Bytes::new())
702	///     .build()
703	///     .unwrap();
704	///
705	/// let ip = request.get_client_ip();
706	/// assert_eq!(ip, Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))));
707	/// ```
708	pub fn get_client_ip(&self) -> Option<std::net::IpAddr> {
709		// Only trust proxy headers if the request comes from a configured trusted proxy
710		if self.is_from_trusted_proxy() {
711			// Try X-Forwarded-For header first (common in proxy setups)
712			if let Some(forwarded) = self.get_header("x-forwarded-for") {
713				// X-Forwarded-For can contain multiple IPs, take the first one
714				if let Some(first_ip) = forwarded.split(',').next()
715					&& let Ok(ip) = first_ip.trim().parse()
716				{
717					return Some(ip);
718				}
719			}
720
721			// Try X-Real-IP header
722			if let Some(real_ip) = self.get_header("x-real-ip")
723				&& let Ok(ip) = real_ip.parse()
724			{
725				return Some(ip);
726			}
727		}
728
729		// Fallback to remote_addr (actual connection info)
730		self.remote_addr.map(|addr| addr.ip())
731	}
732
733	/// Check if the request originates from a trusted proxy.
734	///
735	/// Returns `true` only if trusted proxies are configured AND the
736	/// remote address is in the trusted set.
737	fn is_from_trusted_proxy(&self) -> bool {
738		if let Some(trusted) = self.extensions.get::<TrustedProxies>()
739			&& let Some(addr) = self.remote_addr
740		{
741			return trusted.is_trusted(&addr.ip());
742		}
743		false
744	}
745
746	/// Set trusted proxy configuration for this request.
747	///
748	/// This is typically called by the server/middleware layer to configure
749	/// which proxy IPs are trusted for header forwarding.
750	pub fn set_trusted_proxies(&self, proxies: TrustedProxies) {
751		self.extensions.insert(proxies);
752	}
753
754	/// Validate Content-Type header
755	///
756	/// Checks if the Content-Type header matches the expected value.
757	/// Returns an error if the header is missing or doesn't match.
758	///
759	/// # Examples
760	///
761	/// ```
762	/// use reinhardt_http::Request;
763	/// use hyper::{Method, Version, HeaderMap, header};
764	/// use bytes::Bytes;
765	///
766	/// let mut headers = HeaderMap::new();
767	/// headers.insert(
768	///     header::CONTENT_TYPE,
769	///     "application/json".parse().unwrap()
770	/// );
771	///
772	/// let request = Request::builder()
773	///     .method(Method::POST)
774	///     .uri("/")
775	///     .version(Version::HTTP_11)
776	///     .headers(headers)
777	///     .body(Bytes::new())
778	///     .build()
779	///     .unwrap();
780	///
781	/// assert!(request.validate_content_type("application/json").is_ok());
782	/// ```
783	///
784	/// # Content-Type mismatch
785	///
786	/// ```
787	/// use reinhardt_http::Request;
788	/// use hyper::{Method, Version, HeaderMap, header};
789	/// use bytes::Bytes;
790	///
791	/// let mut headers = HeaderMap::new();
792	/// headers.insert(
793	///     header::CONTENT_TYPE,
794	///     "text/plain".parse().unwrap()
795	/// );
796	///
797	/// let request = Request::builder()
798	///     .method(Method::POST)
799	///     .uri("/")
800	///     .version(Version::HTTP_11)
801	///     .headers(headers)
802	///     .body(Bytes::new())
803	///     .build()
804	///     .unwrap();
805	///
806	/// let result = request.validate_content_type("application/json");
807	/// assert!(result.is_err());
808	/// ```
809	///
810	/// # Missing Content-Type header
811	///
812	/// ```
813	/// use reinhardt_http::Request;
814	/// use hyper::{Method, Version, HeaderMap};
815	/// use bytes::Bytes;
816	///
817	/// let request = Request::builder()
818	///     .method(Method::POST)
819	///     .uri("/")
820	///     .version(Version::HTTP_11)
821	///     .headers(HeaderMap::new())
822	///     .body(Bytes::new())
823	///     .build()
824	///     .unwrap();
825	///
826	/// let result = request.validate_content_type("application/json");
827	/// assert!(result.is_err());
828	/// ```
829	pub fn validate_content_type(&self, expected: &str) -> crate::Result<()> {
830		match self.get_header("content-type") {
831			Some(content_type) if content_type.starts_with(expected) => Ok(()),
832			Some(content_type) => Err(crate::Error::Http(format!(
833				"Invalid Content-Type: expected '{}', got '{}'",
834				expected, content_type
835			))),
836			None => Err(crate::Error::Http(
837				"Missing Content-Type header".to_string(),
838			)),
839		}
840	}
841
842	/// Parse query parameters into typed struct
843	///
844	/// Deserializes query string parameters into the specified type `T`.
845	/// Returns an error if deserialization fails.
846	///
847	/// # Examples
848	///
849	/// ```
850	/// use reinhardt_http::Request;
851	/// use hyper::{Method, Version, HeaderMap};
852	/// use bytes::Bytes;
853	/// use serde::Deserialize;
854	///
855	/// #[derive(Deserialize, Debug, PartialEq)]
856	/// struct Pagination {
857	///     page: u32,
858	///     limit: u32,
859	/// }
860	///
861	/// let request = Request::builder()
862	///     .method(Method::GET)
863	///     .uri("/api/users?page=2&limit=10")
864	///     .version(Version::HTTP_11)
865	///     .headers(HeaderMap::new())
866	///     .body(Bytes::new())
867	///     .build()
868	///     .unwrap();
869	///
870	/// let params: Pagination = request.query_as().unwrap();
871	/// assert_eq!(params, Pagination { page: 2, limit: 10 });
872	/// ```
873	///
874	/// # Type mismatch error
875	///
876	/// ```
877	/// use reinhardt_http::Request;
878	/// use hyper::{Method, Version, HeaderMap};
879	/// use bytes::Bytes;
880	/// use serde::Deserialize;
881	///
882	/// #[derive(Deserialize)]
883	/// struct Pagination {
884	///     page: u32,
885	///     limit: u32,
886	/// }
887	///
888	/// let request = Request::builder()
889	///     .method(Method::GET)
890	///     .uri("/api/users?page=invalid")
891	///     .version(Version::HTTP_11)
892	///     .headers(HeaderMap::new())
893	///     .body(Bytes::new())
894	///     .build()
895	///     .unwrap();
896	///
897	/// let result: Result<Pagination, _> = request.query_as();
898	/// assert!(result.is_err());
899	/// ```
900	pub fn query_as<T: serde::de::DeserializeOwned>(&self) -> crate::Result<T> {
901		// Convert HashMap<String, String> to Vec<(String, String)> for serde_urlencoded
902		let params: Vec<(String, String)> = self
903			.query_params
904			.iter()
905			.map(|(k, v)| (k.clone(), v.clone()))
906			.collect();
907
908		let encoded = serde_urlencoded::to_string(&params)
909			.map_err(|e| crate::Error::Http(format!("Failed to encode query parameters: {}", e)))?;
910		serde_urlencoded::from_str(&encoded)
911			.map_err(|e| crate::Error::Http(format!("Failed to parse query parameters: {}", e)))
912	}
913}
914
915#[cfg(test)]
916mod tests {
917	use super::*;
918	use bytes::Bytes;
919	use hyper::{HeaderMap, Method, Version, header};
920	use rstest::rstest;
921
922	#[rstest]
923	fn test_extract_bearer_token() {
924		let mut headers = HeaderMap::new();
925		headers.insert(
926			header::AUTHORIZATION,
927			"Bearer test_token_123".parse().unwrap(),
928		);
929
930		let request = Request::builder()
931			.method(Method::GET)
932			.uri("/")
933			.version(Version::HTTP_11)
934			.headers(headers)
935			.body(Bytes::new())
936			.build()
937			.unwrap();
938
939		let token = request.extract_bearer_token();
940		assert_eq!(token, Some("test_token_123".to_string()));
941	}
942
943	#[rstest]
944	fn test_extract_bearer_token_missing() {
945		let request = Request::builder()
946			.method(Method::GET)
947			.uri("/")
948			.version(Version::HTTP_11)
949			.headers(HeaderMap::new())
950			.body(Bytes::new())
951			.build()
952			.unwrap();
953
954		let token = request.extract_bearer_token();
955		assert_eq!(token, None);
956	}
957
958	#[rstest]
959	fn test_get_header() {
960		let mut headers = HeaderMap::new();
961		headers.insert(header::USER_AGENT, "TestClient/1.0".parse().unwrap());
962
963		let request = Request::builder()
964			.method(Method::GET)
965			.uri("/")
966			.version(Version::HTTP_11)
967			.headers(headers)
968			.body(Bytes::new())
969			.build()
970			.unwrap();
971
972		let user_agent = request.get_header("user-agent");
973		assert_eq!(user_agent, Some("TestClient/1.0".to_string()));
974	}
975
976	#[rstest]
977	fn test_get_header_missing() {
978		let request = Request::builder()
979			.method(Method::GET)
980			.uri("/")
981			.version(Version::HTTP_11)
982			.headers(HeaderMap::new())
983			.body(Bytes::new())
984			.build()
985			.unwrap();
986
987		let header = request.get_header("x-custom-header");
988		assert_eq!(header, None);
989	}
990
991	#[rstest]
992	fn test_get_client_ip_forwarded_for_with_trusted_proxy() {
993		// Arrange
994		let proxy_ip: std::net::IpAddr = "10.0.0.254".parse().unwrap();
995		let mut headers = HeaderMap::new();
996		headers.insert(
997			header::HeaderName::from_static("x-forwarded-for"),
998			"192.168.1.1, 10.0.0.1".parse().unwrap(),
999		);
1000
1001		let request = Request::builder()
1002			.method(Method::GET)
1003			.uri("/")
1004			.version(Version::HTTP_11)
1005			.headers(headers)
1006			.body(Bytes::new())
1007			.remote_addr(std::net::SocketAddr::new(proxy_ip, 8080))
1008			.build()
1009			.unwrap();
1010
1011		// Configure trusted proxies
1012		request.set_trusted_proxies(TrustedProxies::new(vec![proxy_ip]));
1013
1014		// Act & Assert
1015		let ip = request.get_client_ip();
1016		assert_eq!(ip, Some("192.168.1.1".parse().unwrap()));
1017	}
1018
1019	#[rstest]
1020	fn test_get_client_ip_forwarded_for_without_trusted_proxy() {
1021		// Arrange - proxy headers present but no trusted proxy configured
1022		let mut headers = HeaderMap::new();
1023		headers.insert(
1024			header::HeaderName::from_static("x-forwarded-for"),
1025			"192.168.1.1, 10.0.0.1".parse().unwrap(),
1026		);
1027
1028		let remote_ip: std::net::IpAddr = "10.0.0.254".parse().unwrap();
1029		let request = Request::builder()
1030			.method(Method::GET)
1031			.uri("/")
1032			.version(Version::HTTP_11)
1033			.headers(headers)
1034			.body(Bytes::new())
1035			.remote_addr(std::net::SocketAddr::new(remote_ip, 8080))
1036			.build()
1037			.unwrap();
1038
1039		// Act - no trusted proxies, should use remote_addr
1040		let ip = request.get_client_ip();
1041		assert_eq!(ip, Some(remote_ip));
1042	}
1043
1044	#[rstest]
1045	fn test_get_client_ip_real_ip_with_trusted_proxy() {
1046		// Arrange
1047		let proxy_ip: std::net::IpAddr = "10.0.0.254".parse().unwrap();
1048		let mut headers = HeaderMap::new();
1049		headers.insert(
1050			header::HeaderName::from_static("x-real-ip"),
1051			"203.0.113.5".parse().unwrap(),
1052		);
1053
1054		let request = Request::builder()
1055			.method(Method::GET)
1056			.uri("/")
1057			.version(Version::HTTP_11)
1058			.headers(headers)
1059			.body(Bytes::new())
1060			.remote_addr(std::net::SocketAddr::new(proxy_ip, 8080))
1061			.build()
1062			.unwrap();
1063
1064		request.set_trusted_proxies(TrustedProxies::new(vec![proxy_ip]));
1065
1066		// Act & Assert
1067		let ip = request.get_client_ip();
1068		assert_eq!(ip, Some("203.0.113.5".parse().unwrap()));
1069	}
1070
1071	#[rstest]
1072	fn test_get_client_ip_none() {
1073		let request = Request::builder()
1074			.method(Method::GET)
1075			.uri("/")
1076			.version(Version::HTTP_11)
1077			.headers(HeaderMap::new())
1078			.body(Bytes::new())
1079			.build()
1080			.unwrap();
1081
1082		let ip = request.get_client_ip();
1083		assert_eq!(ip, None);
1084	}
1085
1086	#[rstest]
1087	fn test_validate_content_type_valid() {
1088		let mut headers = HeaderMap::new();
1089		headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
1090
1091		let request = Request::builder()
1092			.method(Method::POST)
1093			.uri("/")
1094			.version(Version::HTTP_11)
1095			.headers(headers)
1096			.body(Bytes::new())
1097			.build()
1098			.unwrap();
1099
1100		assert!(request.validate_content_type("application/json").is_ok());
1101	}
1102
1103	#[rstest]
1104	fn test_validate_content_type_invalid() {
1105		let mut headers = HeaderMap::new();
1106		headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap());
1107
1108		let request = Request::builder()
1109			.method(Method::POST)
1110			.uri("/")
1111			.version(Version::HTTP_11)
1112			.headers(headers)
1113			.body(Bytes::new())
1114			.build()
1115			.unwrap();
1116
1117		assert!(request.validate_content_type("application/json").is_err());
1118	}
1119
1120	#[rstest]
1121	fn test_validate_content_type_missing() {
1122		let request = Request::builder()
1123			.method(Method::POST)
1124			.uri("/")
1125			.version(Version::HTTP_11)
1126			.headers(HeaderMap::new())
1127			.body(Bytes::new())
1128			.build()
1129			.unwrap();
1130
1131		assert!(request.validate_content_type("application/json").is_err());
1132	}
1133}