Skip to main content

reinhardt_http/request/
body.rs

1use super::Request;
2use bytes::Bytes;
3#[cfg(feature = "parsers")]
4use reinhardt_core::parsers::parser::{ParsedData, Parser};
5#[cfg(feature = "parsers")]
6use std::collections::HashMap;
7use std::sync::atomic::Ordering;
8
9impl Request {
10	/// Get a reference to the request body
11	///
12	/// This is a non-consuming accessor that can be called multiple times.
13	/// Useful for testing and inspection purposes.
14	///
15	/// # Examples
16	///
17	/// ```
18	/// use reinhardt_http::Request;
19	/// use hyper::Method;
20	/// use bytes::Bytes;
21	///
22	/// let body = Bytes::from("test body");
23	/// let request = Request::builder()
24	///     .method(Method::POST)
25	///     .uri("/")
26	///     .body(body.clone())
27	///     .build()
28	///     .unwrap();
29	///
30	/// assert_eq!(request.body(), &body);
31	/// ```
32	pub fn body(&self) -> &Bytes {
33		&self.body
34	}
35
36	/// Parse the request body as JSON
37	///
38	/// # Examples
39	///
40	/// ```
41	/// use reinhardt_http::Request;
42	/// use hyper::Method;
43	/// use bytes::Bytes;
44	/// use serde::Deserialize;
45	///
46	/// #[derive(Deserialize, Debug, PartialEq)]
47	/// struct User {
48	///     name: String,
49	///     age: u32,
50	/// }
51	///
52	/// let json_body = r#"{"name": "Alice", "age": 30}"#;
53	/// let mut headers = hyper::HeaderMap::new();
54	/// headers.insert(hyper::header::CONTENT_TYPE, "application/json".parse().unwrap());
55	/// let request = Request::builder()
56	///     .method(Method::POST)
57	///     .uri("/api/users")
58	///     .headers(headers)
59	///     .body(Bytes::from(json_body))
60	///     .build()
61	///     .unwrap();
62	///
63	/// let user: User = request.json().unwrap();
64	/// assert_eq!(user.name, "Alice");
65	/// assert_eq!(user.age, 30);
66	/// ```
67	pub fn json<T: serde::de::DeserializeOwned>(&self) -> crate::Result<T> {
68		use crate::Error;
69
70		// Check Content-Type before parsing body
71		if let Some(content_type) = self
72			.headers
73			.get(hyper::header::CONTENT_TYPE)
74			.and_then(|h| h.to_str().ok())
75		{
76			if !content_type.starts_with("application/json") {
77				return Err(Error::Http(format!(
78					"Unsupported Media Type: expected 'application/json', got '{}'",
79					content_type
80				)));
81			}
82		} else if !self.body.is_empty() {
83			return Err(Error::Http(
84				"Missing Content-Type header: expected 'application/json'".to_string(),
85			));
86		}
87
88		serde_json::from_slice(&self.body).map_err(|e| Error::Serialization(e.to_string()))
89	}
90
91	/// Set parsers for request body parsing
92	///
93	/// # Examples
94	///
95	/// ```
96	/// use reinhardt_http::Request;
97	/// use hyper::Method;
98	///
99	/// let request = Request::builder()
100	///     .method(Method::POST)
101	///     .uri("/")
102	///     .build()
103	///     .unwrap();
104	///
105	// Set up parsers (empty vec for this example)
106	/// let request = request.with_parsers(vec![]);
107	/// assert_eq!(request.method, Method::POST);
108	/// ```
109	#[cfg(feature = "parsers")]
110	pub fn with_parsers(mut self, parsers: Vec<Box<dyn Parser>>) -> Self {
111		self.parsers = parsers;
112		self
113	}
114
115	/// Read and consume the request body
116	/// This marks the body as consumed and subsequent parse attempts will fail
117	///
118	/// # Examples
119	///
120	/// ```
121	/// use reinhardt_http::Request;
122	/// use hyper::Method;
123	/// use bytes::Bytes;
124	///
125	/// let request = Request::builder()
126	///     .method(Method::POST)
127	///     .uri("/")
128	///     .body(Bytes::from("request body"))
129	///     .build()
130	///     .unwrap();
131	///
132	/// let body = request.read_body().unwrap();
133	/// assert_eq!(body, Bytes::from("request body"));
134	///
135	// Second read fails because body is consumed
136	/// assert!(request.read_body().is_err());
137	/// ```
138	pub fn read_body(&self) -> crate::Result<Bytes> {
139		use crate::Error;
140		if self.body_consumed.load(Ordering::SeqCst) {
141			return Err(Error::Http(
142				"Request body has already been consumed".to_string(),
143			));
144		}
145		self.body_consumed.store(true, Ordering::SeqCst);
146		Ok(self.body.clone())
147	}
148
149	/// Get POST data (form-encoded data)
150	/// Returns data only if using FormParser or MultiPartParser
151	///
152	/// # Examples
153	///
154	/// ```no_run
155	/// use reinhardt_http::Request;
156	/// use hyper::Method;
157	///
158	/// async fn example() {
159	///     let request = Request::builder()
160	///         .method(Method::POST)
161	///         .uri("/")
162	///         .build()
163	///         .unwrap();
164	///
165	///     // Without parsers, returns empty HashMap
166	///     let post_data = request.post().await.unwrap();
167	///     assert!(post_data.is_empty());
168	/// }
169	/// ```
170	#[cfg(feature = "parsers")]
171	pub async fn post(&self) -> crate::Result<HashMap<String, Vec<String>>> {
172		use crate::Error;
173		if self.body_consumed.load(Ordering::SeqCst) {
174			return Err(Error::Http(
175				"Request body has already been consumed".to_string(),
176			));
177		}
178
179		// Check if we have form parsers
180		let has_form_parser = self.parsers.iter().any(|p| {
181			let media_types = p.media_types();
182			media_types.contains(&"application/x-www-form-urlencoded".to_string())
183				|| media_types.contains(&"multipart/form-data".to_string())
184		});
185
186		if !has_form_parser {
187			// No form parser, return empty
188			return Ok(HashMap::new());
189		}
190
191		// Parse the body
192		let parsed = self.parse_body_internal().await?;
193
194		match parsed {
195			ParsedData::Form(form) => {
196				// Convert HashMap<String, String> to HashMap<String, Vec<String>>
197				Ok(form.into_iter().map(|(k, v)| (k, vec![v])).collect())
198			}
199			ParsedData::MultiPart { fields, .. } => {
200				// Convert fields to the expected format
201				Ok(fields.into_iter().map(|(k, v)| (k, vec![v])).collect())
202			}
203			_ => Ok(HashMap::new()),
204		}
205	}
206
207	/// Get parsed data
208	/// This performs lazy parsing - only parses once and caches the result
209	///
210	/// # Examples
211	///
212	/// ```no_run
213	/// use reinhardt_http::Request;
214	/// use hyper::Method;
215	///
216	/// async fn example() {
217	///     let request = Request::builder()
218	///         .method(Method::POST)
219	///         .uri("/")
220	///         .build()
221	///         .unwrap();
222	///
223	///     // Without parsers, this will fail
224	///     assert!(request.data().await.is_err());
225	/// }
226	/// ```
227	#[cfg(feature = "parsers")]
228	pub async fn data(&self) -> crate::Result<ParsedData> {
229		use crate::Error;
230		if self.body_consumed.load(Ordering::SeqCst) {
231			return Err(Error::Http(
232				"Request body has already been consumed".to_string(),
233			));
234		}
235
236		self.parse_body_internal().await
237	}
238
239	/// Internal method to parse body with caching
240	#[cfg(feature = "parsers")]
241	pub(super) async fn parse_body_internal(&self) -> crate::Result<ParsedData> {
242		// Check cache first
243		{
244			let cache = self.parsed_data.lock().unwrap();
245			if let Some(data) = &*cache {
246				return Ok(data.clone());
247			}
248		}
249
250		// Parse body
251		let content_type = self
252			.headers
253			.get(hyper::header::CONTENT_TYPE)
254			.and_then(|h| h.to_str().ok());
255
256		// Try each parser
257		for parser in &self.parsers {
258			if parser.can_parse(content_type) {
259				match parser
260					.parse(content_type, self.body.clone(), &self.headers)
261					.await
262				{
263					Ok(data) => {
264						// Cache the result
265						let mut cache = self.parsed_data.lock().unwrap();
266						*cache = Some(data.clone());
267						return Ok(data);
268					}
269					Err(e) => {
270						use crate::Error;
271						return Err(Error::Http(format!("Parse error: {}", e)));
272					}
273				}
274			}
275		}
276
277		// No suitable parser found
278		use crate::Error;
279		Err(Error::Http(
280			"No suitable parser found for content type".to_string(),
281		))
282	}
283}