1use crate::handler_trait::RequestData;
4use crate::query_parser::{parse_query_pairs_to_json, parse_query_string};
5use axum::body::Body;
6use http_body_util::BodyExt;
7use serde_json::Value;
8use std::collections::HashMap;
9use std::sync::Arc;
10use std::sync::OnceLock;
11
12#[derive(Debug, Clone, Copy)]
13pub struct WithoutBodyExtractionOptions {
14 pub include_raw_query_params: bool,
15 pub include_query_params_json: bool,
16 pub include_headers: bool,
17 pub include_cookies: bool,
18}
19
20fn extract_query_params_and_raw(
21 uri: &axum::http::Uri,
22 include_raw_query_params: bool,
23 include_query_params_json: bool,
24) -> (Value, HashMap<String, Vec<String>>) {
25 let query_string = uri.query().unwrap_or("");
26 if query_string.is_empty() {
27 return (Value::Object(serde_json::Map::new()), HashMap::new());
28 }
29
30 match (include_raw_query_params, include_query_params_json) {
31 (false, false) => (Value::Null, HashMap::new()),
32 (false, true) => (
33 crate::query_parser::parse_query_string_to_json(query_string.as_bytes(), true),
34 HashMap::new(),
35 ),
36 (true, false) => {
37 let raw =
38 parse_query_string(query_string.as_bytes(), '&')
39 .into_iter()
40 .fold(HashMap::new(), |mut acc, (k, v)| {
41 acc.entry(k).or_insert_with(Vec::new).push(v);
42 acc
43 });
44 (Value::Null, raw)
45 }
46 (true, true) => {
47 let pairs = parse_query_string(query_string.as_bytes(), '&');
48 let json = parse_query_pairs_to_json(&pairs, true);
49 let raw = pairs.into_iter().fold(HashMap::new(), |mut acc, (k, v)| {
50 acc.entry(k).or_insert_with(Vec::new).push(v);
51 acc
52 });
53 (json, raw)
54 }
55 }
56}
57
58pub fn extract_query_params(uri: &axum::http::Uri) -> Value {
60 let query_string = uri.query().unwrap_or("");
61 if query_string.is_empty() {
62 Value::Object(serde_json::Map::new())
63 } else {
64 crate::query_parser::parse_query_string_to_json(query_string.as_bytes(), true)
65 }
66}
67
68pub fn extract_raw_query_params(uri: &axum::http::Uri) -> HashMap<String, Vec<String>> {
71 let query_string = uri.query().unwrap_or("");
72 if query_string.is_empty() {
73 HashMap::new()
74 } else {
75 parse_query_string(query_string.as_bytes(), '&')
76 .into_iter()
77 .fold(HashMap::new(), |mut acc, (k, v)| {
78 acc.entry(k).or_insert_with(Vec::new).push(v);
79 acc
80 })
81 }
82}
83
84pub fn extract_headers(headers: &axum::http::HeaderMap) -> HashMap<String, String> {
86 let mut map = HashMap::with_capacity(headers.len());
87 for (name, value) in headers.iter() {
88 if let Ok(val_str) = value.to_str() {
89 map.insert(name.as_str().to_string(), val_str.to_string());
91 }
92 }
93 map
94}
95
96fn extract_content_type_header(headers: &axum::http::HeaderMap) -> Arc<HashMap<String, String>> {
97 let Some(value) = headers
98 .get(axum::http::header::CONTENT_TYPE)
99 .and_then(|h| h.to_str().ok())
100 else {
101 return empty_string_map();
102 };
103
104 let mut map = HashMap::with_capacity(1);
105 map.insert("content-type".to_string(), value.to_string());
106 Arc::new(map)
107}
108
109pub fn extract_cookies(headers: &axum::http::HeaderMap) -> HashMap<String, String> {
111 let mut cookies = HashMap::new();
112
113 if let Some(cookie_str) = headers.get(axum::http::header::COOKIE).and_then(|h| h.to_str().ok()) {
114 for cookie in cookie::Cookie::split_parse(cookie_str).flatten() {
115 cookies.insert(cookie.name().to_string(), cookie.value().to_string());
116 }
117 }
118
119 cookies
120}
121
122fn empty_string_map() -> Arc<HashMap<String, String>> {
123 static EMPTY: OnceLock<Arc<HashMap<String, String>>> = OnceLock::new();
124 Arc::clone(EMPTY.get_or_init(|| Arc::new(HashMap::new())))
125}
126
127fn empty_raw_query_map() -> Arc<HashMap<String, Vec<String>>> {
128 static EMPTY: OnceLock<Arc<HashMap<String, Vec<String>>>> = OnceLock::new();
129 Arc::clone(EMPTY.get_or_init(|| Arc::new(HashMap::new())))
130}
131
132pub fn create_request_data_without_body(
136 uri: &axum::http::Uri,
137 method: &axum::http::Method,
138 headers: &axum::http::HeaderMap,
139 path_params: HashMap<String, String>,
140 options: WithoutBodyExtractionOptions,
141) -> RequestData {
142 let (query_params, raw_query_params) =
143 extract_query_params_and_raw(uri, options.include_raw_query_params, options.include_query_params_json);
144 RequestData {
145 path_params: Arc::new(path_params),
146 query_params,
147 raw_query_params: if raw_query_params.is_empty() {
148 empty_raw_query_map()
149 } else {
150 Arc::new(raw_query_params)
151 },
152 validated_params: None,
153 headers: if options.include_headers {
154 Arc::new(extract_headers(headers))
155 } else {
156 empty_string_map()
157 },
158 cookies: if options.include_cookies {
159 Arc::new(extract_cookies(headers))
160 } else {
161 empty_string_map()
162 },
163 body: Value::Null,
164 raw_body: None,
165 method: method.as_str().to_string(),
166 path: uri.path().to_string(),
167 #[cfg(feature = "di")]
168 dependencies: None,
169 }
170}
171
172pub async fn create_request_data_with_body(
178 parts: &axum::http::request::Parts,
179 path_params: HashMap<String, String>,
180 body: Body,
181 include_raw_query_params: bool,
182 include_query_params_json: bool,
183 include_headers: bool,
184 include_cookies: bool,
185) -> Result<RequestData, (axum::http::StatusCode, String)> {
186 let body_bytes = if let Some(pre_read) = parts.extensions.get::<crate::middleware::PreReadBody>() {
187 pre_read.0.clone()
188 } else {
189 body.collect()
190 .await
191 .map_err(|e| {
192 (
193 axum::http::StatusCode::BAD_REQUEST,
194 format!("Failed to read body: {}", e),
195 )
196 })?
197 .to_bytes()
198 };
199
200 let (query_params, raw_query_params) =
201 extract_query_params_and_raw(&parts.uri, include_raw_query_params, include_query_params_json);
202
203 let body_value = parts
204 .extensions
205 .get::<crate::middleware::PreParsedJson>()
206 .map(|parsed| parsed.0.clone())
207 .unwrap_or(Value::Null);
208
209 Ok(RequestData {
210 path_params: Arc::new(path_params),
211 query_params,
212 raw_query_params: if raw_query_params.is_empty() {
213 empty_raw_query_map()
214 } else {
215 Arc::new(raw_query_params)
216 },
217 validated_params: None,
218 headers: if include_headers {
219 Arc::new(extract_headers(&parts.headers))
220 } else {
221 extract_content_type_header(&parts.headers)
222 },
223 cookies: if include_cookies {
224 Arc::new(extract_cookies(&parts.headers))
225 } else {
226 empty_string_map()
227 },
228 body: body_value,
229 raw_body: if body_bytes.is_empty() { None } else { Some(body_bytes) },
230 method: parts.method.as_str().to_string(),
231 path: parts.uri.path().to_string(),
232 #[cfg(feature = "di")]
233 dependencies: None,
234 })
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use axum::http::{HeaderMap, HeaderValue, Method, Uri};
241 use futures_util::stream;
242 use serde_json::json;
243
244 const OPTIONS_ALL: WithoutBodyExtractionOptions = WithoutBodyExtractionOptions {
245 include_raw_query_params: true,
246 include_query_params_json: true,
247 include_headers: true,
248 include_cookies: true,
249 };
250
251 const OPTIONS_SKIP_HEADERS: WithoutBodyExtractionOptions = WithoutBodyExtractionOptions {
252 include_raw_query_params: true,
253 include_query_params_json: true,
254 include_headers: false,
255 include_cookies: true,
256 };
257
258 const OPTIONS_SKIP_COOKIES: WithoutBodyExtractionOptions = WithoutBodyExtractionOptions {
259 include_raw_query_params: true,
260 include_query_params_json: true,
261 include_headers: true,
262 include_cookies: false,
263 };
264
265 #[test]
266 fn test_extract_query_params_empty() {
267 let uri: Uri = "/path".parse().unwrap();
268 let result = extract_query_params(&uri);
269 assert_eq!(result, json!({}));
270 }
271
272 #[test]
273 fn test_extract_query_params_single_param() {
274 let uri: Uri = "/path?name=value".parse().unwrap();
275 let result = extract_query_params(&uri);
276 assert_eq!(result, json!({"name": "value"}));
277 }
278
279 #[test]
280 fn test_extract_query_params_multiple_params() {
281 let uri: Uri = "/path?foo=1&bar=2".parse().unwrap();
282 let result = extract_query_params(&uri);
283 assert_eq!(result, json!({"foo": 1, "bar": 2}));
284 }
285
286 #[test]
287 fn test_extract_query_params_array_params() {
288 let uri: Uri = "/path?tags=rust&tags=http".parse().unwrap();
289 let result = extract_query_params(&uri);
290 assert_eq!(result, json!({"tags": ["rust", "http"]}));
291 }
292
293 #[test]
294 fn test_extract_query_params_mixed_array_and_single() {
295 let uri: Uri = "/path?tags=rust&tags=web&id=123".parse().unwrap();
296 let result = extract_query_params(&uri);
297 assert_eq!(result, json!({"tags": ["rust", "web"], "id": 123}));
298 }
299
300 #[test]
301 fn test_extract_query_params_url_encoded() {
302 let uri: Uri = "/path?email=test%40example.com&name=john+doe".parse().unwrap();
303 let result = extract_query_params(&uri);
304 assert_eq!(result, json!({"email": "test@example.com", "name": "john doe"}));
305 }
306
307 #[test]
308 fn test_extract_query_params_boolean_values() {
309 let uri: Uri = "/path?active=true&enabled=false".parse().unwrap();
310 let result = extract_query_params(&uri);
311 assert_eq!(result, json!({"active": true, "enabled": false}));
312 }
313
314 #[test]
315 fn test_extract_query_params_null_value() {
316 let uri: Uri = "/path?value=null".parse().unwrap();
317 let result = extract_query_params(&uri);
318 assert_eq!(result, json!({"value": null}));
319 }
320
321 #[test]
322 fn test_extract_query_params_empty_string_value() {
323 let uri: Uri = "/path?key=".parse().unwrap();
324 let result = extract_query_params(&uri);
325 assert_eq!(result, json!({"key": ""}));
326 }
327
328 #[test]
329 fn test_extract_raw_query_params_empty() {
330 let uri: Uri = "/path".parse().unwrap();
331 let result = extract_raw_query_params(&uri);
332 assert!(result.is_empty());
333 }
334
335 #[test]
336 fn test_extract_raw_query_params_single() {
337 let uri: Uri = "/path?name=value".parse().unwrap();
338 let result = extract_raw_query_params(&uri);
339 assert_eq!(result.get("name"), Some(&vec!["value".to_string()]));
340 }
341
342 #[test]
343 fn test_extract_raw_query_params_multiple_values() {
344 let uri: Uri = "/path?tag=rust&tag=http".parse().unwrap();
345 let result = extract_raw_query_params(&uri);
346 assert_eq!(result.get("tag"), Some(&vec!["rust".to_string(), "http".to_string()]));
347 }
348
349 #[test]
350 fn test_extract_raw_query_params_mixed() {
351 let uri: Uri = "/path?id=123&tags=rust&tags=web&active=true".parse().unwrap();
352 let result = extract_raw_query_params(&uri);
353 assert_eq!(result.get("id"), Some(&vec!["123".to_string()]));
354 assert_eq!(result.get("tags"), Some(&vec!["rust".to_string(), "web".to_string()]));
355 assert_eq!(result.get("active"), Some(&vec!["true".to_string()]));
356 }
357
358 #[test]
359 fn test_extract_raw_query_params_url_encoded() {
360 let uri: Uri = "/path?email=test%40example.com".parse().unwrap();
361 let result = extract_raw_query_params(&uri);
362 assert_eq!(result.get("email"), Some(&vec!["test@example.com".to_string()]));
363 }
364
365 #[test]
366 fn test_extract_headers_empty() {
367 let headers = HeaderMap::new();
368 let result = extract_headers(&headers);
369 assert!(result.is_empty());
370 }
371
372 #[test]
373 fn test_extract_headers_single() {
374 let mut headers = HeaderMap::new();
375 headers.insert("content-type", HeaderValue::from_static("application/json"));
376 let result = extract_headers(&headers);
377 assert_eq!(result.get("content-type"), Some(&"application/json".to_string()));
378 }
379
380 #[test]
381 fn test_extract_headers_multiple() {
382 let mut headers = HeaderMap::new();
383 headers.insert("content-type", HeaderValue::from_static("application/json"));
384 headers.insert("user-agent", HeaderValue::from_static("test-agent"));
385 headers.insert("authorization", HeaderValue::from_static("Bearer token123"));
386
387 let result = extract_headers(&headers);
388 assert_eq!(result.len(), 3);
389 assert_eq!(result.get("content-type"), Some(&"application/json".to_string()));
390 assert_eq!(result.get("user-agent"), Some(&"test-agent".to_string()));
391 assert_eq!(result.get("authorization"), Some(&"Bearer token123".to_string()));
392 }
393
394 #[test]
395 fn test_extract_headers_case_insensitive() {
396 let mut headers = HeaderMap::new();
397 headers.insert("Content-Type", HeaderValue::from_static("text/html"));
398 headers.insert("USER-Agent", HeaderValue::from_static("chrome"));
399
400 let result = extract_headers(&headers);
401 assert_eq!(result.get("content-type"), Some(&"text/html".to_string()));
402 assert_eq!(result.get("user-agent"), Some(&"chrome".to_string()));
403 }
404
405 #[test]
406 fn test_extract_headers_with_dashes() {
407 let mut headers = HeaderMap::new();
408 headers.insert("x-custom-header", HeaderValue::from_static("custom-value"));
409 headers.insert("x-request-id", HeaderValue::from_static("req-12345"));
410
411 let result = extract_headers(&headers);
412 assert_eq!(result.get("x-custom-header"), Some(&"custom-value".to_string()));
413 assert_eq!(result.get("x-request-id"), Some(&"req-12345".to_string()));
414 }
415
416 #[test]
417 fn test_extract_cookies_no_cookie_header() {
418 let headers = HeaderMap::new();
419 let result = extract_cookies(&headers);
420 assert!(result.is_empty());
421 }
422
423 #[test]
424 fn test_extract_cookies_single() {
425 let mut headers = HeaderMap::new();
426 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("session=abc123"));
427
428 let result = extract_cookies(&headers);
429 assert_eq!(result.get("session"), Some(&"abc123".to_string()));
430 }
431
432 #[test]
433 fn test_extract_cookies_multiple() {
434 let mut headers = HeaderMap::new();
435 headers.insert(
436 axum::http::header::COOKIE,
437 HeaderValue::from_static("session=abc123; user_id=42; theme=dark"),
438 );
439
440 let result = extract_cookies(&headers);
441 assert_eq!(result.len(), 3);
442 assert_eq!(result.get("session"), Some(&"abc123".to_string()));
443 assert_eq!(result.get("user_id"), Some(&"42".to_string()));
444 assert_eq!(result.get("theme"), Some(&"dark".to_string()));
445 }
446
447 #[test]
448 fn test_extract_cookies_with_spaces() {
449 let mut headers = HeaderMap::new();
450 headers.insert(
451 axum::http::header::COOKIE,
452 HeaderValue::from_static("session = abc123 ; theme = light"),
453 );
454
455 let result = extract_cookies(&headers);
456 assert!(result.len() >= 1);
457 }
458
459 #[test]
460 fn test_extract_cookies_empty_value() {
461 let mut headers = HeaderMap::new();
462 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("empty="));
463
464 let result = extract_cookies(&headers);
465 assert_eq!(result.get("empty"), Some(&String::new()));
466 }
467
468 #[test]
469 fn test_create_request_data_without_body_minimal() {
470 let uri: Uri = "/test".parse().unwrap();
471 let method = Method::GET;
472 let headers = HeaderMap::new();
473 let path_params = HashMap::new();
474
475 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_ALL);
476
477 assert_eq!(result.method, "GET");
478 assert_eq!(result.path, "/test");
479 assert!(result.path_params.is_empty());
480 assert_eq!(result.query_params, json!({}));
481 assert!(result.raw_query_params.is_empty());
482 assert!(result.headers.is_empty());
483 assert!(result.cookies.is_empty());
484 assert_eq!(result.body, Value::Null);
485 assert!(result.raw_body.is_none());
486 }
487
488 #[test]
489 fn test_create_request_data_without_body_with_path_params() {
490 let uri: Uri = "/users/42".parse().unwrap();
491 let method = Method::GET;
492 let headers = HeaderMap::new();
493 let mut path_params = HashMap::new();
494 path_params.insert("user_id".to_string(), "42".to_string());
495
496 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_ALL);
497
498 assert_eq!(result.path_params.get("user_id"), Some(&"42".to_string()));
499 }
500
501 #[test]
502 fn test_create_request_data_without_body_with_query_params() {
503 let uri: Uri = "/search?q=rust&limit=10".parse().unwrap();
504 let method = Method::GET;
505 let headers = HeaderMap::new();
506 let path_params = HashMap::new();
507
508 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_ALL);
509
510 assert_eq!(result.query_params, json!({"q": "rust", "limit": 10}));
511 assert_eq!(result.raw_query_params.get("q"), Some(&vec!["rust".to_string()]));
512 assert_eq!(result.raw_query_params.get("limit"), Some(&vec!["10".to_string()]));
513 }
514
515 #[test]
516 fn test_create_request_data_without_body_with_headers() {
517 let uri: Uri = "/test".parse().unwrap();
518 let method = Method::POST;
519 let mut headers = HeaderMap::new();
520 headers.insert("content-type", HeaderValue::from_static("application/json"));
521 headers.insert("authorization", HeaderValue::from_static("Bearer token"));
522 let path_params = HashMap::new();
523
524 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_ALL);
525
526 assert_eq!(
527 result.headers.get("content-type"),
528 Some(&"application/json".to_string())
529 );
530 assert_eq!(result.headers.get("authorization"), Some(&"Bearer token".to_string()));
531 }
532
533 #[test]
534 fn test_create_request_data_without_body_with_cookies() {
535 let uri: Uri = "/test".parse().unwrap();
536 let method = Method::GET;
537 let mut headers = HeaderMap::new();
538 headers.insert(
539 axum::http::header::COOKIE,
540 HeaderValue::from_static("session=xyz; theme=dark"),
541 );
542 let path_params = HashMap::new();
543
544 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_ALL);
545
546 assert_eq!(result.cookies.get("session"), Some(&"xyz".to_string()));
547 assert_eq!(result.cookies.get("theme"), Some(&"dark".to_string()));
548 }
549
550 #[test]
551 fn test_create_request_data_without_body_skip_headers() {
552 let uri: Uri = "/test".parse().unwrap();
553 let method = Method::GET;
554 let mut headers = HeaderMap::new();
555 headers.insert("authorization", HeaderValue::from_static("Bearer token"));
556 let path_params = HashMap::new();
557
558 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_SKIP_HEADERS);
559
560 assert!(result.headers.is_empty());
561 }
562
563 #[test]
564 fn test_create_request_data_without_body_skip_cookies() {
565 let uri: Uri = "/test".parse().unwrap();
566 let method = Method::GET;
567 let mut headers = HeaderMap::new();
568 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("session=xyz"));
569 let path_params = HashMap::new();
570
571 let result = create_request_data_without_body(&uri, &method, &headers, path_params, OPTIONS_SKIP_COOKIES);
572
573 assert!(result.cookies.is_empty());
574 }
575
576 #[test]
577 fn test_create_request_data_without_body_different_methods() {
578 let uri: Uri = "/resource".parse().unwrap();
579 let headers = HeaderMap::new();
580 let path_params = HashMap::new();
581
582 for method in &[Method::GET, Method::POST, Method::PUT, Method::DELETE, Method::PATCH] {
583 let result = create_request_data_without_body(&uri, method, &headers, path_params.clone(), OPTIONS_ALL);
584 assert_eq!(result.method, method.as_str());
585 }
586 }
587
588 #[tokio::test]
589 async fn test_create_request_data_with_body_empty() {
590 let parts = axum::http::request::Request::builder()
591 .method(Method::POST)
592 .uri("/test")
593 .body(Body::empty())
594 .unwrap()
595 .into_parts();
596
597 let body = Body::empty();
598 let path_params = HashMap::new();
599
600 let result = create_request_data_with_body(&parts.0, path_params, body, true, true, true, true)
601 .await
602 .unwrap();
603
604 assert_eq!(result.method, "POST");
605 assert_eq!(result.path, "/test");
606 assert_eq!(result.body, Value::Null);
607 assert!(result.raw_body.is_none());
608 }
609
610 #[tokio::test]
611 async fn test_create_request_data_with_body_json() {
612 let request_body = Body::from(r#"{"key":"value"}"#);
613 let request = axum::http::request::Request::builder()
614 .method(Method::POST)
615 .uri("/test")
616 .body(Body::empty())
617 .unwrap();
618
619 let (parts, _) = request.into_parts();
620 let path_params = HashMap::new();
621
622 let result = create_request_data_with_body(&parts, path_params, request_body, true, true, true, true)
623 .await
624 .unwrap();
625
626 assert_eq!(result.method, "POST");
627 assert_eq!(result.body, Value::Null);
628 assert!(result.raw_body.is_some());
629 assert_eq!(result.raw_body.as_ref().unwrap().as_ref(), br#"{"key":"value"}"#);
630 }
631
632 #[tokio::test]
633 async fn test_create_request_data_with_body_with_query_params() {
634 let request_body = Body::from("test");
635 let request = axum::http::request::Request::builder()
636 .method(Method::POST)
637 .uri("/test?foo=bar&baz=qux")
638 .body(Body::empty())
639 .unwrap();
640
641 let (parts, _) = request.into_parts();
642 let path_params = HashMap::new();
643
644 let result = create_request_data_with_body(&parts, path_params, request_body, true, true, true, true)
645 .await
646 .unwrap();
647
648 assert_eq!(result.query_params, json!({"foo": "bar", "baz": "qux"}));
649 }
650
651 #[tokio::test]
652 async fn test_create_request_data_with_body_with_headers() {
653 let request_body = Body::from("test");
654 let request = axum::http::request::Request::builder()
655 .method(Method::POST)
656 .uri("/test")
657 .header("content-type", "application/json")
658 .header("x-request-id", "req123")
659 .body(Body::empty())
660 .unwrap();
661
662 let (parts, _) = request.into_parts();
663 let path_params = HashMap::new();
664
665 let result = create_request_data_with_body(&parts, path_params, request_body, true, true, true, true)
666 .await
667 .unwrap();
668
669 assert_eq!(
670 result.headers.get("content-type"),
671 Some(&"application/json".to_string())
672 );
673 assert_eq!(result.headers.get("x-request-id"), Some(&"req123".to_string()));
674 }
675
676 #[tokio::test]
677 async fn test_create_request_data_with_body_with_cookies() {
678 let request_body = Body::from("test");
679 let request = axum::http::request::Request::builder()
680 .method(Method::POST)
681 .uri("/test")
682 .header("cookie", "session=xyz; user=123")
683 .body(Body::empty())
684 .unwrap();
685
686 let (parts, _) = request.into_parts();
687 let path_params = HashMap::new();
688
689 let result = create_request_data_with_body(&parts, path_params, request_body, true, true, true, true)
690 .await
691 .unwrap();
692
693 assert_eq!(result.cookies.get("session"), Some(&"xyz".to_string()));
694 assert_eq!(result.cookies.get("user"), Some(&"123".to_string()));
695 }
696
697 #[tokio::test]
698 async fn test_create_request_data_with_body_large_payload() {
699 let large_json = json!({
700 "data": (0..100).map(|i| json!({"id": i, "value": format!("item-{}", i)})).collect::<Vec<_>>()
701 });
702 let json_str = serde_json::to_string(&large_json).unwrap();
703 let request_body = Body::from(json_str.clone());
704
705 let request = axum::http::request::Request::builder()
706 .method(Method::POST)
707 .uri("/test")
708 .body(Body::empty())
709 .unwrap();
710
711 let (parts, _) = request.into_parts();
712 let path_params = HashMap::new();
713
714 let result = create_request_data_with_body(&parts, path_params, request_body, true, true, true, true)
715 .await
716 .unwrap();
717
718 assert!(result.raw_body.is_some());
719 assert_eq!(result.raw_body.as_ref().unwrap().as_ref(), json_str.as_bytes());
720 }
721
722 #[tokio::test]
723 async fn test_create_request_data_with_body_preserves_all_fields() {
724 let request_body = Body::from("request data");
725 let request = axum::http::request::Request::builder()
726 .method(Method::PUT)
727 .uri("/users/42?action=update")
728 .header("authorization", "Bearer token")
729 .header("cookie", "session=abc")
730 .body(Body::empty())
731 .unwrap();
732
733 let (parts, _) = request.into_parts();
734 let mut path_params = HashMap::new();
735 path_params.insert("user_id".to_string(), "42".to_string());
736
737 let result = create_request_data_with_body(&parts, path_params, request_body, true, true, true, true)
738 .await
739 .unwrap();
740
741 assert_eq!(result.method, "PUT");
742 assert_eq!(result.path, "/users/42");
743 assert_eq!(result.path_params.get("user_id"), Some(&"42".to_string()));
744 assert_eq!(result.query_params, json!({"action": "update"}));
745 assert!(result.headers.contains_key("authorization"));
746 assert!(result.cookies.contains_key("session"));
747 assert!(result.raw_body.is_some());
748 }
749
750 #[test]
751 fn test_arc_wrapping_for_cheap_cloning() {
752 let uri: Uri = "/test".parse().unwrap();
753 let method = Method::GET;
754 let mut headers = HeaderMap::new();
755 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("session=abc"));
756 let mut path_params = HashMap::new();
757 path_params.insert("id".to_string(), "1".to_string());
758
759 let request_data = create_request_data_without_body(&uri, &method, &headers, path_params.clone(), OPTIONS_ALL);
760
761 let cloned = request_data.clone();
762
763 assert!(Arc::ptr_eq(&request_data.path_params, &cloned.path_params));
764 assert!(Arc::ptr_eq(&request_data.headers, &cloned.headers));
765 assert!(Arc::ptr_eq(&request_data.cookies, &cloned.cookies));
766 assert!(Arc::ptr_eq(&request_data.raw_query_params, &cloned.raw_query_params));
767 }
768
769 #[tokio::test]
770 async fn create_request_data_with_body_returns_bad_request_when_body_stream_errors() {
771 let request = axum::http::Request::builder()
772 .method(Method::POST)
773 .uri("/path")
774 .body(Body::empty())
775 .unwrap();
776 let (parts, _) = request.into_parts();
777
778 let stream = stream::once(async move {
779 Err::<bytes::Bytes, std::io::Error>(std::io::Error::new(std::io::ErrorKind::Other, "boom"))
780 });
781 let body = Body::from_stream(stream);
782
783 let err = create_request_data_with_body(&parts, HashMap::new(), body, true, true, true, true)
784 .await
785 .unwrap_err();
786 assert_eq!(err.0, axum::http::StatusCode::BAD_REQUEST);
787 assert!(err.1.contains("Failed to read body:"));
788 }
789}