1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use bytes::Bytes;
use hyper::{
    body::Incoming,
    http::{
        header::{self, HeaderMap},
        StatusCode,
    },
};
use serde::{de::DeserializeOwned, ser::Serialize};
#[cfg(all(feature = "serde_json", feature = "sonic_json"))]
compile_error!("features `serde_json` and `sonic_json` cannot be enabled at the same time.");
#[cfg(feature = "serde_json")]
pub use serde_json::Error;
#[cfg(feature = "sonic_json")]
pub use sonic_rs::Error;

use crate::{
    extract::{FromRequest, RejectionError},
    response::IntoResponse,
    HttpContext, Response,
};

#[derive(Debug, Default, Clone, Copy)]
pub struct Json<T>(pub T);

impl<T> IntoResponse for Json<T>
where
    T: Serialize,
{
    fn into_response(self) -> Response {
        #[cfg(feature = "sonic_json")]
        let ser = sonic_rs::to_string(&self.0);
        #[cfg(feature = "serde_json")]
        let ser = serde_json::to_string(&self.0);

        match ser {
            Ok(s) => s.into_response(),
            Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        }
    }
}

impl<T, S> FromRequest<S> for Json<T>
where
    T: DeserializeOwned,
    S: Sync,
{
    type Rejection = RejectionError;

    async fn from_request(
        cx: &mut HttpContext,
        body: Incoming,
        state: &S,
    ) -> Result<Self, Self::Rejection> {
        if !json_content_type(&cx.headers) {
            return Err(RejectionError::InvalidContentType);
        }

        let bytes = Bytes::from_request(cx, body, state).await?;
        #[cfg(feature = "sonic_json")]
        let json = sonic_rs::from_slice(&bytes).map_err(RejectionError::JsonRejection)?;
        #[cfg(feature = "serde_json")]
        let json =
            serde_json::from_slice::<T>(bytes.as_ref()).map_err(RejectionError::JsonRejection)?;

        Ok(Json(json))
    }
}

fn json_content_type(headers: &HeaderMap) -> bool {
    let content_type = if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
        content_type
    } else {
        return false;
    };

    let content_type = if let Ok(content_type) = content_type.to_str() {
        content_type
    } else {
        return false;
    };

    let mime = if let Ok(mime) = content_type.parse::<mime::Mime>() {
        mime
    } else {
        return false;
    };

    let is_json_content_type = mime.type_() == "application"
        && (mime.subtype() == "json" || mime.suffix().map_or(false, |name| name == "json"));

    is_json_content_type
}