Skip to main content

rama_http/layer/validate_request/
accept_header.rs

1use rama_core::error::{BoxError, ErrorContext};
2use rama_http_types::body::OptionalBody;
3
4use super::ValidateRequest;
5use crate::{
6    Body, Request, Response, StatusCode, header,
7    mime::{Mime, MimeIter},
8};
9use std::{fmt, marker::PhantomData, sync::Arc};
10
11/// Type that performs validation of the Accept header.
12pub struct AcceptHeader<ResBody = Body> {
13    header_value: Arc<Mime>,
14    _ty: PhantomData<fn() -> ResBody>,
15}
16
17impl<ResBody> AcceptHeader<ResBody> {
18    /// Create a new `AcceptHeader` from the given Mime.
19    pub(super) fn new(mime: Mime) -> Self {
20        Self {
21            header_value: Arc::new(mime),
22            _ty: PhantomData,
23        }
24    }
25
26    /// Try a new `AcceptHeader` from the given header utf-8 value.
27    ///
28    /// # Errors
29    ///
30    /// Errors if `header_value` is not in the form: `type/subtype`, such as `application/json`
31    pub(super) fn try_new(header_value: &str) -> Result<Self, BoxError> {
32        Ok(Self {
33            header_value: Arc::new(
34                header_value
35                    .parse::<Mime>()
36                    .context("value is not a valid header value")?,
37            ),
38            _ty: PhantomData,
39        })
40    }
41}
42
43impl<ResBody> Clone for AcceptHeader<ResBody> {
44    fn clone(&self) -> Self {
45        Self {
46            header_value: self.header_value.clone(),
47            _ty: PhantomData,
48        }
49    }
50}
51
52impl<ResBody> fmt::Debug for AcceptHeader<ResBody> {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        f.debug_struct("AcceptHeader")
55            .field("header_value", &self.header_value)
56            .finish()
57    }
58}
59
60impl<B, ResBody> ValidateRequest<B> for AcceptHeader<ResBody>
61where
62    B: Send + Sync + 'static,
63    ResBody: Send + 'static,
64{
65    type ResponseBody = OptionalBody<ResBody>;
66
67    async fn validate(&self, req: Request<B>) -> Result<Request<B>, Response<Self::ResponseBody>> {
68        if !req.headers().contains_key(header::ACCEPT) {
69            return Ok(req);
70        }
71        if req
72            .headers()
73            .get_all(header::ACCEPT)
74            .into_iter()
75            .filter_map(|header| header.to_str().ok())
76            .any(|h| {
77                MimeIter::new(h)
78                    .map(|mim| {
79                        if let Ok(mim) = mim {
80                            let typ = self.header_value.type_();
81                            let subtype = self.header_value.subtype();
82                            match (mim.type_(), mim.subtype()) {
83                                (t, s) if t == typ && s == subtype => true,
84                                (t, crate::mime::STAR) if t == typ => true,
85                                (crate::mime::STAR, crate::mime::STAR) => true,
86                                _ => false,
87                            }
88                        } else {
89                            false
90                        }
91                    })
92                    .reduce(|acc, mim| acc || mim)
93                    .unwrap_or(false)
94            })
95        {
96            return Ok(req);
97        }
98        let mut res = Response::new(OptionalBody::none());
99        *res.status_mut() = StatusCode::NOT_ACCEPTABLE;
100        Err(res)
101    }
102}