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
use bytes::Bytes;
use http::{
    header::{self, HeaderMap},
    request::Parts,
    StatusCode,
};
use hyper::body::Incoming;
use mime::Mime;
use serde::de::DeserializeOwned;

use super::{deserialize, Error, Json};
use crate::{
    context::ServerContext,
    error::server::{invalid_content_type, ExtractBodyError},
    response::ServerResponse,
    server::{extract::FromRequest, IntoResponse},
};

impl IntoResponse for Error {
    fn into_response(self) -> ServerResponse {
        StatusCode::INTERNAL_SERVER_ERROR.into_response()
    }
}

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

    async fn from_request(
        cx: &mut ServerContext,
        parts: Parts,
        body: Incoming,
    ) -> Result<Self, Self::Rejection> {
        if !json_content_type(&parts.headers) {
            return Err(invalid_content_type());
        }

        let bytes = Bytes::from_request(cx, parts, body).await?;
        let json = deserialize(&bytes).map_err(ExtractBodyError::Json)?;

        Ok(Json(json))
    }
}

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

    let content_type = match content_type.to_str() {
        Ok(s) => s,
        Err(_) => {
            return false;
        }
    };

    let mime_type = match content_type.parse::<Mime>() {
        Ok(mime_type) => mime_type,
        Err(_) => {
            return false;
        }
    };

    // `application/json` or `application/json+foo`
    if mime_type.type_() == mime::APPLICATION && mime_type.subtype() == mime::JSON {
        return true;
    }

    // `application/foo+json`
    if mime_type.suffix() == Some(mime::JSON) {
        return true;
    }

    false
}