From 8ae278cb68eda1e6c4fbd3463b018e0f0fe1c313 Mon Sep 17 00:00:00 2001
From: Arniu Tseng <arniu2006@gmail.com>
Date: Sat, 11 Sep 2021 08:11:16 +0800
Subject: [PATCH] Remove `FromRequest::Config` (#2233)

Co-authored-by: Jonas Platte <jplatte@users.noreply.github.com>
Co-authored-by: Igor Aleksanov <popzxc@yandex.ru>
Co-authored-by: Rob Ede <robjtede@icloud.com>
---
 CHANGES.md                       |  3 ++
 Cargo.toml                       |  1 +
 MIGRATION.md                     |  2 ++
 actix-files/src/path_buf.rs      |  1 -
 actix-multipart/src/extractor.rs |  1 -
 src/data.rs                      |  1 -
 src/extract.rs                   | 55 ++++++++++++++++++++------------
 src/info.rs                      |  2 --
 src/request.rs                   |  1 -
 src/request_data.rs              |  1 -
 src/types/either.rs              |  1 -
 src/types/form.rs                | 30 +++++++++--------
 src/types/header.rs              |  1 -
 src/types/json.rs                |  1 -
 src/types/path.rs                |  3 +-
 src/types/payload.rs             | 17 ++++------
 src/types/query.rs               |  3 +-
 17 files changed, 66 insertions(+), 58 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 398ac477..d8831602 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,7 +1,10 @@
 # Changes
 
 ## Unreleased - 2021-xx-xx
+### Changed
+* Asscociated type `FromRequest::Config` was removed. [#2233]
 
+[#2233]: https://github.com/actix/actix-web/pull/2233
 
 ## 4.0.0-beta.9 - 2021-09-09
 ### Added
diff --git a/Cargo.toml b/Cargo.toml
index 73a52182..dc7e9af3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ edition = "2018"
 [package.metadata.docs.rs]
 # features that docs.rs will build with
 features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "secure-cookies"]
+rustdoc-args = ["--cfg", "docsrs"]
 
 [lib]
 name = "actix_web"
diff --git a/MIGRATION.md b/MIGRATION.md
index 9a70adb9..d53bd7bf 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -11,6 +11,8 @@
 
   Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.
 
+* The `type Config` of `FromRequest` was removed.
+
 * Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd).
   By default all compression algorithms are enabled.
   To select algorithm you want to include with `middleware::Compress` use following flags:
diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs
index 8a87acd5..76f58930 100644
--- a/actix-files/src/path_buf.rs
+++ b/actix-files/src/path_buf.rs
@@ -59,7 +59,6 @@ impl AsRef<Path> for PathBufWrap {
 impl FromRequest for PathBufWrap {
     type Error = UriSegmentError;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         ready(req.match_info().path().parse())
diff --git a/actix-multipart/src/extractor.rs b/actix-multipart/src/extractor.rs
index c87f8cc2..1ad1f203 100644
--- a/actix-multipart/src/extractor.rs
+++ b/actix-multipart/src/extractor.rs
@@ -33,7 +33,6 @@ use crate::server::Multipart;
 impl FromRequest for Multipart {
     type Error = Error;
     type Future = Ready<Result<Multipart, Error>>;
-    type Config = ();
 
     #[inline]
     fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
diff --git a/src/data.rs b/src/data.rs
index 174faba3..9d4fe084 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -120,7 +120,6 @@ where
 }
 
 impl<T: ?Sized + 'static> FromRequest for Data<T> {
-    type Config = ();
     type Error = Error;
     type Future = Ready<Result<Self, Error>>;
 
diff --git a/src/extract.rs b/src/extract.rs
index 592f7ab8..39062dd1 100644
--- a/src/extract.rs
+++ b/src/extract.rs
@@ -13,13 +13,42 @@ use futures_core::ready;
 
 use crate::{dev::Payload, Error, HttpRequest};
 
-/// Trait implemented by types that can be extracted from request.
+/// A type that implements [`FromRequest`] is called an **extractor** and can extract data
+/// from the request. Examples of types that implement this trait are [`Json`], [`Form`], [`Path`].
 ///
-/// Types that implement this trait can be used with `Route` handlers.
+/// An extractor can be customized by injecting the corresponding configuration with one of:
+///
+/// - [`App::app_data()`](`crate::App::app_data`)
+/// - [`Scope::app_data()`](`crate::Scope::app_data`)
+/// - [`Resource::app_data()`](`crate::Resource::app_data`)
+///
+/// Here are some built-in extractors and their corresponding configuration.
+/// Please refer to the respective documentation for details.
+///
+/// | Extractor   | Configuration     |
+/// |-------------|-------------------|
+/// | [`Json`]    | [`JsonConfig`]    |
+/// | [`Form`]    | [`FormConfig`]    |
+/// | [`Path`]    | [`PathConfig`]    |
+/// | [`Query`]   | [`QueryConfig`]   |
+/// | [`Payload`] | [`PayloadConfig`] |
+/// | [`String`]  | [`PayloadConfig`] |
+/// | [`Bytes`]   | [`PayloadConfig`] |
+///
+/// [`Json`]: crate::web::Json
+/// [`JsonConfig`]: crate::web::JsonConfig
+/// [`Form`]: crate::web::Form
+/// [`FormConfig`]: crate::web::FormConfig
+/// [`Path`]: crate::web::Path
+/// [`PathConfig`]: crate::web::PathConfig
+/// [`Query`]: crate::web::Query
+/// [`QueryConfig`]: crate::web::QueryConfig
+/// [`Payload`]: crate::web::Payload
+/// [`PayloadConfig`]: crate::web::PayloadConfig
+/// [`String`]: FromRequest#impl-FromRequest-for-String
+/// [`Bytes`]: crate::web::Bytes#impl-FromRequest
+#[cfg_attr(docsrs, doc(alias = "Extractor"))]
 pub trait FromRequest: Sized {
-    /// Configuration for this extractor.
-    type Config: Default + 'static;
-
     /// The associated error which can be returned.
     type Error: Into<Error>;
 
@@ -35,14 +64,6 @@ pub trait FromRequest: Sized {
     fn extract(req: &HttpRequest) -> Self::Future {
         Self::from_request(req, &mut Payload::None)
     }
-
-    /// Create and configure config instance.
-    fn configure<F>(f: F) -> Self::Config
-    where
-        F: FnOnce(Self::Config) -> Self::Config,
-    {
-        f(Self::Config::default())
-    }
 }
 
 /// Optionally extract a field from the request
@@ -65,7 +86,6 @@ pub trait FromRequest: Sized {
 /// impl FromRequest for Thing {
 ///     type Error = Error;
 ///     type Future = Ready<Result<Self, Self::Error>>;
-///     type Config = ();
 ///
 ///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
 ///         if rand::random() {
@@ -100,7 +120,6 @@ where
 {
     type Error = Error;
     type Future = FromRequestOptFuture<T::Future>;
-    type Config = T::Config;
 
     #[inline]
     fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
@@ -156,7 +175,6 @@ where
 /// impl FromRequest for Thing {
 ///     type Error = Error;
 ///     type Future = Ready<Result<Thing, Error>>;
-///     type Config = ();
 ///
 ///     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
 ///         if rand::random() {
@@ -189,7 +207,6 @@ where
 {
     type Error = Error;
     type Future = FromRequestResFuture<T::Future>;
-    type Config = T::Config;
 
     #[inline]
     fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
@@ -233,7 +250,6 @@ where
 impl FromRequest for Uri {
     type Error = Infallible;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         ok(req.uri().clone())
@@ -255,7 +271,6 @@ impl FromRequest for Uri {
 impl FromRequest for Method {
     type Error = Infallible;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         ok(req.method().clone())
@@ -266,7 +281,6 @@ impl FromRequest for Method {
 impl FromRequest for () {
     type Error = Infallible;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future {
         ok(())
@@ -306,7 +320,6 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {
         {
             type Error = Error;
             type Future = $fut_type<$($T),+>;
-            type Config = ($($T::Config),+);
 
             fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
                 $fut_type {
diff --git a/src/info.rs b/src/info.rs
index de8ad67e..d928a1e6 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -209,7 +209,6 @@ impl ConnectionInfo {
 impl FromRequest for ConnectionInfo {
     type Error = Infallible;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         ok(req.connection_info().clone())
@@ -252,7 +251,6 @@ impl ResponseError for MissingPeerAddr {}
 impl FromRequest for PeerAddr {
     type Error = MissingPeerAddr;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         match req.peer_addr() {
diff --git a/src/request.rs b/src/request.rs
index c25a5397..0027f9b4 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -358,7 +358,6 @@ impl Drop for HttpRequest {
 /// }
 /// ```
 impl FromRequest for HttpRequest {
-    type Config = ();
     type Error = Error;
     type Future = Ready<Result<Self, Error>>;
 
diff --git a/src/request_data.rs b/src/request_data.rs
index 58194301..575dc1eb 100644
--- a/src/request_data.rs
+++ b/src/request_data.rs
@@ -64,7 +64,6 @@ impl<T: Clone + 'static> Deref for ReqData<T> {
 }
 
 impl<T: Clone + 'static> FromRequest for ReqData<T> {
-    type Config = ();
     type Error = Error;
     type Future = Ready<Result<Self, Error>>;
 
diff --git a/src/types/either.rs b/src/types/either.rs
index 35e63cec..5700b63c 100644
--- a/src/types/either.rs
+++ b/src/types/either.rs
@@ -187,7 +187,6 @@ where
 {
     type Error = EitherExtractError<L::Error, R::Error>;
     type Future = EitherExtractFut<L, R>;
-    type Config = ();
 
     fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
         EitherExtractFut {
diff --git a/src/types/form.rs b/src/types/form.rs
index 2ace0e06..71100eb9 100644
--- a/src/types/form.rs
+++ b/src/types/form.rs
@@ -126,20 +126,12 @@ impl<T> FromRequest for Form<T>
 where
     T: DeserializeOwned + 'static,
 {
-    type Config = FormConfig;
     type Error = Error;
     type Future = FormExtractFut<T>;
 
     #[inline]
     fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
-        let (limit, err_handler) = req
-            .app_data::<Self::Config>()
-            .or_else(|| {
-                req.app_data::<web::Data<Self::Config>>()
-                    .map(|d| d.as_ref())
-            })
-            .map(|c| (c.limit, c.err_handler.clone()))
-            .unwrap_or((16384, None));
+        let FormConfig { limit, err_handler } = FormConfig::from_req(req).clone();
 
         FormExtractFut {
             fut: UrlEncoded::new(req, payload).limit(limit),
@@ -241,14 +233,26 @@ impl FormConfig {
         self.err_handler = Some(Rc::new(f));
         self
     }
+
+    /// Extract payload config from app data.
+    ///
+    /// Checks both `T` and `Data<T>`, in that order, and falls back to the default payload config.
+    fn from_req(req: &HttpRequest) -> &Self {
+        req.app_data::<Self>()
+            .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
+            .unwrap_or(&DEFAULT_CONFIG)
+    }
 }
 
+/// Allow shared refs used as default.
+const DEFAULT_CONFIG: FormConfig = FormConfig {
+    limit: 16_384, // 2^14 bytes (~16kB)
+    err_handler: None,
+};
+
 impl Default for FormConfig {
     fn default() -> Self {
-        FormConfig {
-            limit: 16_384, // 2^14 bytes (~16kB)
-            err_handler: None,
-        }
+        DEFAULT_CONFIG
     }
 }
 
diff --git a/src/types/header.rs b/src/types/header.rs
index 9b64f445..6ea77faf 100644
--- a/src/types/header.rs
+++ b/src/types/header.rs
@@ -62,7 +62,6 @@ where
 {
     type Error = ParseError;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = ();
 
     #[inline]
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
diff --git a/src/types/json.rs b/src/types/json.rs
index 8c2f51a6..19443ea9 100644
--- a/src/types/json.rs
+++ b/src/types/json.rs
@@ -130,7 +130,6 @@ impl<T: Serialize> Responder for Json<T> {
 impl<T: DeserializeOwned + 'static> FromRequest for Json<T> {
     type Error = Error;
     type Future = JsonExtractFut<T>;
-    type Config = JsonConfig;
 
     #[inline]
     fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
diff --git a/src/types/path.rs b/src/types/path.rs
index 4052646e..aed897fa 100644
--- a/src/types/path.rs
+++ b/src/types/path.rs
@@ -97,12 +97,11 @@ where
 {
     type Error = Error;
     type Future = Ready<Result<Self, Self::Error>>;
-    type Config = PathConfig;
 
     #[inline]
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         let error_handler = req
-            .app_data::<Self::Config>()
+            .app_data::<PathConfig>()
             .and_then(|c| c.ehandler.clone());
 
         ready(
diff --git a/src/types/payload.rs b/src/types/payload.rs
index 188da620..46ad96be 100644
--- a/src/types/payload.rs
+++ b/src/types/payload.rs
@@ -63,7 +63,6 @@ impl Stream for Payload {
 
 /// See [here](#usage) for example of usage as an extractor.
 impl FromRequest for Payload {
-    type Config = PayloadConfig;
     type Error = Error;
     type Future = Ready<Result<Payload, Error>>;
 
@@ -90,7 +89,6 @@ impl FromRequest for Payload {
 /// }
 /// ```
 impl FromRequest for Bytes {
-    type Config = PayloadConfig;
     type Error = Error;
     type Future = Either<BytesExtractFut, Ready<Result<Bytes, Error>>>;
 
@@ -126,8 +124,7 @@ impl<'a> Future for BytesExtractFut {
 ///
 /// Text extractor automatically decode body according to the request's charset.
 ///
-/// [**PayloadConfig**](PayloadConfig) allows to configure
-/// extraction process.
+/// Use [`PayloadConfig`] to configure extraction process.
 ///
 /// # Examples
 /// ```
@@ -139,7 +136,6 @@ impl<'a> Future for BytesExtractFut {
 ///     format!("Body {}!", text)
 /// }
 impl FromRequest for String {
-    type Config = PayloadConfig;
     type Error = Error;
     type Future = Either<StringExtractFut, Ready<Result<String, Error>>>;
 
@@ -198,14 +194,15 @@ fn bytes_to_string(body: Bytes, encoding: &'static Encoding) -> Result<String, E
 
 /// Configuration for request payloads.
 ///
-/// Applies to the built-in `Bytes` and `String` extractors. Note that the `Payload` extractor does
-/// not automatically check conformance with this configuration to allow more flexibility when
-/// building extractors on top of `Payload`.
+/// Applies to the built-in [`Bytes`] and [`String`] extractors.
+/// Note that the [`Payload`] extractor does not automatically check
+/// conformance with this configuration to allow more flexibility when
+/// building extractors on top of [`Payload`].
 ///
 /// By default, the payload size limit is 256kB and there is no mime type condition.
 ///
-/// To use this, add an instance of it to your app or service through one of the
-/// `.app_data()` methods.
+/// To use this, add an instance of it to your [`app`](crate::App), [`scope`](crate::Scope)
+/// or [`resource`](crate::Resource) through the associated `.app_data()` method.
 #[derive(Clone)]
 pub struct PayloadConfig {
     limit: usize,
diff --git a/src/types/query.rs b/src/types/query.rs
index 73d08d09..eed33719 100644
--- a/src/types/query.rs
+++ b/src/types/query.rs
@@ -109,12 +109,11 @@ impl<T: fmt::Display> fmt::Display for Query<T> {
 impl<T: DeserializeOwned> FromRequest for Query<T> {
     type Error = Error;
     type Future = Ready<Result<Self, Error>>;
-    type Config = QueryConfig;
 
     #[inline]
     fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
         let error_handler = req
-            .app_data::<Self::Config>()
+            .app_data::<QueryConfig>()
             .and_then(|c| c.err_handler.clone());
 
         serde_urlencoded::from_str::<T>(req.query_string())