Optional extractor extensions (#685)

* Fix inconsistent indentation in axum/Cargo.toml

* Make MatchedPath and OriginalUri extractors and logic for them optional

… they add some runtime cost to handling any request that goes through
a Router, which only makes sense if they actually get used.

Enable both features by default for convenience.
This commit is contained in:
Jonas Platte 2022-01-03 16:28:33 +01:00 committed by David Pedersen
parent d97f2aeae6
commit e0a463b463
5 changed files with 41 additions and 21 deletions

View file

@ -11,11 +11,13 @@ readme = "README.md"
repository = "https://github.com/tokio-rs/axum"
[features]
default = ["http1", "json", "tower-log"]
default = ["http1", "json", "matched-path", "original-uri", "tower-log"]
http1 = ["hyper/http1"]
http2 = ["hyper/http2"]
json = ["serde_json"]
matched-path = []
multipart = ["multer"]
original-uri = []
tower-log = ["tower/log"]
ws = ["tokio-tungstenite", "sha-1", "base64"]
@ -84,10 +86,10 @@ rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.playground]
features = [
"http1",
"http2",
"json",
"multipart",
"tower",
"ws",
"http1",
"http2",
"json",
"multipart",
"tower",
"ws",
]

View file

@ -15,7 +15,6 @@ pub mod ws;
mod content_length_limit;
mod extension;
mod form;
mod matched_path;
mod query;
mod raw_query;
mod request_parts;
@ -31,17 +30,23 @@ pub use self::{
extension::Extension,
extractor_middleware::extractor_middleware,
form::Form,
matched_path::MatchedPath,
path::Path,
query::Query,
raw_query::RawQuery,
request_parts::OriginalUri,
request_parts::{BodyStream, RawBody},
};
#[doc(no_inline)]
#[cfg(feature = "json")]
pub use crate::Json;
#[cfg(feature = "matched-path")]
mod matched_path;
#[cfg(feature = "matched-path")]
#[cfg_attr(docsrs, doc(cfg(feature = "matched-path")))]
#[doc(inline)]
pub use self::matched_path::MatchedPath;
#[cfg(feature = "multipart")]
#[cfg_attr(docsrs, doc(cfg(feature = "multipart")))]
pub mod multipart;
@ -51,6 +56,11 @@ pub mod multipart;
#[doc(inline)]
pub use self::multipart::Multipart;
#[cfg(feature = "original-uri")]
#[cfg_attr(docsrs, doc(cfg(feature = "original-uri")))]
#[doc(inline)]
pub use self::request_parts::OriginalUri;
#[cfg(feature = "ws")]
#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
#[doc(inline)]

View file

@ -80,9 +80,11 @@ use sync_wrapper::SyncWrapper;
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
/// # };
/// ```
#[cfg(feature = "original-uri")]
#[derive(Debug, Clone)]
pub struct OriginalUri(pub Uri);
#[cfg(feature = "original-uri")]
#[async_trait]
impl<B> FromRequest<B> for OriginalUri
where

View file

@ -375,12 +375,16 @@
//! `http1` | Enables hyper's `http1` feature | Yes
//! `http2` | Enables hyper's `http2` feature | No
//! `json` | Enables the [`Json`] type and some similar convenience functionality | Yes
//! `matched-path` | Enables capturing of every request's router path and the [`MatchedPath`] extractor | Yes
//! `multipart` | Enables parsing `multipart/form-data` requests with [`Multipart`] | No
//! `original-uri` | Enables capturing of every request's original URI and the [`OriginalUri`] extractor | Yes
//! `tower-log` | Enables `tower`'s `log` feature | Yes
//! `ws` | Enables WebSockets support via [`extract::ws`] | No
//!
//! [`TypedHeader`]: crate::extract::TypedHeader
//! [`MatchedPath`]: crate::extract::MatchedPath
//! [`Multipart`]: crate::extract::Multipart
//! [`OriginalUri`]: crate::extract::OriginalUri
//! [`tower`]: https://crates.io/crates/tower
//! [`tower-http`]: https://crates.io/crates/tower-http
//! [`tokio`]: http://crates.io/crates/tokio
@ -392,7 +396,6 @@
//! [examples]: https://github.com/tokio-rs/axum/tree/main/examples
//! [`Router::merge`]: crate::routing::Router::merge
//! [`axum::Server`]: hyper::server::Server
//! [`OriginalUri`]: crate::extract::OriginalUri
//! [`Service`]: tower::Service
//! [`Service::poll_ready`]: tower::Service::poll_ready
//! [`Service`'s]: tower::Service

View file

@ -3,13 +3,8 @@
use self::{future::RouterFuture, not_found::NotFound};
use crate::{
body::{boxed, Body, Bytes, HttpBody},
extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
MatchedPath, OriginalUri,
},
response::IntoResponse,
response::Redirect,
response::Response,
extract::connect_info::{Connected, IntoMakeServiceWithConnectInfo},
response::{Response, Redirect, IntoResponse},
routing::strip_prefix::StripPrefix,
util::{try_downcast, ByteStr, PercentDecodedByteStr},
BoxError,
@ -405,7 +400,10 @@ where
let id = *match_.value;
req.extensions_mut().insert(id);
#[cfg(feature = "matched-path")]
if let Some(matched_path) = self.node.route_id_to_path.get(&id) {
use crate::extract::MatchedPath;
let matched_path = if let Some(previous) = req.extensions_mut().get::<MatchedPath>() {
// a previous `MatchedPath` might exist if we're inside a nested Router
let previous = if let Some(previous) =
@ -476,9 +474,14 @@ where
#[inline]
fn call(&mut self, mut req: Request<B>) -> Self::Future {
if req.extensions().get::<OriginalUri>().is_none() {
let original_uri = OriginalUri(req.uri().clone());
req.extensions_mut().insert(original_uri);
#[cfg(feature = "original-uri")]
{
use crate::extract::OriginalUri;
if req.extensions().get::<OriginalUri>().is_none() {
let original_uri = OriginalUri(req.uri().clone());
req.extensions_mut().insert(original_uri);
}
}
let path = req.uri().path().to_owned();