1use std::{
4 convert::Infallible,
5 future::Future,
6 marker::PhantomData,
7 pin::Pin,
8 task::{Context, Poll},
9};
10
11use actix_http::{Method, Uri};
12use actix_utils::future::{ok, Ready};
13use futures_core::ready;
14use pin_project_lite::pin_project;
15
16use crate::{dev::Payload, Error, HttpRequest};
17
18#[doc(alias = "extract", alias = "extractor")]
65pub trait FromRequest: Sized {
66 type Error: Into<Error>;
68
69 type Future: Future<Output = Result<Self, Self::Error>>;
87
88 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future;
90
91 fn extract(req: &HttpRequest) -> Self::Future {
95 Self::from_request(req, &mut Payload::None)
96 }
97}
98
99impl<T> FromRequest for Option<T>
145where
146 T: FromRequest,
147{
148 type Error = Infallible;
149 type Future = FromRequestOptFuture<T::Future>;
150
151 #[inline]
152 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
153 FromRequestOptFuture {
154 fut: T::from_request(req, payload),
155 }
156 }
157}
158
159pin_project! {
160 pub struct FromRequestOptFuture<Fut> {
161 #[pin]
162 fut: Fut,
163 }
164}
165
166impl<Fut, T, E> Future for FromRequestOptFuture<Fut>
167where
168 Fut: Future<Output = Result<T, E>>,
169 E: Into<Error>,
170{
171 type Output = Result<Option<T>, Infallible>;
172
173 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
174 let this = self.project();
175 let res = ready!(this.fut.poll(cx));
176 match res {
177 Ok(t) => Poll::Ready(Ok(Some(t))),
178 Err(err) => {
179 log::debug!("Error for Option<T> extractor: {}", err.into());
180 Poll::Ready(Ok(None))
181 }
182 }
183 }
184}
185
186impl<T, E> FromRequest for Result<T, E>
230where
231 T: FromRequest,
232 T::Error: Into<E>,
233{
234 type Error = Infallible;
235 type Future = FromRequestResFuture<T::Future, E>;
236
237 #[inline]
238 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
239 FromRequestResFuture {
240 fut: T::from_request(req, payload),
241 _phantom: PhantomData,
242 }
243 }
244}
245
246pin_project! {
247 pub struct FromRequestResFuture<Fut, E> {
248 #[pin]
249 fut: Fut,
250 _phantom: PhantomData<E>,
251 }
252}
253
254impl<Fut, T, Ei, E> Future for FromRequestResFuture<Fut, E>
255where
256 Fut: Future<Output = Result<T, Ei>>,
257 Ei: Into<E>,
258{
259 type Output = Result<Result<T, E>, Infallible>;
260
261 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
262 let this = self.project();
263 let res = ready!(this.fut.poll(cx));
264 Poll::Ready(Ok(res.map_err(Into::into)))
265 }
266}
267
268impl FromRequest for Uri {
281 type Error = Infallible;
282 type Future = Ready<Result<Self, Self::Error>>;
283
284 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
285 ok(req.uri().clone())
286 }
287}
288
289impl FromRequest for Method {
302 type Error = Infallible;
303 type Future = Ready<Result<Self, Self::Error>>;
304
305 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
306 ok(req.method().clone())
307 }
308}
309
310#[doc(hidden)]
311#[allow(non_snake_case)]
312mod tuple_from_req {
313 use super::*;
314
315 macro_rules! tuple_from_req {
316 ($fut: ident; $($T: ident),*) => {
317 #[allow(unused_parens)]
319 impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+)
320 {
321 type Error = Error;
322 type Future = $fut<$($T),+>;
323
324 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
325 $fut {
326 $(
327 $T: ExtractFuture::Future {
328 fut: $T::from_request(req, payload)
329 },
330 )+
331 }
332 }
333 }
334
335 pin_project! {
336 pub struct $fut<$($T: FromRequest),+> {
337 $(
338 #[pin]
339 $T: ExtractFuture<$T::Future, $T>,
340 )+
341 }
342 }
343
344 impl<$($T: FromRequest),+> Future for $fut<$($T),+>
345 {
346 type Output = Result<($($T,)+), Error>;
347
348 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
349 let mut this = self.project();
350
351 let mut ready = true;
352 $(
353 match this.$T.as_mut().project() {
354 ExtractProj::Future { fut } => match fut.poll(cx) {
355 Poll::Ready(Ok(output)) => {
356 let _ = this.$T.as_mut().project_replace(ExtractFuture::Done { output });
357 },
358 Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())),
359 Poll::Pending => ready = false,
360 },
361 ExtractProj::Done { .. } => {},
362 ExtractProj::Empty => unreachable!("FromRequest polled after finished"),
363 }
364 )+
365
366 if ready {
367 Poll::Ready(Ok(
368 ($(
369 match this.$T.project_replace(ExtractFuture::Empty) {
370 ExtractReplaceProj::Done { output } => output,
371 _ => unreachable!("FromRequest polled after finished"),
372 },
373 )+)
374 ))
375 } else {
376 Poll::Pending
377 }
378 }
379 }
380 };
381 }
382
383 pin_project! {
384 #[project = ExtractProj]
385 #[project_replace = ExtractReplaceProj]
386 enum ExtractFuture<Fut, Res> {
387 Future {
388 #[pin]
389 fut: Fut
390 },
391 Done {
392 output: Res,
393 },
394 Empty
395 }
396 }
397
398 impl FromRequest for () {
399 type Error = Infallible;
400 type Future = Ready<Result<Self, Self::Error>>;
401
402 fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
403 ok(())
404 }
405 }
406
407 tuple_from_req! { TupleFromRequest1; A }
408 tuple_from_req! { TupleFromRequest2; A, B }
409 tuple_from_req! { TupleFromRequest3; A, B, C }
410 tuple_from_req! { TupleFromRequest4; A, B, C, D }
411 tuple_from_req! { TupleFromRequest5; A, B, C, D, E }
412 tuple_from_req! { TupleFromRequest6; A, B, C, D, E, F }
413 tuple_from_req! { TupleFromRequest7; A, B, C, D, E, F, G }
414 tuple_from_req! { TupleFromRequest8; A, B, C, D, E, F, G, H }
415 tuple_from_req! { TupleFromRequest9; A, B, C, D, E, F, G, H, I }
416 tuple_from_req! { TupleFromRequest10; A, B, C, D, E, F, G, H, I, J }
417 tuple_from_req! { TupleFromRequest11; A, B, C, D, E, F, G, H, I, J, K }
418 tuple_from_req! { TupleFromRequest12; A, B, C, D, E, F, G, H, I, J, K, L }
419 tuple_from_req! { TupleFromRequest13; A, B, C, D, E, F, G, H, I, J, K, L, M }
420 tuple_from_req! { TupleFromRequest14; A, B, C, D, E, F, G, H, I, J, K, L, M, N }
421 tuple_from_req! { TupleFromRequest15; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O }
422 tuple_from_req! { TupleFromRequest16; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P }
423}
424
425#[cfg(test)]
426mod tests {
427 use actix_http::header;
428 use bytes::Bytes;
429 use serde::Deserialize;
430
431 use super::*;
432 use crate::{
433 test::TestRequest,
434 types::{Form, FormConfig},
435 };
436
437 #[derive(Deserialize, Debug, PartialEq)]
438 struct Info {
439 hello: String,
440 }
441
442 #[actix_rt::test]
443 async fn test_option() {
444 let (req, mut pl) = TestRequest::default()
445 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
446 .data(FormConfig::default().limit(4096))
447 .to_http_parts();
448
449 let r = Option::<Form<Info>>::from_request(&req, &mut pl)
450 .await
451 .unwrap();
452 assert_eq!(r, None);
453
454 let (req, mut pl) = TestRequest::default()
455 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
456 .insert_header((header::CONTENT_LENGTH, "9"))
457 .set_payload(Bytes::from_static(b"hello=world"))
458 .to_http_parts();
459
460 let r = Option::<Form<Info>>::from_request(&req, &mut pl)
461 .await
462 .unwrap();
463 assert_eq!(
464 r,
465 Some(Form(Info {
466 hello: "world".into()
467 }))
468 );
469
470 let (req, mut pl) = TestRequest::default()
471 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
472 .insert_header((header::CONTENT_LENGTH, "9"))
473 .set_payload(Bytes::from_static(b"bye=world"))
474 .to_http_parts();
475
476 let r = Option::<Form<Info>>::from_request(&req, &mut pl)
477 .await
478 .unwrap();
479 assert_eq!(r, None);
480 }
481
482 #[actix_rt::test]
483 async fn test_result() {
484 let (req, mut pl) = TestRequest::default()
485 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
486 .insert_header((header::CONTENT_LENGTH, "11"))
487 .set_payload(Bytes::from_static(b"hello=world"))
488 .to_http_parts();
489
490 let r = Result::<Form<Info>, Error>::from_request(&req, &mut pl)
491 .await
492 .unwrap()
493 .unwrap();
494 assert_eq!(
495 r,
496 Form(Info {
497 hello: "world".into()
498 })
499 );
500
501 let (req, mut pl) = TestRequest::default()
502 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
503 .insert_header((header::CONTENT_LENGTH, 9))
504 .set_payload(Bytes::from_static(b"bye=world"))
505 .to_http_parts();
506
507 struct MyError;
508 impl From<Error> for MyError {
509 fn from(_: Error) -> Self {
510 Self
511 }
512 }
513
514 let r = Result::<Form<Info>, MyError>::from_request(&req, &mut pl)
515 .await
516 .unwrap();
517 assert!(r.is_err());
518 }
519
520 #[actix_rt::test]
521 async fn test_uri() {
522 let req = TestRequest::default().uri("/foo/bar").to_http_request();
523 let uri = Uri::extract(&req).await.unwrap();
524 assert_eq!(uri.path(), "/foo/bar");
525 }
526
527 #[actix_rt::test]
528 async fn test_method() {
529 let req = TestRequest::default().method(Method::GET).to_http_request();
530 let method = Method::extract(&req).await.unwrap();
531 assert_eq!(method, Method::GET);
532 }
533
534 #[actix_rt::test]
535 async fn test_concurrent() {
536 let (req, mut pl) = TestRequest::default()
537 .uri("/foo/bar")
538 .method(Method::GET)
539 .insert_header((header::CONTENT_TYPE, "application/x-www-form-urlencoded"))
540 .insert_header((header::CONTENT_LENGTH, "11"))
541 .set_payload(Bytes::from_static(b"hello=world"))
542 .to_http_parts();
543 let (method, uri, form) = <(Method, Uri, Form<Info>)>::from_request(&req, &mut pl)
544 .await
545 .unwrap();
546 assert_eq!(method, Method::GET);
547 assert_eq!(uri.path(), "/foo/bar");
548 assert_eq!(
549 form,
550 Form(Info {
551 hello: "world".into()
552 })
553 );
554 }
555}