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}