Skip to main content

rama_http/layer/validate_request/
validate_request_header.rs

1use super::{AcceptHeader, BoxValidateRequestFn, ValidateRequest};
2use crate::{Request, Response};
3use rama_core::error::BoxError;
4use rama_core::{Layer, Service};
5use rama_http_types::mime::Mime;
6use rama_utils::macros::define_inner_service_accessors;
7
8/// Layer that applies [`ValidateRequestHeader`] which validates all requests.
9///
10/// See the [module docs](crate::layer::validate_request) for an example.
11#[derive(Debug, Clone)]
12pub struct ValidateRequestHeaderLayer<T> {
13    pub(crate) validate: T,
14}
15
16impl<ResBody> ValidateRequestHeaderLayer<AcceptHeader<ResBody>> {
17    /// Validate requests have the required Accept header.
18    ///
19    /// The `Accept` header is required to be `*/*`, `type/*` or `type/subtype`,
20    /// as configured.
21    ///
22    /// # Errors
23    ///
24    /// Errors if `header_value` is not in the form: `type/subtype`, such as `application/json`
25    pub fn try_accept_for_str(value: &str) -> Result<Self, BoxError> {
26        Ok(Self::custom(AcceptHeader::try_new(value)?))
27    }
28
29    /// Validate requests have the required Accept header.
30    ///
31    /// The `Accept` header is required to be `*/*`, `type/*` or `type/subtype`,
32    /// as configured.
33    #[must_use]
34    pub fn accept(mime: Mime) -> Self {
35        Self::custom(AcceptHeader::new(mime))
36    }
37}
38
39impl<T> ValidateRequestHeaderLayer<T> {
40    /// Validate requests using a custom validator.
41    pub fn custom(validate: T) -> Self {
42        Self { validate }
43    }
44}
45
46impl<F, A> ValidateRequestHeaderLayer<BoxValidateRequestFn<F, A>> {
47    /// Validate requests using a custom validator Fn.
48    pub fn custom_fn(validate: F) -> Self {
49        Self {
50            validate: BoxValidateRequestFn::new(validate),
51        }
52    }
53}
54
55impl<S, T> Layer<S> for ValidateRequestHeaderLayer<T>
56where
57    T: Clone,
58{
59    type Service = ValidateRequestHeader<S, T>;
60
61    fn layer(&self, inner: S) -> Self::Service {
62        ValidateRequestHeader::new(inner, self.validate.clone())
63    }
64
65    fn into_layer(self, inner: S) -> Self::Service {
66        ValidateRequestHeader::new(inner, self.validate)
67    }
68}
69
70/// Middleware that validates requests.
71///
72/// See the [module docs](crate::layer::validate_request) for an example.
73#[derive(Debug, Clone)]
74pub struct ValidateRequestHeader<S, T> {
75    inner: S,
76    pub(crate) validate: T,
77}
78
79impl<S, T> ValidateRequestHeader<S, T> {
80    fn new(inner: S, validate: T) -> Self {
81        Self::custom(inner, validate)
82    }
83
84    define_inner_service_accessors!();
85}
86
87impl<S, ResBody> ValidateRequestHeader<S, AcceptHeader<ResBody>> {
88    /// Validate requests have the required Accept header.
89    ///
90    /// The `Accept` header is required to be `*/*`, `type/*` or `type/subtype`,
91    /// as configured.
92    ///
93    /// # Errors
94    ///
95    /// Errors if `header_value` is not in the form: `type/subtype`, such as `application/json`
96    pub fn try_accept_for_str(inner: S, value: &str) -> Result<Self, BoxError> {
97        Ok(Self::custom(inner, AcceptHeader::try_new(value)?))
98    }
99
100    /// Validate requests have the required Accept header.
101    ///
102    /// The `Accept` header is required to be `*/*`, `type/*` or `type/subtype`,
103    /// as configured.
104    #[must_use]
105    pub fn accept(inner: S, mime: Mime) -> Self {
106        Self::custom(inner, AcceptHeader::new(mime))
107    }
108}
109
110impl<S, T> ValidateRequestHeader<S, T> {
111    /// Validate requests using a custom validator.
112    pub fn custom(inner: S, validate: T) -> Self {
113        Self { inner, validate }
114    }
115}
116
117impl<S, F, A> ValidateRequestHeader<S, BoxValidateRequestFn<F, A>> {
118    /// Validate requests using a custom validator Fn.
119    pub fn custom_fn(inner: S, validate: F) -> Self {
120        Self {
121            inner,
122            validate: BoxValidateRequestFn::new(validate),
123        }
124    }
125}
126
127impl<ReqBody, ServiceResBody, ValidateResBody, S, V> Service<Request<ReqBody>>
128    for ValidateRequestHeader<S, V>
129where
130    ReqBody: Send + 'static,
131    ServiceResBody: Send + 'static,
132    ValidateResBody: From<ServiceResBody> + Send + 'static,
133    V: ValidateRequest<ReqBody, ResponseBody = ValidateResBody>,
134    S: Service<Request<ReqBody>, Output = Response<ServiceResBody>>,
135{
136    type Output = Response<ValidateResBody>;
137    type Error = S::Error;
138
139    async fn serve(&self, req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
140        match self.validate.validate(req).await {
141            Ok(req) => Ok(self.inner.serve(req).await?.map(ValidateResBody::from)),
142            Err(res) => Ok(res),
143        }
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    use crate::{Body, StatusCode, header};
152    use rama_core::{Layer, error::BoxError, service::service_fn};
153    use rama_http_types::mime::APPLICATION_JSON;
154
155    #[tokio::test]
156    async fn valid_accept_header() {
157        let service = ValidateRequestHeaderLayer::try_accept_for_str("application/json")
158            .unwrap()
159            .into_layer(service_fn(echo));
160
161        let request = Request::get("/")
162            .header(header::ACCEPT, "application/json")
163            .body(Body::empty())
164            .unwrap();
165
166        let res = service.serve(request).await.unwrap();
167
168        assert_eq!(res.status(), StatusCode::OK);
169    }
170
171    #[tokio::test]
172    async fn valid_accept_header_with_mime() {
173        let service =
174            ValidateRequestHeaderLayer::accept(APPLICATION_JSON).into_layer(service_fn(echo));
175
176        let request = Request::get("/")
177            .header(header::ACCEPT, "application/json")
178            .body(Body::empty())
179            .unwrap();
180
181        let res = service.serve(request).await.unwrap();
182
183        assert_eq!(res.status(), StatusCode::OK);
184    }
185
186    #[tokio::test]
187    async fn valid_accept_header_accept_all_json() {
188        let service = ValidateRequestHeaderLayer::try_accept_for_str("application/json")
189            .unwrap()
190            .into_layer(service_fn(echo));
191
192        let request = Request::get("/")
193            .header(header::ACCEPT, "application/*")
194            .body(Body::empty())
195            .unwrap();
196
197        let res = service.serve(request).await.unwrap();
198
199        assert_eq!(res.status(), StatusCode::OK);
200    }
201
202    #[tokio::test]
203    async fn valid_accept_header_accept_all() {
204        let service = ValidateRequestHeaderLayer::try_accept_for_str("application/json")
205            .unwrap()
206            .into_layer(service_fn(echo));
207
208        let request = Request::get("/")
209            .header(header::ACCEPT, "*/*")
210            .body(Body::empty())
211            .unwrap();
212
213        let res = service.serve(request).await.unwrap();
214
215        assert_eq!(res.status(), StatusCode::OK);
216    }
217
218    #[tokio::test]
219    async fn invalid_accept_header() {
220        let service =
221            ValidateRequestHeaderLayer::accept(APPLICATION_JSON).into_layer(service_fn(echo));
222
223        let request = Request::get("/")
224            .header(header::ACCEPT, "invalid")
225            .body(Body::empty())
226            .unwrap();
227
228        let res = service.serve(request).await.unwrap();
229
230        assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
231    }
232    #[tokio::test]
233    async fn not_accepted_accept_header_subtype() {
234        let service =
235            ValidateRequestHeaderLayer::accept(APPLICATION_JSON).into_layer(service_fn(echo));
236
237        let request = Request::get("/")
238            .header(header::ACCEPT, "application/strings")
239            .body(Body::empty())
240            .unwrap();
241
242        let res = service.serve(request).await.unwrap();
243
244        assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
245    }
246
247    #[tokio::test]
248    async fn not_accepted_accept_header() {
249        let service =
250            ValidateRequestHeaderLayer::accept(APPLICATION_JSON).into_layer(service_fn(echo));
251
252        let request = Request::get("/")
253            .header(header::ACCEPT, "text/strings")
254            .body(Body::empty())
255            .unwrap();
256
257        let res = service.serve(request).await.unwrap();
258
259        assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
260    }
261
262    #[tokio::test]
263    async fn accepted_multiple_header_value() {
264        let service =
265            ValidateRequestHeaderLayer::accept(APPLICATION_JSON).into_layer(service_fn(echo));
266
267        let request = Request::get("/")
268            .header(header::ACCEPT, "text/strings")
269            .header(header::ACCEPT, "invalid, application/json")
270            .body(Body::empty())
271            .unwrap();
272
273        let res = service.serve(request).await.unwrap();
274
275        assert_eq!(res.status(), StatusCode::OK);
276    }
277
278    #[tokio::test]
279    async fn accepted_inner_header_value() {
280        let service =
281            ValidateRequestHeaderLayer::accept(APPLICATION_JSON).into_layer(service_fn(echo));
282
283        let request = Request::get("/")
284            .header(header::ACCEPT, "text/strings, invalid, application/json")
285            .body(Body::empty())
286            .unwrap();
287
288        let res = service.serve(request).await.unwrap();
289
290        assert_eq!(res.status(), StatusCode::OK);
291    }
292
293    #[tokio::test]
294    async fn accepted_header_with_quotes_valid() {
295        let value = "foo/bar; parisien=\"baguette, text/html, jambon, fromage\", application/*";
296        let service = ValidateRequestHeaderLayer::try_accept_for_str("application/xml")
297            .unwrap()
298            .into_layer(service_fn(echo));
299
300        let request = Request::get("/")
301            .header(header::ACCEPT, value)
302            .body(Body::empty())
303            .unwrap();
304
305        let res = service.serve(request).await.unwrap();
306
307        assert_eq!(res.status(), StatusCode::OK);
308    }
309
310    #[tokio::test]
311    async fn accepted_header_with_quotes_invalid() {
312        let value = "foo/bar; parisien=\"baguette, text/html, jambon, fromage\"";
313        let service = ValidateRequestHeaderLayer::try_accept_for_str("text/html")
314            .unwrap()
315            .into_layer(service_fn(echo));
316
317        let request = Request::get("/")
318            .header(header::ACCEPT, value)
319            .body(Body::empty())
320            .unwrap();
321
322        let res = service.serve(request).await.unwrap();
323
324        assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
325    }
326
327    async fn echo<B>(req: Request<B>) -> Result<Response<B>, BoxError> {
328        Ok(Response::new(req.into_body()))
329    }
330}