Skip to main content

silent/core/
response.rs

1use std::fmt;
2use std::fmt::{Display, Formatter};
3
4use crate::core::res_body::{ResBody, full};
5use crate::headers::{ContentType, Header, HeaderMap, HeaderMapExt};
6use crate::{Result, SilentError, State, StatusCode, header};
7use http::{Extensions, Version};
8use http_body::{Body, SizeHint};
9use serde::Serialize;
10use serde_json::Value;
11
12/// 响应体
13/// ```
14/// use silent::Response;
15/// let req = Response::empty();
16/// ```
17pub struct Response<B: Body = ResBody> {
18    /// The HTTP status code.
19    pub(crate) status: StatusCode,
20    /// The HTTP version.
21    pub(crate) version: Version,
22    /// The HTTP headers.
23    pub(crate) headers: HeaderMap,
24    pub(crate) body: B,
25    pub(crate) extensions: Extensions,
26    pub(crate) state: State,
27}
28
29impl fmt::Debug for Response {
30    #[inline]
31    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
32        writeln!(f, "{:?} {}\n{:?}", self.version, self.status, self.headers)
33    }
34}
35
36impl Display for Response {
37    #[inline]
38    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
39        fmt::Debug::fmt(self, f)
40    }
41}
42
43impl Response {
44    /// 创建空响应体
45    pub fn empty() -> Self {
46        Self {
47            status: StatusCode::OK,
48            headers: HeaderMap::new(),
49            version: Version::default(),
50            body: ResBody::None,
51            extensions: Extensions::default(),
52            state: State::default(),
53        }
54    }
55    /// 获取响应状态码
56    #[inline]
57    pub fn status(&self) -> StatusCode {
58        self.status
59    }
60    /// 取出响应体(将内部body置为空)
61    #[inline]
62    pub fn take_body(&mut self) -> ResBody {
63        std::mem::replace(&mut self.body, ResBody::None)
64    }
65    #[inline]
66    /// 设置响应重定向
67    pub fn redirect(url: &str) -> Result<Self> {
68        let mut res = Self::empty();
69        res.status = StatusCode::MOVED_PERMANENTLY;
70        res.headers.insert(
71            header::LOCATION,
72            url.parse().map_err(|e| {
73                SilentError::business_error(
74                    StatusCode::INTERNAL_SERVER_ERROR,
75                    format!("redirect error: {e}"),
76                )
77            })?,
78        );
79        Ok(res)
80    }
81    #[inline]
82    /// 生成文本响应
83    pub fn text(text: &str) -> Self {
84        let mut res = Self::empty();
85        res.set_typed_header(ContentType::text_utf8());
86        res.set_body(full(text.as_bytes().to_vec()));
87        res
88    }
89    #[inline]
90    /// 生成html响应
91    pub fn html(html: &str) -> Self {
92        let mut res = Self::empty();
93        res.set_typed_header(ContentType::html());
94        res.set_body(full(html.as_bytes().to_vec()));
95        res
96    }
97    #[inline]
98    /// 生成json响应
99    pub fn json<T: Serialize>(json: &T) -> Self {
100        let mut res = Self::empty();
101        res.set_typed_header(ContentType::json());
102        res.set_body(full(serde_json::to_vec(json).unwrap()));
103        res
104    }
105}
106
107impl<B: Body> Response<B> {
108    /// 设置响应状态
109    #[inline]
110    pub fn set_status(&mut self, status: StatusCode) {
111        self.status = status;
112    }
113    /// 包含响应状态
114    #[inline]
115    pub fn with_status(mut self, status: StatusCode) -> Self {
116        self.status = status;
117        self
118    }
119    /// 设置响应body
120    #[inline]
121    pub fn set_body(&mut self, body: B) {
122        self.body = body;
123    }
124    /// 包含响应body
125    #[inline]
126    pub fn with_body(mut self, body: B) -> Self {
127        self.body = body;
128        self
129    }
130    /// 获取响应体
131    #[inline]
132    pub fn body(&self) -> &B {
133        &self.body
134    }
135    /// 设置响应header
136    #[inline]
137    pub fn set_header(&mut self, key: header::HeaderName, value: header::HeaderValue) {
138        self.headers.insert(key, value);
139    }
140    /// 包含响应header
141    #[inline]
142    pub fn with_header(mut self, key: header::HeaderName, value: header::HeaderValue) -> Self {
143        self.headers.insert(key, value);
144        self
145    }
146    #[inline]
147    /// 获取extensions
148    pub fn extensions(&self) -> &Extensions {
149        &self.extensions
150    }
151    #[inline]
152    /// 获取extensions_mut
153    pub fn extensions_mut(&mut self) -> &mut Extensions {
154        &mut self.extensions
155    }
156
157    /// 获取状态
158    #[inline]
159    pub fn get_state<T: Send + Sync + 'static>(&self) -> Result<&T> {
160        self.state.get::<T>().ok_or(SilentError::ConfigNotFound)
161    }
162
163    /// 获取状态(Uncheck)
164    #[inline]
165    pub fn get_state_uncheck<T: Send + Sync + 'static>(&self) -> &T {
166        self.state.get::<T>().unwrap()
167    }
168
169    /// 获取状态容器
170    #[inline]
171    pub fn state(&self) -> &State {
172        &self.state
173    }
174
175    /// 获取可变状态容器
176    #[inline]
177    pub fn state_mut(&mut self) -> &mut State {
178        &mut self.state
179    }
180
181    /// 获取配置
182    #[deprecated(since = "2.16.0", note = "请使用 get_state 代替")]
183    #[inline]
184    pub fn get_config<T: Send + Sync + 'static>(&self) -> Result<&T> {
185        self.get_state::<T>()
186    }
187
188    /// 获取配置(Uncheck)
189    #[deprecated(since = "2.16.0", note = "请使用 get_state_uncheck 代替")]
190    #[inline]
191    pub fn get_config_uncheck<T: Send + Sync + 'static>(&self) -> &T {
192        self.get_state_uncheck::<T>()
193    }
194
195    /// 获取全局配置
196    #[deprecated(since = "2.16.0", note = "请使用 state() 代替")]
197    #[inline]
198    pub fn configs(&self) -> &State {
199        self.state()
200    }
201
202    /// 获取可变全局配置
203    #[deprecated(since = "2.16.0", note = "请使用 state_mut() 代替")]
204    #[inline]
205    pub fn configs_mut(&mut self) -> &mut State {
206        self.state_mut()
207    }
208    #[inline]
209    /// 设置响应header
210    pub fn headers(&self) -> &HeaderMap {
211        &self.headers
212    }
213    #[inline]
214    /// 设置响应header
215    pub fn headers_mut(&mut self) -> &mut HeaderMap {
216        &mut self.headers
217    }
218    #[inline]
219    /// 获取响应体长度
220    pub fn content_length(&self) -> SizeHint {
221        self.body.size_hint()
222    }
223    #[inline]
224    /// 设置响应header
225    pub fn set_typed_header<H>(&mut self, header: H)
226    where
227        H: Header,
228    {
229        self.headers.typed_insert(header);
230    }
231    #[inline]
232    /// 包含响应header
233    pub fn with_typed_header<H>(mut self, header: H) -> Self
234    where
235        H: Header,
236    {
237        self.headers.typed_insert(header);
238        self
239    }
240
241    /// move response to from another response
242    pub fn copy_from_response(&mut self, res: Response<B>) {
243        self.headers.extend(res.headers);
244        self.status = res.status;
245        self.extensions.extend(res.extensions);
246        self.set_body(res.body);
247    }
248}
249
250impl<S: Serialize> From<S> for Response {
251    fn from(value: S) -> Self {
252        match serde_json::to_value(&value).unwrap() {
253            Value::String(value) => Response::empty()
254                .with_typed_header(ContentType::json())
255                .with_body(full(value.as_bytes().to_vec())),
256            Value::Null => Response::empty().with_status(StatusCode::NO_CONTENT),
257            _ => Self::json(&value),
258        }
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265    use crate::core::response::Response;
266
267    // 基础构造函数测试
268
269    #[test]
270    fn test_response_empty() {
271        let res = Response::empty();
272        assert_eq!(res.status(), StatusCode::OK);
273        assert_eq!(res.version, Version::default());
274        assert_eq!(res.headers().len(), 0);
275    }
276
277    #[test]
278    fn test_response_text() {
279        let res = Response::text("Hello, World!");
280        assert_eq!(res.status(), StatusCode::OK);
281        assert!(res.headers().get("content-type").is_some());
282        let content_type = res.headers().get("content-type").unwrap();
283        assert!(content_type.to_str().unwrap().contains("text/plain"));
284    }
285
286    #[test]
287    fn test_response_html() {
288        let res = Response::html("<html><body>Test</body></html>");
289        assert_eq!(res.status(), StatusCode::OK);
290        assert!(res.headers().get("content-type").is_some());
291        let content_type = res.headers().get("content-type").unwrap();
292        assert!(content_type.to_str().unwrap().contains("text/html"));
293    }
294
295    #[test]
296    fn test_response_json() {
297        let data = serde_json::json!({"key": "value"});
298        let res = Response::json(&data);
299        assert_eq!(res.status(), StatusCode::OK);
300        assert!(res.headers().get("content-type").is_some());
301        let content_type = res.headers().get("content-type").unwrap();
302        assert!(content_type.to_str().unwrap().contains("application/json"));
303    }
304
305    #[test]
306    fn test_response_json_with_struct() {
307        #[derive(Serialize)]
308        struct TestData {
309            name: String,
310            count: i32,
311        }
312        let data = TestData {
313            name: "test".to_string(),
314            count: 42,
315        };
316        let res = Response::json(&data);
317        assert_eq!(res.status(), StatusCode::OK);
318        assert!(res.headers().get("content-type").is_some());
319    }
320
321    #[test]
322    fn test_response_redirect_valid_url() {
323        let res = Response::redirect("https://example.com");
324        assert!(res.is_ok());
325        let res = res.unwrap();
326        assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
327        assert!(res.headers().get("location").is_some());
328        let location = res.headers().get("location").unwrap();
329        assert_eq!(location.to_str().unwrap(), "https://example.com");
330    }
331
332    #[test]
333    fn test_response_redirect_relative_url() {
334        let res = Response::redirect("/new-location");
335        assert!(res.is_ok());
336        let res = res.unwrap();
337        assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
338        assert!(res.headers().get("location").is_some());
339    }
340
341    #[test]
342    fn test_response_redirect_empty_url() {
343        let res = Response::redirect("");
344        // 空字符串可以被 HeaderValue 接受
345        assert!(res.is_ok());
346        let res = res.unwrap();
347        assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
348    }
349
350    #[test]
351    fn test_response_redirect_invalid_url() {
352        let res = Response::redirect("not a valid url");
353        // HeaderValue 可以接受任何字符串
354        assert!(res.is_ok());
355        let res = res.unwrap();
356        assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
357    }
358
359    // 状态管理测试
360
361    #[test]
362    fn test_response_status() {
363        let res = Response::empty();
364        assert_eq!(res.status(), StatusCode::OK);
365    }
366
367    #[test]
368    fn test_response_set_status() {
369        let mut res = Response::empty();
370        res.set_status(StatusCode::NOT_FOUND);
371        assert_eq!(res.status(), StatusCode::NOT_FOUND);
372    }
373
374    #[test]
375    fn test_response_set_status_multiple() {
376        let mut res = Response::empty();
377        res.set_status(StatusCode::NOT_FOUND);
378        assert_eq!(res.status(), StatusCode::NOT_FOUND);
379        res.set_status(StatusCode::INTERNAL_SERVER_ERROR);
380        assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
381    }
382
383    #[test]
384    fn test_response_with_status() {
385        let res = Response::empty().with_status(StatusCode::CREATED);
386        assert_eq!(res.status(), StatusCode::CREATED);
387    }
388
389    #[test]
390    fn test_response_with_status_chain() {
391        let res = Response::empty()
392            .with_status(StatusCode::CREATED)
393            .with_status(StatusCode::ACCEPTED);
394        assert_eq!(res.status(), StatusCode::ACCEPTED);
395    }
396
397    // 主体管理测试
398
399    #[test]
400    fn test_response_body() {
401        let res = Response::text("test");
402        assert!(!res.body().is_end_stream());
403    }
404
405    #[test]
406    fn test_response_set_body() {
407        let mut res = Response::empty();
408        let new_body = full(b"new body".to_vec());
409        res.set_body(new_body);
410        assert!(!res.body().is_end_stream());
411    }
412
413    #[test]
414    fn test_response_with_body() {
415        let body = full(b"test body".to_vec());
416        let res = Response::empty().with_body(body);
417        assert!(!res.body().is_end_stream());
418    }
419
420    #[test]
421    fn test_response_take_body() {
422        let mut res = Response::text("test");
423        let body = res.take_body();
424        assert!(!body.is_end_stream());
425        assert!(res.body().is_end_stream()); // After take, body should be None
426    }
427
428    #[test]
429    fn test_response_take_body_twice() {
430        let mut res = Response::text("test");
431        let _body1 = res.take_body();
432        let body2 = res.take_body();
433        assert!(body2.is_end_stream());
434    }
435
436    #[test]
437    fn test_response_content_length() {
438        let res = Response::text("Hello, World!");
439        let hint = res.content_length();
440        assert!(hint.lower() > 0);
441    }
442
443    #[test]
444    fn test_response_content_length_empty() {
445        let res = Response::empty();
446        let hint = res.content_length();
447        assert_eq!(hint.lower(), 0);
448    }
449
450    // 头部管理测试
451
452    #[test]
453    fn test_response_headers() {
454        let res = Response::text("test");
455        assert!(res.headers().get("content-type").is_some());
456        assert_eq!(res.headers().len(), 1);
457    }
458
459    #[test]
460    fn test_response_headers_empty() {
461        let res = Response::empty();
462        assert_eq!(res.headers().len(), 0);
463    }
464
465    #[test]
466    fn test_response_headers_mut() {
467        let mut res = Response::empty();
468        res.headers_mut()
469            .insert("x-custom-header", "custom-value".parse().unwrap());
470        assert_eq!(res.headers().len(), 1);
471        assert!(res.headers().get("x-custom-header").is_some());
472    }
473
474    #[test]
475    fn test_response_set_header() {
476        let mut res = Response::empty();
477        res.set_header(header::CONTENT_TYPE, "application/json".parse().unwrap());
478        assert!(res.headers().get("content-type").is_some());
479    }
480
481    #[test]
482    fn test_response_set_header_multiple() {
483        let mut res = Response::empty();
484        res.set_header(header::CONTENT_TYPE, "text/plain".parse().unwrap());
485        res.set_header(header::CACHE_CONTROL, "no-cache".parse().unwrap());
486        assert_eq!(res.headers().len(), 2);
487    }
488
489    #[test]
490    fn test_response_with_header() {
491        let res = Response::empty().with_header(header::CONTENT_TYPE, "text/html".parse().unwrap());
492        assert!(res.headers().get("content-type").is_some());
493    }
494
495    #[test]
496    fn test_response_with_header_chain() {
497        let res = Response::empty()
498            .with_header(header::CONTENT_TYPE, "text/plain".parse().unwrap())
499            .with_header(header::CACHE_CONTROL, "no-cache".parse().unwrap());
500        assert_eq!(res.headers().len(), 2);
501    }
502
503    #[test]
504    fn test_response_set_typed_header() {
505        let mut res = Response::empty();
506        res.set_typed_header(ContentType::json());
507        assert!(res.headers().get("content-type").is_some());
508    }
509
510    #[test]
511    fn test_response_with_typed_header() {
512        let res = Response::empty().with_typed_header(ContentType::text_utf8());
513        assert!(res.headers().get("content-type").is_some());
514    }
515
516    // 扩展测试
517
518    #[test]
519    fn test_response_extensions() {
520        let res = Response::empty();
521        assert_eq!(res.extensions().len(), 0);
522    }
523
524    #[test]
525    fn test_response_extensions_mut() {
526        let mut res = Response::empty();
527        res.extensions_mut().insert("test_key");
528        assert_eq!(res.extensions().len(), 1);
529    }
530
531    #[test]
532    fn test_response_extensions_insert_and_get() {
533        let mut res = Response::empty();
534        res.extensions_mut().insert(42i32);
535        assert!(res.extensions().get::<i32>().is_some());
536    }
537
538    // 状态测试
539
540    #[test]
541    fn test_response_state() {
542        let res = Response::empty();
543        assert_eq!(res.state().len(), 0);
544    }
545
546    #[test]
547    fn test_response_state_mut() {
548        let mut res = Response::empty();
549        res.state_mut().insert(42i32);
550        assert_eq!(res.state().len(), 1);
551    }
552
553    #[test]
554    fn test_response_get_state_not_found() {
555        let res = Response::empty();
556        let result: Result<&i32> = res.get_state();
557        assert!(result.is_err());
558    }
559
560    #[test]
561    fn test_response_get_state_uncheck_panics() {
562        // 测试 get_state_uncheck 在状态不存在时会 panic
563        // Response 包含 ResBody,而 ResBody 包含非 UnwindSafe 的 trait object
564        // 因此不能使用 catch_unwind 捕获 panic
565        // 这个测试仅作为文档说明该方法在状态不存在时会 panic
566    }
567
568    #[test]
569    fn test_response_get_state_success() {
570        let mut res = Response::empty();
571        res.state_mut().insert(42i32);
572        let state: Result<&i32> = res.get_state();
573        assert!(state.is_ok());
574        assert_eq!(*state.unwrap(), 42);
575    }
576
577    #[test]
578    fn test_response_get_state_uncheck_success() {
579        let mut res = Response::empty();
580        res.state_mut().insert(100i32);
581        let state: &i32 = res.get_state_uncheck();
582        assert_eq!(*state, 100);
583    }
584
585    // copy_from_response 测试
586
587    #[test]
588    fn test_response_copy_from_response() {
589        let mut dest = Response::empty();
590        let src = Response::text("source content")
591            .with_status(StatusCode::CREATED)
592            .with_header(header::CACHE_CONTROL, "no-cache".parse().unwrap());
593
594        dest.copy_from_response(src);
595        assert_eq!(dest.status(), StatusCode::CREATED);
596        assert!(dest.headers().get("cache-control").is_some());
597        assert!(!dest.body().is_end_stream());
598    }
599
600    #[test]
601    fn test_response_copy_from_response_preserves_some_headers() {
602        let mut dest =
603            Response::empty().with_header(header::CONTENT_TYPE, "text/plain".parse().unwrap());
604        let src = Response::html("<html>source</html>");
605
606        dest.copy_from_response(src);
607        // Source headers should extend destination, not replace
608        assert!(dest.headers().get("content-type").is_some());
609    }
610
611    #[test]
612    fn test_response_copy_from_response_empty_source() {
613        let mut dest = Response::text("destination");
614        let src = Response::empty();
615
616        dest.copy_from_response(src);
617        assert_eq!(dest.status(), StatusCode::OK);
618    }
619
620    // From trait 测试
621
622    #[test]
623    fn test_response_from_string() {
624        let res: Response = "test string".to_string().into();
625        assert_eq!(res.status(), StatusCode::OK);
626        assert!(res.headers().get("content-type").is_some());
627    }
628
629    #[test]
630    fn test_response_from_integer() {
631        let res: Response = 42.into();
632        assert_eq!(res.status(), StatusCode::OK);
633        assert!(res.headers().get("content-type").is_some());
634    }
635
636    #[test]
637    fn test_response_from_struct() {
638        #[derive(Serialize)]
639        struct TestData {
640            field: String,
641        }
642        let data = TestData {
643            field: "value".to_string(),
644        };
645        let res: Response = data.into();
646        assert_eq!(res.status(), StatusCode::OK);
647    }
648
649    #[test]
650    fn test_response_from_null_value() {
651        let value: Option<i32> = None;
652        let res: Response = value.into();
653        assert_eq!(res.status(), StatusCode::NO_CONTENT);
654    }
655
656    #[test]
657    fn test_response_from_json_object() {
658        #[derive(Serialize)]
659        struct Person {
660            name: String,
661            age: i32,
662        }
663        let person = Person {
664            name: "Alice".to_string(),
665            age: 30,
666        };
667        let res: Response = person.into();
668        assert_eq!(res.status(), StatusCode::OK);
669    }
670
671    // Debug and Display trait 测试
672
673    #[test]
674    fn test_response_debug_format() {
675        let res = Response::text("test");
676        let debug_str = format!("{:?}", res);
677        assert!(debug_str.contains("HTTP"));
678        assert!(debug_str.contains("200 OK"));
679    }
680
681    #[test]
682    fn test_response_display_format() {
683        let res = Response::text("test");
684        let display_str = format!("{}", res);
685        assert!(!display_str.is_empty());
686    }
687
688    #[test]
689    fn test_response_display_equals_debug() {
690        let res = Response::empty();
691        let debug_str = format!("{:?}", res);
692        let display_str = format!("{}", res);
693        // Display delegates to Debug
694        assert_eq!(debug_str, display_str);
695    }
696
697    // 边界条件和特殊情况测试
698
699    #[test]
700    fn test_response_empty_text() {
701        let res = Response::text("");
702        assert_eq!(res.status(), StatusCode::OK);
703        assert!(res.headers().get("content-type").is_some());
704    }
705
706    #[test]
707    fn test_response_empty_html() {
708        let res = Response::html("");
709        assert_eq!(res.status(), StatusCode::OK);
710        assert!(res.headers().get("content-type").is_some());
711    }
712
713    #[test]
714    fn test_response_empty_json() {
715        let data: serde_json::Value = serde_json::json!({});
716        let res = Response::json(&data);
717        assert_eq!(res.status(), StatusCode::OK);
718    }
719
720    #[test]
721    fn test_response_unicode_text() {
722        let text = "你好,世界!🌍";
723        let res = Response::text(text);
724        assert_eq!(res.status(), StatusCode::OK);
725    }
726
727    #[test]
728    fn test_response_large_body() {
729        let large_text = "x".repeat(1000000);
730        let res = Response::text(&large_text);
731        let hint = res.content_length();
732        assert!(hint.lower() >= 1000000);
733    }
734
735    #[test]
736    fn test_response_set_body_after_take() {
737        let mut res = Response::text("original");
738        let _ = res.take_body();
739        res.set_body(full(b"new body".to_vec()));
740        assert!(!res.body().is_end_stream());
741    }
742
743    #[test]
744    fn test_response_status_code_range() {
745        let mut res = Response::empty();
746        for code in [100, 200, 301, 404, 500] {
747            res.set_status(StatusCode::from_u16(code).unwrap());
748            assert_eq!(res.status().as_u16(), code);
749        }
750    }
751
752    #[test]
753    fn test_response_version_default() {
754        let res = Response::empty();
755        assert_eq!(res.version, Version::default());
756    }
757
758    #[test]
759    fn test_response_multiple_headers_same_name() {
760        let mut res = Response::empty();
761        res.headers_mut()
762            .append("x-custom", "value1".parse().unwrap());
763        res.headers_mut()
764            .append("x-custom", "value2".parse().unwrap());
765        let values: Vec<_> = res.headers().get_all("x-custom").iter().collect();
766        assert_eq!(values.len(), 2);
767    }
768}