rama_http/layer/validate_request/
validate_request_header.rs1use 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#[derive(Debug, Clone)]
12pub struct ValidateRequestHeaderLayer<T> {
13 pub(crate) validate: T,
14}
15
16impl<ResBody> ValidateRequestHeaderLayer<AcceptHeader<ResBody>> {
17 pub fn try_accept_for_str(value: &str) -> Result<Self, BoxError> {
26 Ok(Self::custom(AcceptHeader::try_new(value)?))
27 }
28
29 #[must_use]
34 pub fn accept(mime: Mime) -> Self {
35 Self::custom(AcceptHeader::new(mime))
36 }
37}
38
39impl<T> ValidateRequestHeaderLayer<T> {
40 pub fn custom(validate: T) -> Self {
42 Self { validate }
43 }
44}
45
46impl<F, A> ValidateRequestHeaderLayer<BoxValidateRequestFn<F, A>> {
47 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#[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 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 #[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 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 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}