checkpoint

This commit is contained in:
David Pedersen
2022-07-03 15:56:48 +02:00
parent 63560e0299
commit a5b6b94530
23 changed files with 256 additions and 185 deletions
+27 -28
View File
@@ -60,29 +60,30 @@ mod tuple;
/// [`http::Request<B>`]: http::Request
/// [`axum::extract`]: https://docs.rs/axum/latest/axum/extract/index.html
#[async_trait]
pub trait FromRequest<B>: Sized {
pub trait FromRequest<S, B>: Sized {
/// If the extractor fails it'll use this "rejection" type. A rejection is
/// a kind of error that can be converted into a response.
type Rejection: IntoResponse;
/// Perform the extraction.
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection>;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection>;
}
/// The type used with [`FromRequest`] to extract data from requests.
///
/// Has several convenience methods for getting owned parts of the request.
#[derive(Debug)]
pub struct RequestParts<B> {
pub struct RequestParts<S, B> {
method: Method,
uri: Uri,
version: Version,
headers: HeaderMap,
extensions: Extensions,
body: Option<B>,
state: S,
}
impl<B> RequestParts<B> {
impl<S, B> RequestParts<S, B> {
/// Create a new `RequestParts`.
///
/// You generally shouldn't need to construct this type yourself, unless
@@ -90,7 +91,10 @@ impl<B> RequestParts<B> {
/// [`tower::Service`].
///
/// [`tower::Service`]: https://docs.rs/tower/lastest/tower/trait.Service.html
pub fn new(req: Request<B>) -> Self {
pub fn new(state: S, req: Request<B>) -> Self
where
S: Send + Sync + 'static,
{
let (
http::request::Parts {
method,
@@ -110,6 +114,7 @@ impl<B> RequestParts<B> {
headers,
extensions,
body: Some(body),
state,
}
}
@@ -141,7 +146,10 @@ impl<B> RequestParts<B> {
/// }
/// }
/// ```
pub async fn extract<E: FromRequest<B>>(&mut self) -> Result<E, E::Rejection> {
pub async fn extract<E>(&mut self) -> Result<E, E::Rejection>
where
E: FromRequest<S, B>,
{
E::from_request(self).await
}
@@ -159,6 +167,7 @@ impl<B> RequestParts<B> {
headers,
extensions,
mut body,
state: _,
} = self;
let mut req = if let Some(body) = body.take() {
@@ -245,46 +254,36 @@ impl<B> RequestParts<B> {
pub fn take_body(&mut self) -> Option<B> {
self.body.take()
}
pub fn state(&self) -> &S {
&self.state
}
}
#[async_trait]
impl<T, B> FromRequest<B> for Option<T>
impl<T, S, B> FromRequest<S, B> for Option<T>
where
T: FromRequest<B>,
T: FromRequest<S, B>,
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Option<T>, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Option<T>, Self::Rejection> {
Ok(T::from_request(req).await.ok())
}
}
#[async_trait]
impl<T, B> FromRequest<B> for Result<T, T::Rejection>
impl<T, S, B> FromRequest<S, B> for Result<T, T::Rejection>
where
T: FromRequest<B>,
T: FromRequest<S, B>,
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(T::from_request(req).await)
}
}
/// TODO(david): docs
#[derive(Clone, Copy, Debug, Default)]
pub struct State<S>(pub S);
#[async_trait]
impl<S, B> FromRequest<B> for State<S>
where
B: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
todo!()
}
}
+26 -17
View File
@@ -6,13 +6,14 @@ use http::{Extensions, HeaderMap, Method, Request, Uri, Version};
use std::convert::Infallible;
#[async_trait]
impl<B> FromRequest<B> for Request<B>
impl<S, B> FromRequest<S, B> for Request<B>
where
B: Send,
S: Clone + Send,
{
type Rejection = BodyAlreadyExtracted;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let req = std::mem::replace(
req,
RequestParts {
@@ -22,6 +23,7 @@ where
headers: HeaderMap::new(),
extensions: Extensions::default(),
body: None,
state: req.state.clone(),
},
);
@@ -30,37 +32,40 @@ where
}
#[async_trait]
impl<B> FromRequest<B> for Method
impl<S, B> FromRequest<S, B> for Method
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.method().clone())
}
}
#[async_trait]
impl<B> FromRequest<B> for Uri
impl<S, B> FromRequest<S, B> for Uri
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.uri().clone())
}
}
#[async_trait]
impl<B> FromRequest<B> for Version
impl<S, B> FromRequest<S, B> for Version
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.version())
}
}
@@ -71,27 +76,29 @@ where
///
/// [`TypedHeader`]: https://docs.rs/axum/latest/axum/extract/struct.TypedHeader.html
#[async_trait]
impl<B> FromRequest<B> for HeaderMap
impl<S, B> FromRequest<S, B> for HeaderMap
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(req.headers().clone())
}
}
#[async_trait]
impl<B> FromRequest<B> for Bytes
impl<S, B> FromRequest<S, B> for Bytes
where
B: http_body::Body + Send,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = BytesRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?;
let bytes = crate::body::to_bytes(body)
@@ -103,15 +110,16 @@ where
}
#[async_trait]
impl<B> FromRequest<B> for String
impl<S, B> FromRequest<S, B> for String
where
B: http_body::Body + Send,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = StringRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?;
let bytes = crate::body::to_bytes(body)
@@ -126,13 +134,14 @@ where
}
#[async_trait]
impl<B> FromRequest<B> for http::request::Parts
impl<S, B> FromRequest<S, B> for http::request::Parts
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let method = unwrap_infallible(Method::from_request(req).await);
let uri = unwrap_infallible(Uri::from_request(req).await);
let version = unwrap_infallible(Version::from_request(req).await);
@@ -159,6 +168,6 @@ fn unwrap_infallible<T>(result: Result<T, Infallible>) -> T {
}
}
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
pub(crate) fn take_body<S, B>(req: &mut RequestParts<S, B>) -> Result<B, BodyAlreadyExtracted> {
req.take_body().ok_or(BodyAlreadyExtracted)
}
+7 -5
View File
@@ -4,13 +4,14 @@ use async_trait::async_trait;
use std::convert::Infallible;
#[async_trait]
impl<B> FromRequest<B> for ()
impl<S, B> FromRequest<S, B> for ()
where
S: Send,
B: Send,
{
type Rejection = Infallible;
async fn from_request(_: &mut RequestParts<B>) -> Result<(), Self::Rejection> {
async fn from_request(_: &mut RequestParts<S, B>) -> Result<(), Self::Rejection> {
Ok(())
}
}
@@ -21,14 +22,15 @@ macro_rules! impl_from_request {
( $($ty:ident),* $(,)? ) => {
#[async_trait]
#[allow(non_snake_case)]
impl<B, $($ty,)*> FromRequest<B> for ($($ty,)*)
impl<S, B, $($ty,)*> FromRequest<S, B> for ($($ty,)*)
where
$( $ty: FromRequest<B> + Send, )*
$( $ty: FromRequest<S, B> + Send, )*
S: Send,
B: Send,
{
type Rejection = Response;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
$( let $ty = $ty::from_request(req).await.map_err(|err| err.into_response())?; )*
Ok(($($ty,)*))
}
+27 -25
View File
@@ -22,6 +22,8 @@ use tower_service::Service;
/// that handles errors by converting them into responses.
///
/// See [module docs](self) for more details on axum's error handling model.
// TODO(david): cannot access state, is that bad? It leads to inference issues and one has to
// specify the type manually and risk getting it wrong. So its basically an Extension at that point
pub struct HandleErrorLayer<F, T> {
f: F,
_extractor: PhantomData<fn() -> T>,
@@ -49,7 +51,7 @@ where
}
}
impl<F, E> fmt::Debug for HandleErrorLayer<F, E> {
impl<F, T> fmt::Debug for HandleErrorLayer<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HandleErrorLayer")
.field("f", &format_args!("{}", std::any::type_name::<F>()))
@@ -57,13 +59,13 @@ impl<F, E> fmt::Debug for HandleErrorLayer<F, E> {
}
}
impl<S, F, T> Layer<S> for HandleErrorLayer<F, T>
impl<Svc, F, T> Layer<Svc> for HandleErrorLayer<F, T>
where
F: Clone,
{
type Service = HandleError<S, F, T>;
type Service = HandleError<Svc, F, T>;
fn layer(&self, inner: S) -> Self::Service {
fn layer(&self, inner: Svc) -> Self::Service {
HandleError::new(inner, self.f.clone())
}
}
@@ -71,15 +73,15 @@ where
/// A [`Service`] adapter that handles errors by converting them into responses.
///
/// See [module docs](self) for more details on axum's error handling model.
pub struct HandleError<S, F, T> {
inner: S,
pub struct HandleError<Svc, F, T> {
inner: Svc,
f: F,
_extractor: PhantomData<fn() -> T>,
}
impl<S, F, T> HandleError<S, F, T> {
impl<Svc, F, T> HandleError<Svc, F, T> {
/// Create a new `HandleError`.
pub fn new(inner: S, f: F) -> Self {
pub fn new(inner: Svc, f: F) -> Self {
Self {
inner,
f,
@@ -88,9 +90,9 @@ impl<S, F, T> HandleError<S, F, T> {
}
}
impl<S, F, T> Clone for HandleError<S, F, T>
impl<Svc, F, T> Clone for HandleError<Svc, F, T>
where
S: Clone,
Svc: Clone,
F: Clone,
{
fn clone(&self) -> Self {
@@ -102,9 +104,9 @@ where
}
}
impl<S, F, E> fmt::Debug for HandleError<S, F, E>
impl<Svc, F, E> fmt::Debug for HandleError<Svc, F, E>
where
S: fmt::Debug,
Svc: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HandleError")
@@ -114,12 +116,12 @@ where
}
}
impl<S, F, ReqBody, ResBody, Fut, Res> Service<Request<ReqBody>> for HandleError<S, F, ()>
impl<Svc, F, ReqBody, ResBody, Fut, Res> Service<Request<ReqBody>> for HandleError<Svc, F, ()>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Error: Send,
S::Future: Send,
F: FnOnce(S::Error) -> Fut + Clone + Send + 'static,
Svc: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
Svc::Error: Send,
Svc::Future: Send,
F: FnOnce(Svc::Error) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
ReqBody: Send + 'static,
@@ -154,16 +156,16 @@ where
#[allow(unused_macros)]
macro_rules! impl_service {
( $($ty:ident),* $(,)? ) => {
impl<S, F, ReqBody, ResBody, Res, Fut, $($ty,)*> Service<Request<ReqBody>>
for HandleError<S, F, ($($ty,)*)>
impl<Svc, F, ReqBody, ResBody, Res, Fut, $($ty,)*> Service<Request<ReqBody>>
for HandleError<Svc, F, ($($ty,)*)>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Error: Send,
S::Future: Send,
F: FnOnce($($ty),*, S::Error) -> Fut + Clone + Send + 'static,
Svc: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
Svc::Error: Send,
Svc::Future: Send,
F: FnOnce($($ty),*, Svc::Error) -> Fut + Clone + Send + 'static,
Fut: Future<Output = Res> + Send,
Res: IntoResponse,
$( $ty: FromRequest<ReqBody> + Send,)*
$( $ty: FromRequest<(), ReqBody> + Send,)*
ReqBody: Send + 'static,
ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>,
@@ -185,7 +187,7 @@ macro_rules! impl_service {
let inner = std::mem::replace(&mut self.inner, clone);
let future = Box::pin(async move {
let mut req = RequestParts::new(req);
let mut req = RequestParts::new((), req);
$(
let $ty = match $ty::from_request(&mut req).await {
+3 -2
View File
@@ -73,14 +73,15 @@ use tower_service::Service;
pub struct Extension<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Extension<T>
impl<T, S, B> FromRequest<S, B> for Extension<T>
where
T: Clone + Send + Sync + 'static,
B: Send,
S: Send,
{
type Rejection = ExtensionRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let value = req
.extensions()
.get::<T>()
+4 -3
View File
@@ -128,14 +128,15 @@ opaque_future! {
pub struct ConnectInfo<T>(pub T);
#[async_trait]
impl<B, T> FromRequest<B> for ConnectInfo<T>
impl<B, S, T> FromRequest<S, B> for ConnectInfo<T>
where
B: Send,
T: Clone + Send + Sync + 'static,
S: Send,
{
type Rejection = <Extension<Self> as FromRequest<B>>::Rejection;
type Rejection = <Extension<Self> as FromRequest<S, B>>::Rejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let Extension(connect_info) = Extension::<Self>::from_request(req).await?;
Ok(connect_info)
}
+4 -3
View File
@@ -36,15 +36,16 @@ use std::ops::Deref;
pub struct ContentLengthLimit<T, const N: u64>(pub T);
#[async_trait]
impl<T, B, const N: u64> FromRequest<B> for ContentLengthLimit<T, N>
impl<T, S, B, const N: u64> FromRequest<S, B> for ContentLengthLimit<T, N>
where
T: FromRequest<B>,
T: FromRequest<S, B>,
T::Rejection: IntoResponse,
B: Send,
S: Send,
{
type Rejection = ContentLengthLimitRejection<T::Rejection>;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let content_length = req
.headers()
.get(http::header::CONTENT_LENGTH)
+3 -2
View File
@@ -21,13 +21,14 @@ const X_FORWARDED_HOST_HEADER_KEY: &str = "X-Forwarded-Host";
pub struct Host(pub String);
#[async_trait]
impl<B> FromRequest<B> for Host
impl<S, B> FromRequest<S, B> for Host
where
B: Send,
S: Send,
{
type Rejection = HostRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if let Some(host) = parse_forwarded(req.headers()) {
return Ok(Host(host.to_owned()));
}
+3 -2
View File
@@ -64,13 +64,14 @@ impl MatchedPath {
}
#[async_trait]
impl<B> FromRequest<B> for MatchedPath
impl<S, B> FromRequest<S, B> for MatchedPath
where
B: Send,
S: Send,
{
type Rejection = MatchedPathRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let matched_path = req
.extensions()
.get::<Self>()
+6 -4
View File
@@ -14,9 +14,10 @@ mod content_length_limit;
mod host;
mod raw_query;
mod request_parts;
mod state;
#[doc(inline)]
pub use axum_core::extract::{FromRequest, RequestParts, State};
pub use axum_core::extract::{FromRequest, RequestParts};
#[doc(inline)]
#[allow(deprecated)]
@@ -27,6 +28,7 @@ pub use self::{
path::Path,
raw_query::RawQuery,
request_parts::{BodyStream, RawBody},
state::State,
};
#[doc(no_inline)]
@@ -73,13 +75,13 @@ pub use self::ws::WebSocketUpgrade;
#[doc(no_inline)]
pub use crate::TypedHeader;
pub(crate) fn take_body<B>(req: &mut RequestParts<B>) -> Result<B, BodyAlreadyExtracted> {
pub(crate) fn take_body<S, B>(req: &mut RequestParts<S, B>) -> Result<B, BodyAlreadyExtracted> {
req.take_body().ok_or_else(BodyAlreadyExtracted::default)
}
// this is duplicated in `axum-extra/src/extract/form.rs`
pub(super) fn has_content_type<B>(
req: &RequestParts<B>,
pub(super) fn has_content_type<S, B>(
req: &RequestParts<S, B>,
expected_content_type: &mime::Mime,
) -> bool {
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
+3 -2
View File
@@ -50,14 +50,15 @@ pub struct Multipart {
}
#[async_trait]
impl<B> FromRequest<B> for Multipart
impl<S, B> FromRequest<S, B> for Multipart
where
B: HttpBody<Data = Bytes> + Default + Unpin + Send + 'static,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = MultipartRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let stream = BodyStream::from_request(req).await?;
let headers = req.headers();
let boundary = parse_boundary(headers).ok_or(InvalidBoundary)?;
+3 -2
View File
@@ -163,14 +163,15 @@ impl<T> DerefMut for Path<T> {
}
#[async_trait]
impl<T, B> FromRequest<B> for Path<T>
impl<T, S, B> FromRequest<S, B> for Path<T>
where
T: DeserializeOwned + Send,
B: Send,
S: Send,
{
type Rejection = PathRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let params = match req.extensions_mut().get::<UrlParams>() {
Some(UrlParams::Params(params)) => params,
Some(UrlParams::InvalidUtf8InPathParam { key }) => {
+9 -4
View File
@@ -49,14 +49,15 @@ use std::ops::Deref;
pub struct Query<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Query<T>
impl<T, S, B> FromRequest<S, B> for Query<T>
where
T: DeserializeOwned,
B: Send,
S: Send,
{
type Rejection = QueryRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let query = req.uri().query().unwrap_or_default();
let value = serde_urlencoded::from_str(query)
.map_err(FailedToDeserializeQueryString::__private_new::<T, _>)?;
@@ -80,8 +81,12 @@ mod tests {
use serde::Deserialize;
use std::fmt::Debug;
async fn check<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
let mut req = RequestParts::new(Request::builder().uri(uri.as_ref()).body(()).unwrap());
async fn check<T>(uri: impl AsRef<str>, value: T)
where
T: DeserializeOwned + PartialEq + Debug,
{
let req = Request::builder().uri(uri.as_ref()).body(()).unwrap();
let mut req = RequestParts::new((), req);
assert_eq!(Query::<T>::from_request(&mut req).await.unwrap().0, value);
}
+3 -2
View File
@@ -27,13 +27,14 @@ use std::convert::Infallible;
pub struct RawQuery(pub Option<String>);
#[async_trait]
impl<B> FromRequest<B> for RawQuery
impl<S, B> FromRequest<S, B> for RawQuery
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let query = req.uri().query().map(|query| query.to_owned());
Ok(Self(query))
}
+9 -6
View File
@@ -86,13 +86,14 @@ pub struct OriginalUri(pub Uri);
#[cfg(feature = "original-uri")]
#[async_trait]
impl<B> FromRequest<B> for OriginalUri
impl<S, B> FromRequest<S, B> for OriginalUri
where
B: Send,
S: Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let uri = Extension::<Self>::from_request(req)
.await
.unwrap_or_else(|_| Extension(OriginalUri(req.uri().clone())))
@@ -140,15 +141,16 @@ impl Stream for BodyStream {
}
#[async_trait]
impl<B> FromRequest<B> for BodyStream
impl<S, B> FromRequest<S, B> for BodyStream
where
B: HttpBody + Send + 'static,
B::Data: Into<Bytes>,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = BodyAlreadyExtracted;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?
.map_data(Into::into)
.map_err(|err| Error::new(err.into()));
@@ -196,13 +198,14 @@ fn body_stream_traits() {
pub struct RawBody<B = Body>(pub B);
#[async_trait]
impl<B> FromRequest<B> for RawBody<B>
impl<S, B> FromRequest<S, B> for RawBody<B>
where
B: Send,
S: Send,
{
type Rejection = BodyAlreadyExtracted;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
let body = take_body(req)?;
Ok(Self(body))
}
+20
View File
@@ -0,0 +1,20 @@
use super::{FromRequest, RequestParts};
use async_trait::async_trait;
use std::convert::Infallible;
/// TODO(david): docs
#[derive(Clone, Copy, Debug, Default)]
pub struct State<S>(pub S);
#[async_trait]
impl<S, B> FromRequest<S, B> for State<S>
where
B: Send,
S: Clone + Send,
{
type Rejection = Infallible;
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
Ok(Self(req.state().clone()))
}
}
+5 -4
View File
@@ -244,13 +244,14 @@ impl WebSocketUpgrade {
}
#[async_trait]
impl<B> FromRequest<B> for WebSocketUpgrade
impl<S, B> FromRequest<S, B> for WebSocketUpgrade
where
B: Send,
S: Send,
{
type Rejection = WebSocketUpgradeRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if req.method() != Method::GET {
return Err(MethodNotGet.into());
}
@@ -288,7 +289,7 @@ where
}
}
fn header_eq<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) -> bool {
fn header_eq<S, B>(req: &RequestParts<S, B>, key: HeaderName, value: &'static str) -> bool {
if let Some(header) = req.headers().get(&key) {
header.as_bytes().eq_ignore_ascii_case(value.as_bytes())
} else {
@@ -296,7 +297,7 @@ fn header_eq<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) ->
}
}
fn header_contains<B>(req: &RequestParts<B>, key: HeaderName, value: &'static str) -> bool {
fn header_contains<S, B>(req: &RequestParts<S, B>, key: HeaderName, value: &'static str) -> bool {
let header = if let Some(header) = req.headers().get(&key) {
header
} else {
+34 -36
View File
@@ -56,16 +56,17 @@ use std::ops::Deref;
pub struct Form<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Form<T>
impl<T, S, B> FromRequest<S, B> for Form<T>
where
T: DeserializeOwned,
B: HttpBody + Send,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = FormRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if req.method() == Method::GET {
let query = req.uri().query().unwrap_or_default();
let value = serde_urlencoded::from_str(query)
@@ -125,29 +126,27 @@ mod tests {
}
async fn check_query<T: DeserializeOwned + PartialEq + Debug>(uri: impl AsRef<str>, value: T) {
let mut req = RequestParts::new(
Request::builder()
.uri(uri.as_ref())
.body(Empty::<Bytes>::new())
.unwrap(),
);
let req = Request::builder()
.uri(uri.as_ref())
.body(Empty::<Bytes>::new())
.unwrap();
let mut req = RequestParts::new((), req);
assert_eq!(Form::<T>::from_request(&mut req).await.unwrap().0, value);
}
async fn check_body<T: Serialize + DeserializeOwned + PartialEq + Debug>(value: T) {
let mut req = RequestParts::new(
Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&value).unwrap().into(),
))
.unwrap(),
);
let req = Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(
http::header::CONTENT_TYPE,
mime::APPLICATION_WWW_FORM_URLENCODED.as_ref(),
)
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&value).unwrap().into(),
))
.unwrap();
let mut req = RequestParts::new((), req);
assert_eq!(Form::<T>::from_request(&mut req).await.unwrap().0, value);
}
@@ -204,21 +203,20 @@ mod tests {
#[tokio::test]
async fn test_incorrect_content_type() {
let mut req = RequestParts::new(
Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&Pagination {
size: Some(10),
page: None,
})
.unwrap()
.into(),
))
.unwrap(),
);
let req = Request::builder()
.uri("http://example.com/test")
.method(Method::POST)
.header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())
.body(Full::<Bytes>::new(
serde_urlencoded::to_string(&Pagination {
size: Some(10),
page: None,
})
.unwrap()
.into(),
))
.unwrap();
let mut req = RequestParts::new((), req);
assert!(matches!(
Form::<Pagination>::from_request(&mut req)
.await
+3 -2
View File
@@ -244,13 +244,14 @@ macro_rules! impl_handler {
Fut: Future<Output = Res> + Send,
B: Send + 'static,
Res: IntoResponse,
$( $ty: FromRequest<B> + Send,)*
$( $ty: FromRequest<S, B> + Send,)*
S: Send + Sync + 'static
{
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
fn call(self, state: S, req: Request<B>) -> Self::Future {
Box::pin(async move {
let mut req = RequestParts::new(req);
let mut req = RequestParts::new(state, req);
$(
let $ty = match $ty::from_request(&mut req).await {
+4 -3
View File
@@ -93,16 +93,17 @@ use std::ops::{Deref, DerefMut};
pub struct Json<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Json<T>
impl<T, S, B> FromRequest<S, B> for Json<T>
where
T: DeserializeOwned,
B: HttpBody + Send,
B::Data: Send,
B::Error: Into<BoxError>,
S: Send,
{
type Rejection = JsonRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if json_content_type(req) {
let bytes = Bytes::from_request(req).await?;
@@ -135,7 +136,7 @@ where
}
}
fn json_content_type<B>(req: &RequestParts<B>) -> bool {
fn json_content_type<S, B>(req: &RequestParts<S, B>) -> bool {
let content_type = if let Some(content_type) = req.headers().get(header::CONTENT_TYPE) {
content_type
} else {
+45 -31
View File
@@ -116,10 +116,10 @@ impl<E> fmt::Debug for FromExtractorLayer<E> {
}
}
impl<E, S> Layer<S> for FromExtractorLayer<E> {
type Service = FromExtractor<S, E>;
impl<E, T> Layer<T> for FromExtractorLayer<E> {
type Service = FromExtractor<T, E>;
fn layer(&self, inner: S) -> Self::Service {
fn layer(&self, inner: T) -> Self::Service {
FromExtractor {
inner,
_extractor: PhantomData,
@@ -130,8 +130,8 @@ impl<E, S> Layer<S> for FromExtractorLayer<E> {
/// Middleware that runs an extractor and discards the value.
///
/// See [`from_extractor`] for more details.
pub struct FromExtractor<S, E> {
inner: S,
pub struct FromExtractor<T, E> {
inner: T,
_extractor: PhantomData<fn() -> E>,
}
@@ -142,9 +142,9 @@ fn traits() {
assert_sync::<FromExtractor<(), NotSendSync>>();
}
impl<S, E> Clone for FromExtractor<S, E>
impl<T, E> Clone for FromExtractor<T, E>
where
S: Clone,
T: Clone,
{
fn clone(&self) -> Self {
Self {
@@ -154,9 +154,9 @@ where
}
}
impl<S, E> fmt::Debug for FromExtractor<S, E>
impl<T, E> fmt::Debug for FromExtractor<T, E>
where
S: fmt::Debug,
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FromExtractor")
@@ -166,17 +166,17 @@ where
}
}
impl<S, E, ReqBody, ResBody> Service<Request<ReqBody>> for FromExtractor<S, E>
impl<T, E, ReqBody, ResBody> Service<Request<ReqBody>> for FromExtractor<T, E>
where
E: FromRequest<ReqBody> + 'static,
E: FromRequest<(), ReqBody> + 'static,
ReqBody: Default + Send + 'static,
S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone,
T: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone,
ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>,
{
type Response = Response;
type Error = S::Error;
type Future = ResponseFuture<ReqBody, S, E>;
type Error = T::Error;
type Future = ResponseFuture<ReqBody, T, E>;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -185,7 +185,7 @@ where
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
let extract_future = Box::pin(async move {
let mut req = RequestParts::new(req);
let mut req = RequestParts::new((), req);
let extracted = E::from_request(&mut req).await;
(req, extracted)
});
@@ -202,38 +202,41 @@ where
pin_project! {
/// Response future for [`FromExtractor`].
#[allow(missing_debug_implementations)]
pub struct ResponseFuture<ReqBody, S, E>
pub struct ResponseFuture<ReqBody, T, E>
where
E: FromRequest<ReqBody>,
S: Service<Request<ReqBody>>,
E: FromRequest<(), ReqBody>,
T: Service<Request<ReqBody>>,
{
#[pin]
state: State<ReqBody, S, E>,
svc: Option<S>,
state: State<ReqBody, T, E>,
svc: Option<T>,
}
}
pin_project! {
#[project = StateProj]
enum State<ReqBody, S, E>
enum State<ReqBody, T, E>
where
E: FromRequest<ReqBody>,
S: Service<Request<ReqBody>>,
E: FromRequest<(), ReqBody>,
T: Service<Request<ReqBody>>,
{
Extracting { future: BoxFuture<'static, (RequestParts<ReqBody>, Result<E, E::Rejection>)> },
Call { #[pin] future: S::Future },
Extracting {
future: BoxFuture<'static, (RequestParts<(), ReqBody>, Result<E, E::Rejection>)>
},
Call { #[pin] future: T::Future },
Error { response: Option<Response> }
}
}
impl<ReqBody, S, E, ResBody> Future for ResponseFuture<ReqBody, S, E>
impl<ReqBody, T, E, ResBody> Future for ResponseFuture<ReqBody, T, E>
where
E: FromRequest<ReqBody>,
S: Service<Request<ReqBody>, Response = Response<ResBody>>,
E: FromRequest<(), ReqBody>,
T: Service<Request<ReqBody>, Response = Response<ResBody>>,
ReqBody: Default,
ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>,
{
type Output = Result<Response, S::Error>;
type Output = Result<Response, T::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
@@ -261,6 +264,11 @@ where
.poll(cx)
.map(|result| result.map(|response| response.map(crate::body::boxed)));
}
StateProj::Error { response } => {
return Poll::Ready(Ok(response
.take()
.expect("future polled after completion")))
}
};
this.state.set(new_state);
@@ -279,13 +287,14 @@ mod tests {
struct RequireAuth;
#[async_trait::async_trait]
impl<B> FromRequest<B> for RequireAuth
impl<S, B> FromRequest<S, B> for RequireAuth
where
B: Send,
S: Send,
{
type Rejection = StatusCode;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
if let Some(auth) = req
.headers()
.get(header::AUTHORIZATION)
@@ -317,4 +326,9 @@ mod tests {
.await;
assert_eq!(res.status(), StatusCode::OK);
}
#[test]
fn extracting_state() {
todo!()
}
}
+5
View File
@@ -313,4 +313,9 @@ mod tests {
let body = hyper::body::to_bytes(res).await.unwrap();
assert_eq!(&body[..], b"ok");
}
#[test]
fn extracting_state() {
todo!()
}
}
+3 -2
View File
@@ -52,14 +52,15 @@ use std::{convert::Infallible, ops::Deref};
pub struct TypedHeader<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for TypedHeader<T>
impl<T, S, B> FromRequest<S, B> for TypedHeader<T>
where
T: headers::Header,
B: Send,
S: Send,
{
type Rejection = TypedHeaderRejection;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
match req.headers().typed_try_get::<T>() {
Ok(Some(value)) => Ok(Self(value)),
Ok(None) => Err(TypedHeaderRejection {