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