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