1use crate::body::RequestBody;
4use crate::extensions::Extensions;
5use crate::headers::HeaderMap;
6use crate::query::{QueryPairs, QueryView, parse as parse_query};
7use crate::{ByteStr, Method};
8use bytes::Bytes;
9use serde::{Deserialize, Serialize};
10use smallvec::SmallVec;
11use std::collections::HashMap;
12use std::sync::{Arc, OnceLock};
13
14pub type RouteParams = SmallVec<[(&'static str, Bytes); 4]>;
22
23pub trait RouteParamsExt {
28 fn get_str(&self, name: &str) -> Option<&str>;
30
31 fn get_bytes(&self, name: &str) -> Option<&Bytes>;
33}
34
35impl RouteParamsExt for RouteParams {
36 #[inline]
37 fn get_str(&self, name: &str) -> Option<&str> {
38 self.get_bytes(name)
39 .and_then(|v| std::str::from_utf8(v).ok())
40 }
41
42 #[inline]
43 fn get_bytes(&self, name: &str) -> Option<&Bytes> {
44 self.iter().find(|(k, _)| *k == name).map(|(_, v)| v)
45 }
46}
47
48#[derive(Debug, Default)]
54pub struct QueryCache(OnceLock<QueryPairs>);
55
56impl Clone for QueryCache {
57 fn clone(&self) -> Self {
63 Self(OnceLock::new())
64 }
65}
66
67#[derive(Debug, Clone)]
72pub struct HttpRequest {
73 pub method: Method,
79 pub path: ByteStr,
85 pub headers: HeaderMap,
92 pub body: Bytes,
97 pub path_params: RouteParams,
98 pub extensions: Extensions,
103 query_cache: QueryCache,
105}
106
107impl HttpRequest {
108 #[inline]
113 pub fn new(method: impl Into<Method>, path: impl Into<ByteStr>) -> Self {
114 Self {
115 method: method.into(),
116 path: path.into(),
117 headers: HeaderMap::new(),
118 body: Bytes::new(),
119 path_params: RouteParams::new(),
120 extensions: Extensions::new(),
121 query_cache: QueryCache::default(),
122 }
123 }
124
125 #[inline]
127 pub fn with_extensions_capacity(
128 method: impl Into<Method>,
129 path: impl Into<ByteStr>,
130 capacity: usize,
131 ) -> Self {
132 Self {
133 method: method.into(),
134 path: path.into(),
135 headers: HeaderMap::new(),
136 body: Bytes::new(),
137 path_params: RouteParams::new(),
138 extensions: Extensions::with_capacity(capacity),
139 query_cache: QueryCache::default(),
140 }
141 }
142
143 #[inline]
148 pub fn with_bytes_body(
149 method: impl Into<Method>,
150 path: impl Into<ByteStr>,
151 body: Bytes,
152 ) -> Self {
153 Self {
154 method: method.into(),
155 path: path.into(),
156 headers: HeaderMap::new(),
157 body,
158 path_params: RouteParams::new(),
159 extensions: Extensions::new(),
160 query_cache: QueryCache::default(),
161 }
162 }
163
164 #[inline]
166 pub fn set_body_bytes(&mut self, bytes: Bytes) {
167 self.body = bytes;
168 }
169
170 #[inline]
172 pub fn body_bytes(&self) -> Bytes {
173 self.body.clone()
174 }
175
176 #[inline]
178 pub fn body_slice(&self) -> &[u8] {
179 &self.body
180 }
181
182 #[inline]
184 pub fn body_ref(&self) -> &[u8] {
185 &self.body
186 }
187
188 #[inline]
190 pub fn path_str(&self) -> &str {
191 self.path.as_str()
192 }
193
194 #[inline]
200 pub fn path_only(&self) -> &str {
201 self.path
202 .split_once('?')
203 .map_or(self.path.as_str(), |(p, _)| p)
204 }
205
206 #[inline]
208 pub fn request_body(&self) -> RequestBody {
209 RequestBody::from_bytes(self.body_bytes())
210 }
211
212 #[inline]
217 pub fn has_bytes_body(&self) -> bool {
218 !self.body.is_empty()
219 }
220
221 #[inline]
223 pub fn method_str(&self) -> &str {
224 self.method.as_str()
225 }
226
227 #[inline]
229 pub fn set_body(&mut self, body: Vec<u8>) {
230 self.body = Bytes::from(body);
231 }
232
233 #[inline]
235 pub fn from_parts(
236 method: impl Into<Method>,
237 path: impl Into<ByteStr>,
238 headers: HashMap<String, String>,
239 body: Vec<u8>,
240 path_params: HashMap<String, String>,
241 query_params: HashMap<String, String>,
242 ) -> Self {
243 let path_params: RouteParams = path_params
247 .into_iter()
248 .map(|(k, v)| (crate::param_intern::intern(&k), Bytes::from(v)))
249 .collect();
250 let _ = query_params;
254 Self {
255 method: method.into(),
256 path: path.into(),
257 headers: headers.into(),
258 body: Bytes::from(body),
259 path_params,
260 extensions: Extensions::new(),
261 query_cache: QueryCache::default(),
262 }
263 }
264
265 #[inline]
276 pub fn insert_extension<T: Send + Sync + 'static>(&mut self, value: T) {
277 self.extensions.insert(value);
278 }
279
280 #[inline]
284 pub fn insert_extension_arc<T: Send + Sync + 'static>(&mut self, value: Arc<T>) {
285 self.extensions.insert_arc(value);
286 }
287
288 #[inline]
292 pub fn extension<T: Send + Sync + 'static>(&self) -> Option<&T> {
293 self.extensions.get::<T>()
294 }
295
296 #[inline]
298 pub fn extension_arc<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
299 self.extensions.get_arc::<T>()
300 }
301
302 #[inline]
313 pub fn json<T: for<'de> Deserialize<'de>>(&self) -> Result<T, crate::Error> {
314 crate::json::from_slice(self.body_ref())
315 .map_err(|e| crate::Error::Deserialization(e.to_string()))
316 }
317
318 pub fn form<T: for<'de> Deserialize<'de>>(&self) -> Result<T, crate::Error> {
320 crate::form::parse_form(self.body_ref())
321 }
322
323 pub fn form_map(&self) -> Result<HashMap<String, String>, crate::Error> {
325 crate::form::parse_form_map(self.body_ref())
326 }
327
328 pub fn multipart(&self) -> Result<Vec<crate::form::FormField>, crate::Error> {
330 let content_type = self
333 .headers
334 .get("Content-Type")
335 .ok_or_else(|| crate::Error::BadRequest("Missing Content-Type header".to_string()))?;
336
337 let parser = crate::form::MultipartParser::from_content_type(content_type)?;
338 parser.parse(self.body_ref())
339 }
340
341 #[inline]
343 pub fn param(&self, name: &str) -> Option<&str> {
344 self.param_bytes(name)
345 .and_then(|v| std::str::from_utf8(v).ok())
346 }
347
348 #[inline]
350 pub fn param_bytes(&self, name: &str) -> Option<&Bytes> {
351 self.path_params
352 .iter()
353 .find(|(k, _)| *k == name)
354 .map(|(_, v)| v)
355 }
356
357 pub fn push_param(&mut self, name: &str, value: impl Into<Bytes>) {
367 self.path_params
368 .push((crate::param_intern::intern(name), value.into()));
369 }
370
371 #[inline]
373 pub fn set_params(&mut self, params: RouteParams) {
374 self.path_params = params;
375 }
376
377 #[inline]
379 pub fn query_string(&self) -> Option<&str> {
380 self.path.as_str().split_once('?').map(|(_, q)| q)
381 }
382
383 #[inline]
389 pub fn query(&self) -> QueryView<'_> {
390 let pairs = self
391 .query_cache
392 .0
393 .get_or_init(|| match self.query_string() {
394 Some(q) => parse_query(q),
395 None => QueryPairs::new(),
396 });
397 QueryView::new(pairs)
398 }
399
400 pub fn push_query_param(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) {
406 let pair = [(name.as_ref(), value.as_ref())];
407 let Ok(encoded) = serde_urlencoded::to_string(pair) else {
408 return;
409 };
410 let separator = if self.path.contains('?') { '&' } else { '?' };
411 self.path = ByteStr::from(format!("{}{separator}{encoded}", self.path.as_str()));
412 self.query_cache = QueryCache::default();
413 }
414
415 #[inline]
417 pub fn query_param(&self, name: &str) -> Option<&str> {
418 self.query().get(name)
419 }
420}
421
422#[derive(Debug, Clone, Default)]
427pub struct LazyHeaders {
428 inner: Option<HashMap<String, String>>,
429}
430
431impl LazyHeaders {
432 #[inline(always)]
434 pub const fn new() -> Self {
435 Self { inner: None }
436 }
437
438 #[inline]
440 pub fn with_capacity(cap: usize) -> Self {
441 Self {
442 inner: Some(HashMap::with_capacity(cap)),
443 }
444 }
445
446 #[inline]
448 pub fn insert(&mut self, key: String, value: String) -> Option<String> {
449 self.inner
450 .get_or_insert_with(HashMap::new)
451 .insert(key, value)
452 }
453
454 #[inline]
456 pub fn get(&self, key: &str) -> Option<&String> {
457 self.inner.as_ref()?.get(key)
458 }
459
460 #[inline]
462 pub fn contains_key(&self, key: &str) -> bool {
463 self.inner.as_ref().is_some_and(|m| m.contains_key(key))
464 }
465
466 #[inline]
468 pub fn len(&self) -> usize {
469 self.inner.as_ref().map_or(0, |m| m.len())
470 }
471
472 #[inline]
474 pub fn is_empty(&self) -> bool {
475 self.inner.as_ref().is_none_or(|m| m.is_empty())
476 }
477
478 #[inline]
480 pub fn iter(&self) -> impl Iterator<Item = (&String, &String)> {
481 self.inner.iter().flat_map(|m| m.iter())
482 }
483
484 #[inline]
486 pub fn to_hashmap(&self) -> HashMap<String, String> {
487 self.inner.clone().unwrap_or_default()
488 }
489
490 #[inline]
492 pub fn remove(&mut self, key: &str) -> Option<String> {
493 self.inner.as_mut()?.remove(key)
494 }
495
496 #[inline]
498 pub fn entry(&mut self, key: String) -> std::collections::hash_map::Entry<'_, String, String> {
499 self.inner.get_or_insert_with(HashMap::new).entry(key)
500 }
501
502 #[inline]
504 pub fn extend<I: IntoIterator<Item = (String, String)>>(&mut self, iter: I) {
505 let map = self.inner.get_or_insert_with(HashMap::new);
506 map.extend(iter);
507 }
508
509 #[inline]
511 pub fn clear(&mut self) {
512 if let Some(ref mut map) = self.inner {
513 map.clear();
514 }
515 }
516
517 #[inline]
519 pub fn clone_inner(&self) -> Option<HashMap<String, String>> {
520 self.inner.clone()
521 }
522}
523
524impl From<HashMap<String, String>> for LazyHeaders {
525 #[inline]
526 fn from(map: HashMap<String, String>) -> Self {
527 Self { inner: Some(map) }
528 }
529}
530
531impl From<LazyHeaders> for HashMap<String, String> {
532 #[inline]
533 fn from(lazy: LazyHeaders) -> Self {
534 lazy.inner.unwrap_or_default()
535 }
536}
537
538impl<'a> IntoIterator for &'a LazyHeaders {
540 type Item = (&'a String, &'a String);
541 type IntoIter = std::iter::Flatten<std::option::Iter<'a, HashMap<String, String>>>;
542
543 fn into_iter(self) -> Self::IntoIter {
544 self.inner.iter().flatten()
545 }
546}
547
548#[derive(Debug)]
561pub struct HttpResponse {
562 pub status: u16,
563 pub headers: LazyHeaders,
565 pub cookies: Vec<String>,
567 pub body: Bytes,
572}
573
574pub const DEFAULT_RESPONSE_CAPACITY: usize = 512;
576
577impl HttpResponse {
578 #[inline(always)]
584 pub fn new(status: u16) -> Self {
585 Self {
586 status,
587 headers: LazyHeaders::new(),
588 cookies: Vec::new(),
589 body: Bytes::new(),
590 }
591 }
592
593 #[inline]
605 pub fn with_capacity(status: u16, _capacity: usize) -> Self {
606 Self {
607 status,
608 headers: LazyHeaders::with_capacity(8),
609 cookies: Vec::new(),
610 body: Bytes::new(),
611 }
612 }
613
614 #[inline(always)]
616 pub fn ok() -> Self {
617 Self::new(200)
618 }
619
620 #[inline]
622 pub fn ok_preallocated() -> Self {
623 Self::with_capacity(200, DEFAULT_RESPONSE_CAPACITY)
624 }
625
626 #[inline(always)]
628 pub fn created() -> Self {
629 Self::new(201)
630 }
631
632 #[inline(always)]
634 pub fn no_content() -> Self {
635 Self::new(204)
636 }
637
638 #[inline(always)]
640 pub fn bad_request() -> Self {
641 Self::new(400)
642 }
643
644 #[inline(always)]
646 pub fn not_found() -> Self {
647 Self::new(404)
648 }
649
650 #[inline(always)]
652 pub fn internal_server_error() -> Self {
653 Self::new(500)
654 }
655
656 pub fn with_body(mut self, body: Vec<u8>) -> Self {
658 self.body = Bytes::from(body);
659 self
660 }
661
662 #[inline]
667 pub fn with_bytes_body(mut self, bytes: Bytes) -> Self {
668 self.body = bytes;
669 self
670 }
671
672 #[inline]
674 pub fn with_static_body(mut self, body: &'static [u8]) -> Self {
675 self.body = Bytes::from_static(body);
676 self
677 }
678
679 #[inline]
681 pub fn body_bytes(&self) -> Bytes {
682 self.body.clone()
683 }
684
685 #[inline]
687 pub fn into_body_bytes(self) -> Bytes {
688 self.body
689 }
690
691 #[inline]
693 pub fn body_slice(&self) -> &[u8] {
694 &self.body
695 }
696
697 #[inline]
699 pub fn body_ref(&self) -> &[u8] {
700 &self.body
701 }
702
703 #[inline]
705 pub fn body_len(&self) -> usize {
706 self.body.len()
707 }
708
709 #[inline]
714 pub fn has_bytes_body(&self) -> bool {
715 !self.body.is_empty()
716 }
717
718 #[inline]
731 pub fn with_json<T: Serialize>(mut self, value: &T) -> Result<Self, crate::Error> {
732 let vec =
733 crate::json::to_vec(value).map_err(|e| crate::Error::Serialization(e.to_string()))?;
734 self.body = Bytes::from(vec);
735 self.headers
736 .insert("Content-Type".to_string(), "application/json".to_string());
737 Ok(self)
738 }
739
740 pub fn with_header(mut self, key: String, value: String) -> Self {
741 self.headers.insert(key, value);
742 self
743 }
744
745 #[inline]
747 pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
748 self.headers = LazyHeaders::from(headers);
749 self
750 }
751
752 #[inline]
754 pub fn with_status_and_headers(status: u16, headers: HashMap<String, String>) -> Self {
755 Self {
756 status,
757 headers: LazyHeaders::from(headers),
758 cookies: Vec::new(),
759 body: Bytes::new(),
760 }
761 }
762
763 #[inline]
767 pub fn from_parts(status: u16, headers: HashMap<String, String>, body: Vec<u8>) -> Self {
768 Self {
769 status,
770 headers: LazyHeaders::from(headers),
771 cookies: Vec::new(),
772 body: Bytes::from(body),
773 }
774 }
775
776 pub fn accepted() -> Self {
789 Self::new(202)
790 }
791
792 pub fn unauthorized() -> Self {
801 Self::new(401)
802 }
803
804 pub fn forbidden() -> Self {
813 Self::new(403)
814 }
815
816 pub fn conflict() -> Self {
825 Self::new(409)
826 }
827
828 pub fn service_unavailable() -> Self {
837 Self::new(503)
838 }
839
840 pub fn json<T: Serialize>(value: &T) -> Result<Self, crate::Error> {
851 Self::ok().with_json(value)
852 }
853
854 pub fn html(content: impl Into<String>) -> Self {
864 Self::ok()
865 .with_header(
866 "Content-Type".to_string(),
867 "text/html; charset=utf-8".to_string(),
868 )
869 .with_body(content.into().into_bytes())
870 }
871
872 pub fn text(content: impl Into<String>) -> Self {
882 Self::ok()
883 .with_header(
884 "Content-Type".to_string(),
885 "text/plain; charset=utf-8".to_string(),
886 )
887 .with_body(content.into().into_bytes())
888 }
889
890 pub fn redirect(url: impl Into<String>) -> Self {
900 Self::new(302).with_header("Location".to_string(), url.into())
901 }
902
903 pub fn redirect_permanent(url: impl Into<String>) -> Self {
912 Self::new(301).with_header("Location".to_string(), url.into())
913 }
914
915 pub fn see_other(url: impl Into<String>) -> Self {
925 Self::new(303).with_header("Location".to_string(), url.into())
926 }
927
928 pub fn empty() -> Self {
937 Self::no_content()
938 }
939
940 pub fn content_type(self, content_type: impl Into<String>) -> Self {
949 self.with_header("Content-Type".to_string(), content_type.into())
950 }
951
952 pub fn cache_control(self, directive: impl Into<String>) -> Self {
960 self.with_header("Cache-Control".to_string(), directive.into())
961 }
962
963 pub fn no_cache(self) -> Self {
971 self.cache_control("no-store, no-cache, must-revalidate")
972 }
973
974 pub fn cookie(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
985 self.cookies
986 .push(format!("{}={}", name.into(), value.into()));
987 self
988 }
989
990 pub fn clear_cookie(mut self, name: impl Into<String>, path: impl Into<String>) -> Self {
998 self.cookies
999 .push(format!("{}=; Path={}; Max-Age=0", name.into(), path.into(),));
1000 self
1001 }
1002
1003 pub fn body_string(&self) -> String {
1005 String::from_utf8_lossy(self.body_ref()).to_string()
1006 }
1007
1008 pub fn is_success(&self) -> bool {
1010 (200..300).contains(&self.status)
1011 }
1012
1013 pub fn is_redirect(&self) -> bool {
1015 (300..400).contains(&self.status)
1016 }
1017
1018 pub fn is_client_error(&self) -> bool {
1020 (400..500).contains(&self.status)
1021 }
1022
1023 pub fn is_server_error(&self) -> bool {
1025 (500..600).contains(&self.status)
1026 }
1027}
1028
1029#[derive(Debug)]
1031pub struct Json<T: Serialize>(pub T);
1032
1033impl<T: Serialize> Json<T> {
1034 pub fn into_response(self) -> Result<HttpResponse, crate::Error> {
1035 HttpResponse::ok().with_json(&self.0)
1036 }
1037}
1038
1039#[cfg(test)]
1040mod tests {
1041 use super::*;
1042
1043 #[test]
1044 fn new_accepts_str_and_string_and_method() {
1045 let a = HttpRequest::new("GET", "/a".to_string());
1048 let b = HttpRequest::new("POST", "/b".to_string());
1049 let c = HttpRequest::new(Method::Put, "/c".to_string());
1050 assert_eq!(a.method, Method::Get);
1051 assert_eq!(b.method, Method::Post);
1052 assert_eq!(c.method, Method::Put);
1053 }
1054
1055 #[test]
1056 fn from_parts_ignores_query_params_entirely() {
1057 let mut query = HashMap::new();
1061 query.insert("page".to_string(), "2".to_string());
1062
1063 let req = HttpRequest::from_parts(
1064 "GET",
1065 "/items",
1066 HashMap::new(),
1067 Vec::new(),
1068 HashMap::new(),
1069 query,
1070 );
1071
1072 assert_eq!(req.query_string(), None);
1073 assert_eq!(req.query_param("page"), None);
1074 assert_eq!(req.query().len(), 0);
1075 }
1076
1077 #[test]
1078 fn with_capacity_ignores_its_capacity_argument() {
1079 let small = HttpResponse::with_capacity(200, 0);
1083 let large = HttpResponse::with_capacity(200, 1 << 20);
1084
1085 assert_eq!(small.status, large.status);
1086 assert_eq!(small.body.len(), large.body.len());
1087 assert!(small.body.is_empty());
1088 assert_eq!(small.headers.len(), large.headers.len());
1089 }
1090
1091 #[test]
1092 fn params_read_back_as_str_and_bytes() {
1093 let mut req = HttpRequest::new("GET", "/users/42/posts/7");
1094 let mut params = RouteParams::new();
1095 params.push((
1096 crate::param_intern::intern("user_id"),
1097 Bytes::from_static(b"42"),
1098 ));
1099 params.push((
1100 crate::param_intern::intern("post_id"),
1101 Bytes::from_static(b"7"),
1102 ));
1103 req.set_params(params);
1104
1105 assert_eq!(req.param("user_id"), Some("42"));
1106 assert_eq!(req.param("post_id"), Some("7"));
1107 assert_eq!(req.param("nope"), None);
1108 assert_eq!(req.param_bytes("user_id").map(|b| b.len()), Some(2));
1109 assert_eq!(
1110 req.param("user_id").and_then(|v| v.parse::<u32>().ok()),
1111 Some(42)
1112 );
1113 }
1114
1115 #[test]
1116 fn four_params_stay_inline() {
1117 let mut params = RouteParams::new();
1118 for name in ["a", "b", "c", "d"] {
1119 params.push((crate::param_intern::intern(name), Bytes::from_static(b"x")));
1120 }
1121 assert!(!params.spilled(), "four params must not allocate");
1122 }
1123
1124 #[test]
1125 fn path_is_a_bytestr_and_still_compares_and_prints_as_a_str() {
1126 let req = HttpRequest::new("GET", "/users/42?a=1");
1127 assert_eq!(req.path_str(), "/users/42?a=1");
1128 assert!(req.path == "/users/42?a=1");
1129 assert_eq!(format!("{}", req.path), "/users/42?a=1");
1130 assert!(req.path.starts_with("/users"));
1132 }
1133
1134 #[test]
1135 fn request_body_is_bytes_and_the_shadow_field_is_gone() {
1136 let mut req = HttpRequest::new("POST", "/x");
1137 req.set_body(b"hello".to_vec());
1138 assert_eq!(req.body_slice(), b"hello");
1139 assert_eq!(req.body_bytes(), Bytes::from_static(b"hello"));
1142 assert!(req.has_bytes_body());
1143
1144 req.set_body_bytes(Bytes::from_static(b"world"));
1145 assert_eq!(req.body_slice(), b"world");
1146 assert_eq!(req.body_ref(), b"world");
1147 }
1148
1149 #[test]
1150 fn cloning_a_body_does_not_copy_it() {
1151 let big = Bytes::from(vec![7u8; 64 * 1024]);
1152 let mut req = HttpRequest::new("POST", "/x");
1153 req.set_body_bytes(big.clone());
1154 let copy = req.clone();
1155 assert_eq!(copy.body.as_ptr(), req.body.as_ptr());
1157 }
1158
1159 #[test]
1160 fn response_body_is_bytes() {
1161 let mut resp = HttpResponse::new(200);
1162 resp.body = Bytes::from_static(b"{}");
1163 assert_eq!(resp.body_slice(), b"{}");
1164 assert_eq!(resp.body_len(), 2);
1165 }
1166
1167 #[test]
1168 fn method_compares_against_str_and_reports_itself_as_str() {
1169 let req = HttpRequest::new("DELETE", "/x".to_string());
1170 assert!(req.method == "DELETE");
1171 assert!(req.method != "GET");
1172 assert_eq!(req.method_str(), "DELETE");
1173
1174 let odd = HttpRequest::new("PURGE", "/x".to_string());
1176 assert_eq!(odd.method_str(), "PURGE");
1177 assert!(odd.method == "PURGE");
1178 }
1179
1180 #[test]
1181 fn test_http_request_new() {
1182 let req = HttpRequest::new("GET", "/test".to_string());
1183 assert_eq!(req.method, "GET");
1184 assert_eq!(req.path, "/test");
1185 assert!(req.headers.is_empty());
1186 assert!(req.body.is_empty());
1187 }
1188
1189 #[test]
1190 fn test_http_request_with_body() {
1191 let mut req = HttpRequest::new("POST", "/api".to_string());
1192 req.body = Bytes::from(vec![1, 2, 3, 4]);
1193 assert_eq!(req.body.len(), 4);
1194 }
1195
1196 #[test]
1197 fn test_http_request_json_deserialization() {
1198 #[derive(Deserialize, Debug, PartialEq)]
1199 struct TestData {
1200 name: String,
1201 age: u32,
1202 }
1203
1204 let mut req = HttpRequest::new("POST", "/api".to_string());
1205 req.body = Bytes::from(
1206 serde_json::to_vec(&serde_json::json!({
1207 "name": "John",
1208 "age": 30
1209 }))
1210 .unwrap(),
1211 );
1212
1213 let data: TestData = req.json().unwrap();
1214 assert_eq!(data.name, "John");
1215 assert_eq!(data.age, 30);
1216 }
1217
1218 #[test]
1219 fn test_http_request_param() {
1220 let mut req = HttpRequest::new("GET", "/users/123".to_string());
1221 req.push_param("id", "123");
1222
1223 assert_eq!(req.param("id"), Some("123"));
1224 assert_eq!(req.param("name"), None);
1225 }
1226
1227 #[test]
1228 fn test_http_request_query() {
1229 let req = HttpRequest::new("GET", "/users?sort=asc");
1230
1231 assert_eq!(req.query_param("sort"), Some("asc"));
1232 assert_eq!(req.query_param("limit"), None);
1233 }
1234
1235 #[test]
1236 fn test_http_request_clone() {
1237 let req1 = HttpRequest::new("GET", "/test".to_string());
1238 let req2 = req1.clone();
1239
1240 assert_eq!(req1.method, req2.method);
1241 assert_eq!(req1.path, req2.path);
1242 }
1243
1244 #[test]
1245 fn test_http_response_ok() {
1246 let res = HttpResponse::ok();
1247 assert_eq!(res.status, 200);
1248 }
1249
1250 #[test]
1251 fn test_http_response_created() {
1252 let res = HttpResponse::created();
1253 assert_eq!(res.status, 201);
1254 }
1255
1256 #[test]
1257 fn test_http_response_no_content() {
1258 let res = HttpResponse::no_content();
1259 assert_eq!(res.status, 204);
1260 }
1261
1262 #[test]
1263 fn test_http_response_bad_request() {
1264 let res = HttpResponse::bad_request();
1265 assert_eq!(res.status, 400);
1266 }
1267
1268 #[test]
1269 fn test_http_response_not_found() {
1270 let res = HttpResponse::not_found();
1271 assert_eq!(res.status, 404);
1272 }
1273
1274 #[test]
1275 fn test_http_response_internal_server_error() {
1276 let res = HttpResponse::internal_server_error();
1277 assert_eq!(res.status, 500);
1278 }
1279
1280 #[test]
1281 fn test_http_response_with_body() {
1282 let body = b"Hello, World!".to_vec();
1283 let res = HttpResponse::ok().with_body(body.clone());
1284 assert_eq!(res.body, body);
1285 }
1286
1287 #[test]
1288 fn test_http_response_with_json() {
1289 #[derive(Serialize)]
1290 struct TestData {
1291 message: String,
1292 }
1293
1294 let data = TestData {
1295 message: "test".to_string(),
1296 };
1297
1298 let res = HttpResponse::ok().with_json(&data).unwrap();
1299 assert!(!res.body_ref().is_empty());
1300 assert_eq!(
1301 res.headers.get("Content-Type"),
1302 Some(&"application/json".to_string())
1303 );
1304 }
1305
1306 #[test]
1307 fn test_http_response_with_header() {
1308 let res = HttpResponse::ok().with_header("X-Custom".to_string(), "value".to_string());
1309
1310 assert_eq!(res.headers.get("X-Custom"), Some(&"value".to_string()));
1311 }
1312
1313 #[test]
1314 fn test_http_response_multiple_headers() {
1315 let res = HttpResponse::ok()
1316 .with_header("X-Header-1".to_string(), "value1".to_string())
1317 .with_header("X-Header-2".to_string(), "value2".to_string());
1318
1319 assert_eq!(res.headers.len(), 2);
1320 }
1321
1322 #[test]
1323 fn test_json_helper() {
1324 #[derive(Serialize)]
1325 struct Data {
1326 value: i32,
1327 }
1328
1329 let json = Json(Data { value: 42 });
1330 let response = json.into_response().unwrap();
1331
1332 assert_eq!(response.status, 200);
1333 assert!(!response.body_ref().is_empty());
1334 }
1335
1336 #[test]
1337 fn test_http_request_with_headers() {
1338 let mut req = HttpRequest::new("GET", "/api".to_string());
1339 req.headers
1340 .insert("Authorization", "Bearer token".to_string());
1341 req.headers
1342 .insert("Content-Type", "application/json".to_string());
1343
1344 assert_eq!(req.headers.len(), 2);
1345 }
1346
1347 #[test]
1348 fn test_http_request_from_parts_headermap_roundtrip() {
1349 let mut headers = HashMap::new();
1352 headers.insert("Content-Type".to_string(), "application/json".to_string());
1353 headers.insert("X-Custom".to_string(), "abc".to_string());
1354
1355 let req = HttpRequest::from_parts(
1356 "GET",
1357 "/api".to_string(),
1358 headers,
1359 Vec::new(),
1360 HashMap::new(),
1361 HashMap::new(),
1362 );
1363
1364 assert_eq!(req.headers.len(), 2);
1365 assert_eq!(req.headers.get("content-type"), Some("application/json"));
1367 assert_eq!(req.headers.get("Content-Type"), Some("application/json"));
1368 assert!(req.headers.contains_key("x-custom"));
1369 }
1370
1371 #[test]
1372 fn test_http_request_json_invalid() {
1373 #[derive(Deserialize)]
1374 #[allow(dead_code)]
1375 struct TestData {
1376 name: String,
1377 }
1378
1379 let mut req = HttpRequest::new("POST", "/api".to_string());
1380 req.body = Bytes::from_static(b"invalid json");
1381
1382 let result: Result<TestData, crate::Error> = req.json();
1383 assert!(result.is_err());
1384 }
1385
1386 #[test]
1387 fn test_http_response_new_custom_status() {
1388 let res = HttpResponse::new(418); assert_eq!(res.status, 418);
1390 }
1391
1392 #[test]
1393 fn test_http_response_with_json_complex() {
1394 #[derive(Serialize)]
1395 struct ComplexData {
1396 nested: Vec<HashMap<String, i32>>,
1397 }
1398
1399 let mut map = HashMap::new();
1400 map.insert("key".to_string(), 123);
1401
1402 let data = ComplexData { nested: vec![map] };
1403
1404 let res = HttpResponse::ok().with_json(&data);
1405 assert!(res.is_ok());
1406 }
1407}