use std::fmt;
use std::fmt::{Display, Formatter};
use crate::core::res_body::{ResBody, full};
use crate::headers::{ContentType, Header, HeaderMap, HeaderMapExt};
use crate::{Result, SilentError, State, StatusCode, header};
use http::{Extensions, Version};
use http_body::{Body, SizeHint};
use serde::Serialize;
use serde_json::Value;
pub struct Response<B: Body = ResBody> {
pub(crate) status: StatusCode,
pub(crate) version: Version,
pub(crate) headers: HeaderMap,
pub(crate) body: B,
pub(crate) extensions: Extensions,
pub(crate) state: State,
}
impl fmt::Debug for Response {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "{:?} {}\n{:?}", self.version, self.status, self.headers)
}
}
impl Display for Response {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl Response {
pub fn empty() -> Self {
Self {
status: StatusCode::OK,
headers: HeaderMap::new(),
version: Version::default(),
body: ResBody::None,
extensions: Extensions::default(),
state: State::default(),
}
}
#[inline]
pub fn status(&self) -> StatusCode {
self.status
}
#[inline]
pub fn take_body(&mut self) -> ResBody {
std::mem::replace(&mut self.body, ResBody::None)
}
#[inline]
pub fn redirect(url: &str) -> Result<Self> {
let mut res = Self::empty();
res.status = StatusCode::MOVED_PERMANENTLY;
res.headers.insert(
header::LOCATION,
url.parse().map_err(|e| {
SilentError::business_error(
StatusCode::INTERNAL_SERVER_ERROR,
format!("redirect error: {e}"),
)
})?,
);
Ok(res)
}
#[inline]
pub fn text(text: &str) -> Self {
let mut res = Self::empty();
res.set_typed_header(ContentType::text_utf8());
res.set_body(full(text.as_bytes().to_vec()));
res
}
#[inline]
pub fn html(html: &str) -> Self {
let mut res = Self::empty();
res.set_typed_header(ContentType::html());
res.set_body(full(html.as_bytes().to_vec()));
res
}
#[inline]
pub fn json<T: Serialize>(json: &T) -> Self {
let mut res = Self::empty();
res.set_typed_header(ContentType::json());
res.set_body(full(serde_json::to_vec(json).unwrap()));
res
}
}
impl<B: Body> Response<B> {
#[inline]
pub fn set_status(&mut self, status: StatusCode) {
self.status = status;
}
#[inline]
pub fn with_status(mut self, status: StatusCode) -> Self {
self.status = status;
self
}
#[inline]
pub fn set_body(&mut self, body: B) {
self.body = body;
}
#[inline]
pub fn with_body(mut self, body: B) -> Self {
self.body = body;
self
}
#[inline]
pub fn body(&self) -> &B {
&self.body
}
#[inline]
pub fn set_header(&mut self, key: header::HeaderName, value: header::HeaderValue) {
self.headers.insert(key, value);
}
#[inline]
pub fn with_header(mut self, key: header::HeaderName, value: header::HeaderValue) -> Self {
self.headers.insert(key, value);
self
}
#[inline]
pub fn extensions(&self) -> &Extensions {
&self.extensions
}
#[inline]
pub fn extensions_mut(&mut self) -> &mut Extensions {
&mut self.extensions
}
#[inline]
pub fn get_state<T: Send + Sync + 'static>(&self) -> Result<&T> {
self.state.get::<T>().ok_or(SilentError::ConfigNotFound)
}
#[inline]
pub fn get_state_uncheck<T: Send + Sync + 'static>(&self) -> &T {
self.state.get::<T>().unwrap()
}
#[inline]
pub fn state(&self) -> &State {
&self.state
}
#[inline]
pub fn state_mut(&mut self) -> &mut State {
&mut self.state
}
#[deprecated(since = "2.16.0", note = "请使用 get_state 代替")]
#[inline]
pub fn get_config<T: Send + Sync + 'static>(&self) -> Result<&T> {
self.get_state::<T>()
}
#[deprecated(since = "2.16.0", note = "请使用 get_state_uncheck 代替")]
#[inline]
pub fn get_config_uncheck<T: Send + Sync + 'static>(&self) -> &T {
self.get_state_uncheck::<T>()
}
#[deprecated(since = "2.16.0", note = "请使用 state() 代替")]
#[inline]
pub fn configs(&self) -> &State {
self.state()
}
#[deprecated(since = "2.16.0", note = "请使用 state_mut() 代替")]
#[inline]
pub fn configs_mut(&mut self) -> &mut State {
self.state_mut()
}
#[inline]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
#[inline]
pub fn content_length(&self) -> SizeHint {
self.body.size_hint()
}
#[inline]
pub fn set_typed_header<H>(&mut self, header: H)
where
H: Header,
{
self.headers.typed_insert(header);
}
#[inline]
pub fn with_typed_header<H>(mut self, header: H) -> Self
where
H: Header,
{
self.headers.typed_insert(header);
self
}
pub fn copy_from_response(&mut self, res: Response<B>) {
self.headers.extend(res.headers);
self.status = res.status;
self.extensions.extend(res.extensions);
self.set_body(res.body);
}
}
impl<S: Serialize> From<S> for Response {
fn from(value: S) -> Self {
match serde_json::to_value(&value).unwrap() {
Value::String(value) => Response::empty()
.with_typed_header(ContentType::json())
.with_body(full(value.as_bytes().to_vec())),
Value::Null => Response::empty().with_status(StatusCode::NO_CONTENT),
_ => Self::json(&value),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::response::Response;
#[test]
fn test_response_empty() {
let res = Response::empty();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.version, Version::default());
assert_eq!(res.headers().len(), 0);
}
#[test]
fn test_response_text() {
let res = Response::text("Hello, World!");
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
let content_type = res.headers().get("content-type").unwrap();
assert!(content_type.to_str().unwrap().contains("text/plain"));
}
#[test]
fn test_response_html() {
let res = Response::html("<html><body>Test</body></html>");
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
let content_type = res.headers().get("content-type").unwrap();
assert!(content_type.to_str().unwrap().contains("text/html"));
}
#[test]
fn test_response_json() {
let data = serde_json::json!({"key": "value"});
let res = Response::json(&data);
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
let content_type = res.headers().get("content-type").unwrap();
assert!(content_type.to_str().unwrap().contains("application/json"));
}
#[test]
fn test_response_json_with_struct() {
#[derive(Serialize)]
struct TestData {
name: String,
count: i32,
}
let data = TestData {
name: "test".to_string(),
count: 42,
};
let res = Response::json(&data);
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_redirect_valid_url() {
let res = Response::redirect("https://example.com");
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
assert!(res.headers().get("location").is_some());
let location = res.headers().get("location").unwrap();
assert_eq!(location.to_str().unwrap(), "https://example.com");
}
#[test]
fn test_response_redirect_relative_url() {
let res = Response::redirect("/new-location");
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
assert!(res.headers().get("location").is_some());
}
#[test]
fn test_response_redirect_empty_url() {
let res = Response::redirect("");
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
}
#[test]
fn test_response_redirect_invalid_url() {
let res = Response::redirect("not a valid url");
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.status(), StatusCode::MOVED_PERMANENTLY);
}
#[test]
fn test_response_status() {
let res = Response::empty();
assert_eq!(res.status(), StatusCode::OK);
}
#[test]
fn test_response_set_status() {
let mut res = Response::empty();
res.set_status(StatusCode::NOT_FOUND);
assert_eq!(res.status(), StatusCode::NOT_FOUND);
}
#[test]
fn test_response_set_status_multiple() {
let mut res = Response::empty();
res.set_status(StatusCode::NOT_FOUND);
assert_eq!(res.status(), StatusCode::NOT_FOUND);
res.set_status(StatusCode::INTERNAL_SERVER_ERROR);
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_response_with_status() {
let res = Response::empty().with_status(StatusCode::CREATED);
assert_eq!(res.status(), StatusCode::CREATED);
}
#[test]
fn test_response_with_status_chain() {
let res = Response::empty()
.with_status(StatusCode::CREATED)
.with_status(StatusCode::ACCEPTED);
assert_eq!(res.status(), StatusCode::ACCEPTED);
}
#[test]
fn test_response_body() {
let res = Response::text("test");
assert!(!res.body().is_end_stream());
}
#[test]
fn test_response_set_body() {
let mut res = Response::empty();
let new_body = full(b"new body".to_vec());
res.set_body(new_body);
assert!(!res.body().is_end_stream());
}
#[test]
fn test_response_with_body() {
let body = full(b"test body".to_vec());
let res = Response::empty().with_body(body);
assert!(!res.body().is_end_stream());
}
#[test]
fn test_response_take_body() {
let mut res = Response::text("test");
let body = res.take_body();
assert!(!body.is_end_stream());
assert!(res.body().is_end_stream()); }
#[test]
fn test_response_take_body_twice() {
let mut res = Response::text("test");
let _body1 = res.take_body();
let body2 = res.take_body();
assert!(body2.is_end_stream());
}
#[test]
fn test_response_content_length() {
let res = Response::text("Hello, World!");
let hint = res.content_length();
assert!(hint.lower() > 0);
}
#[test]
fn test_response_content_length_empty() {
let res = Response::empty();
let hint = res.content_length();
assert_eq!(hint.lower(), 0);
}
#[test]
fn test_response_headers() {
let res = Response::text("test");
assert!(res.headers().get("content-type").is_some());
assert_eq!(res.headers().len(), 1);
}
#[test]
fn test_response_headers_empty() {
let res = Response::empty();
assert_eq!(res.headers().len(), 0);
}
#[test]
fn test_response_headers_mut() {
let mut res = Response::empty();
res.headers_mut()
.insert("x-custom-header", "custom-value".parse().unwrap());
assert_eq!(res.headers().len(), 1);
assert!(res.headers().get("x-custom-header").is_some());
}
#[test]
fn test_response_set_header() {
let mut res = Response::empty();
res.set_header(header::CONTENT_TYPE, "application/json".parse().unwrap());
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_set_header_multiple() {
let mut res = Response::empty();
res.set_header(header::CONTENT_TYPE, "text/plain".parse().unwrap());
res.set_header(header::CACHE_CONTROL, "no-cache".parse().unwrap());
assert_eq!(res.headers().len(), 2);
}
#[test]
fn test_response_with_header() {
let res = Response::empty().with_header(header::CONTENT_TYPE, "text/html".parse().unwrap());
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_with_header_chain() {
let res = Response::empty()
.with_header(header::CONTENT_TYPE, "text/plain".parse().unwrap())
.with_header(header::CACHE_CONTROL, "no-cache".parse().unwrap());
assert_eq!(res.headers().len(), 2);
}
#[test]
fn test_response_set_typed_header() {
let mut res = Response::empty();
res.set_typed_header(ContentType::json());
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_with_typed_header() {
let res = Response::empty().with_typed_header(ContentType::text_utf8());
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_extensions() {
let res = Response::empty();
assert_eq!(res.extensions().len(), 0);
}
#[test]
fn test_response_extensions_mut() {
let mut res = Response::empty();
res.extensions_mut().insert("test_key");
assert_eq!(res.extensions().len(), 1);
}
#[test]
fn test_response_extensions_insert_and_get() {
let mut res = Response::empty();
res.extensions_mut().insert(42i32);
assert!(res.extensions().get::<i32>().is_some());
}
#[test]
fn test_response_state() {
let res = Response::empty();
assert_eq!(res.state().len(), 0);
}
#[test]
fn test_response_state_mut() {
let mut res = Response::empty();
res.state_mut().insert(42i32);
assert_eq!(res.state().len(), 1);
}
#[test]
fn test_response_get_state_not_found() {
let res = Response::empty();
let result: Result<&i32> = res.get_state();
assert!(result.is_err());
}
#[test]
fn test_response_get_state_uncheck_panics() {
}
#[test]
fn test_response_get_state_success() {
let mut res = Response::empty();
res.state_mut().insert(42i32);
let state: Result<&i32> = res.get_state();
assert!(state.is_ok());
assert_eq!(*state.unwrap(), 42);
}
#[test]
fn test_response_get_state_uncheck_success() {
let mut res = Response::empty();
res.state_mut().insert(100i32);
let state: &i32 = res.get_state_uncheck();
assert_eq!(*state, 100);
}
#[test]
fn test_response_copy_from_response() {
let mut dest = Response::empty();
let src = Response::text("source content")
.with_status(StatusCode::CREATED)
.with_header(header::CACHE_CONTROL, "no-cache".parse().unwrap());
dest.copy_from_response(src);
assert_eq!(dest.status(), StatusCode::CREATED);
assert!(dest.headers().get("cache-control").is_some());
assert!(!dest.body().is_end_stream());
}
#[test]
fn test_response_copy_from_response_preserves_some_headers() {
let mut dest =
Response::empty().with_header(header::CONTENT_TYPE, "text/plain".parse().unwrap());
let src = Response::html("<html>source</html>");
dest.copy_from_response(src);
assert!(dest.headers().get("content-type").is_some());
}
#[test]
fn test_response_copy_from_response_empty_source() {
let mut dest = Response::text("destination");
let src = Response::empty();
dest.copy_from_response(src);
assert_eq!(dest.status(), StatusCode::OK);
}
#[test]
fn test_response_from_string() {
let res: Response = "test string".to_string().into();
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_from_integer() {
let res: Response = 42.into();
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_from_struct() {
#[derive(Serialize)]
struct TestData {
field: String,
}
let data = TestData {
field: "value".to_string(),
};
let res: Response = data.into();
assert_eq!(res.status(), StatusCode::OK);
}
#[test]
fn test_response_from_null_value() {
let value: Option<i32> = None;
let res: Response = value.into();
assert_eq!(res.status(), StatusCode::NO_CONTENT);
}
#[test]
fn test_response_from_json_object() {
#[derive(Serialize)]
struct Person {
name: String,
age: i32,
}
let person = Person {
name: "Alice".to_string(),
age: 30,
};
let res: Response = person.into();
assert_eq!(res.status(), StatusCode::OK);
}
#[test]
fn test_response_debug_format() {
let res = Response::text("test");
let debug_str = format!("{:?}", res);
assert!(debug_str.contains("HTTP"));
assert!(debug_str.contains("200 OK"));
}
#[test]
fn test_response_display_format() {
let res = Response::text("test");
let display_str = format!("{}", res);
assert!(!display_str.is_empty());
}
#[test]
fn test_response_display_equals_debug() {
let res = Response::empty();
let debug_str = format!("{:?}", res);
let display_str = format!("{}", res);
assert_eq!(debug_str, display_str);
}
#[test]
fn test_response_empty_text() {
let res = Response::text("");
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_empty_html() {
let res = Response::html("");
assert_eq!(res.status(), StatusCode::OK);
assert!(res.headers().get("content-type").is_some());
}
#[test]
fn test_response_empty_json() {
let data: serde_json::Value = serde_json::json!({});
let res = Response::json(&data);
assert_eq!(res.status(), StatusCode::OK);
}
#[test]
fn test_response_unicode_text() {
let text = "你好,世界!🌍";
let res = Response::text(text);
assert_eq!(res.status(), StatusCode::OK);
}
#[test]
fn test_response_large_body() {
let large_text = "x".repeat(1000000);
let res = Response::text(&large_text);
let hint = res.content_length();
assert!(hint.lower() >= 1000000);
}
#[test]
fn test_response_set_body_after_take() {
let mut res = Response::text("original");
let _ = res.take_body();
res.set_body(full(b"new body".to_vec()));
assert!(!res.body().is_end_stream());
}
#[test]
fn test_response_status_code_range() {
let mut res = Response::empty();
for code in [100, 200, 301, 404, 500] {
res.set_status(StatusCode::from_u16(code).unwrap());
assert_eq!(res.status().as_u16(), code);
}
}
#[test]
fn test_response_version_default() {
let res = Response::empty();
assert_eq!(res.version, Version::default());
}
#[test]
fn test_response_multiple_headers_same_name() {
let mut res = Response::empty();
res.headers_mut()
.append("x-custom", "value1".parse().unwrap());
res.headers_mut()
.append("x-custom", "value2".parse().unwrap());
let values: Vec<_> = res.headers().get_all("x-custom").iter().collect();
assert_eq!(values.len(), 2);
}
}