iota_rest_api/
content_type.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use axum::http::{self, HeaderMap, StatusCode, header};
6use mime::Mime;
7use tap::Pipe;
8
9/// `Content-Type` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)
10#[derive(Debug, Clone)]
11pub struct ContentType(pub Mime);
12
13impl ContentType {
14    pub fn from_headers(headers: &HeaderMap) -> Option<Self> {
15        parse_content_type(headers).ok()?.map(Self)
16    }
17}
18
19fn parse_content_type(headers: &HeaderMap) -> Result<Option<Mime>, mime::FromStrError> {
20    let Some(header) = headers
21        .get(header::CONTENT_TYPE)
22        .and_then(|hval| hval.to_str().ok())
23    else {
24        return Ok(None);
25    };
26
27    let mime: Mime = header.parse()?;
28    Ok(Some(mime))
29}
30
31#[axum::async_trait]
32impl<S> axum::extract::FromRequestParts<S> for ContentType
33where
34    S: Send + Sync,
35{
36    type Rejection = (StatusCode, &'static str);
37
38    async fn from_request_parts(
39        parts: &mut http::request::Parts,
40        _: &S,
41    ) -> Result<Self, Self::Rejection> {
42        parse_content_type(&parts.headers)
43            .map_err(|_| (StatusCode::BAD_REQUEST, "invalid Content-Type mime"))?
44            .ok_or((StatusCode::BAD_REQUEST, "Content-Type header missing"))?
45            .pipe(Self)
46            .pipe(Ok)
47    }
48}