Skip to content

Commit 0f07c7a

Browse files
committed
DOES NOT WORK stateless from request parts
1 parent b7f815f commit 0f07c7a

File tree

5 files changed

+173
-13
lines changed

5 files changed

+173
-13
lines changed

axum-core/src/extract/mod.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,21 @@ pub use self::{
2828
/// type used with axum.
2929
pub type Request<T = Body> = http::Request<T>;
3030

31-
mod private {
31+
pub mod private {
3232
#[derive(Debug, Clone, Copy)]
3333
pub enum ViaParts {}
3434

35+
#[derive(Debug, Clone, Copy)]
36+
pub enum ViaStatelessParts {}
37+
3538
#[derive(Debug, Clone, Copy)]
3639
pub enum ViaRequest {}
40+
41+
#[derive(Debug, Clone, Copy)]
42+
pub enum WithState {}
43+
44+
#[derive(Debug, Clone, Copy)]
45+
pub enum Stateless {}
3746
}
3847

3948
/// Types that can be created from request parts.
@@ -53,7 +62,7 @@ mod private {
5362
note = "Function argument is not a valid axum extractor. \nSee `https://docs.rs/axum/0.8/axum/extract/index.html` for details",
5463
)
5564
)]
56-
pub trait FromRequestParts<S>: Sized {
65+
pub trait FromRequestParts<S, Via = private::WithState>: Sized {
5766
/// If the extractor fails it'll use this "rejection" type. A rejection is
5867
/// a kind of error that can be converted into a response.
5968
type Rejection: IntoResponse;
@@ -65,6 +74,32 @@ pub trait FromRequestParts<S>: Sized {
6574
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
6675
}
6776

77+
/// Like `FromRequestParts` but without `State`.
78+
pub trait FromStatelessRequestParts: Sized {
79+
/// If the extractor fails it'll use this "rejection" type. A rejection is
80+
/// a kind of error that can be converted into a response.
81+
type Rejection: IntoResponse;
82+
83+
/// Perform the extraction.
84+
fn from_request_parts(
85+
parts: &mut Parts,
86+
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
87+
}
88+
89+
impl<T, S> FromRequestParts<S, private::Stateless> for T
90+
where
91+
T: FromStatelessRequestParts,
92+
{
93+
type Rejection = T::Rejection;
94+
95+
fn from_request_parts(
96+
parts: &mut Parts,
97+
_state: &S,
98+
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
99+
T::from_request_parts(parts)
100+
}
101+
}
102+
68103
/// Types that can be created from requests.
69104
///
70105
/// Extractors that implement `FromRequest` can consume the request body and can thus only be run
@@ -97,7 +132,7 @@ pub trait FromRequest<S, M = private::ViaRequest>: Sized {
97132
impl<S, T> FromRequest<S, private::ViaParts> for T
98133
where
99134
S: Send + Sync,
100-
T: FromRequestParts<S>,
135+
T: FromRequestParts<S, private::WithState>,
101136
{
102137
type Rejection = <Self as FromRequestParts<S>>::Rejection;
103138

@@ -110,6 +145,23 @@ where
110145
}
111146
}
112147

148+
impl<S, T> FromRequest<S, private::ViaStatelessParts> for T
149+
where
150+
S: Send + Sync,
151+
T: FromRequestParts<S, private::Stateless>,
152+
{
153+
type Rejection = <Self as FromRequestParts<S, private::Stateless>>::Rejection;
154+
155+
fn from_request(
156+
req: Request,
157+
state: &S,
158+
) -> impl Future<Output = Result<Self, Self::Rejection>> {
159+
let (mut parts, _) = req.into_parts();
160+
async move { Self::from_request_parts(&mut parts, state).await }
161+
}
162+
}
163+
164+
113165
impl<S, T> FromRequestParts<S> for Result<T, T::Rejection>
114166
where
115167
T: FromRequestParts<S>,

axum-extra/src/handler/or.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ where
5252
}
5353
}
5454

55-
impl<S, L, R, Lt, Rt, M> Handler<(M, Lt, Rt), S> for Or<L, R, Lt, Rt, S>
55+
impl<S, L, R, Lt, Rt, M1, M2> Handler<((M1, M2), Lt, Rt), S> for Or<L, R, Lt, Rt, S>
5656
where
5757
L: HandlerCallWithExtractors<Lt, S> + Clone + Send + Sync + 'static,
5858
R: HandlerCallWithExtractors<Rt, S> + Clone + Send + Sync + 'static,
59-
Lt: FromRequestParts<S> + Send + 'static,
60-
Rt: FromRequest<S, M> + Send + 'static,
59+
Lt: FromRequestParts<S, M1> + Send + 'static,
60+
Rt: FromRequest<S, M2> + Send + 'static,
6161
Lt::Rejection: Send,
6262
Rt::Rejection: Send,
6363
S: Send + Sync + 'static,

axum/src/extract/query.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use super::{rejection::*, FromRequestParts};
1+
use super::rejection::*;
2+
use axum_core::extract::FromStatelessRequestParts;
23
use http::{request::Parts, Uri};
34
use serde::de::DeserializeOwned;
45

@@ -50,14 +51,13 @@ use serde::de::DeserializeOwned;
5051
#[derive(Debug, Clone, Copy, Default)]
5152
pub struct Query<T>(pub T);
5253

53-
impl<T, S> FromRequestParts<S> for Query<T>
54+
impl<T> FromStatelessRequestParts for Query<T>
5455
where
5556
T: DeserializeOwned,
56-
S: Send + Sync,
5757
{
5858
type Rejection = QueryRejection;
5959

60-
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
60+
async fn from_request_parts(parts: &mut Parts) -> Result<Self, Self::Rejection> {
6161
Self::try_from_uri(&parts.uri)
6262
}
6363
}

axum/src/handler/mod.rs

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ use crate::{
5151
response::{IntoResponse, Response},
5252
routing::IntoMakeService,
5353
};
54+
use axum_core::extract::FromStatelessRequestParts;
5455
use std::{convert::Infallible, fmt, future::Future, marker::PhantomData, pin::Pin};
5556
use tower::ServiceExt;
5657
use tower_layer::Layer;
@@ -203,6 +204,113 @@ where
203204
}
204205
}
205206

207+
struct AutoDerefSpecialization<T>(PhantomData<fn() -> *const T>);
208+
209+
impl<T> AutoDerefSpecialization<T> {
210+
fn new() -> Self {
211+
Self(PhantomData)
212+
}
213+
}
214+
215+
trait ViaRequestParts<S> {
216+
type SelfT;
217+
type Rejection;
218+
219+
async fn extract(
220+
self,
221+
req: &mut http::request::Parts,
222+
state: &S,
223+
) -> Result<Self::SelfT, Self::Rejection>;
224+
}
225+
226+
trait ViaStatelessRequestParts<S> {
227+
type SelfT;
228+
type Rejection;
229+
230+
async fn extract(
231+
self,
232+
req: &mut http::request::Parts,
233+
state: &S,
234+
) -> Result<Self::SelfT, Self::Rejection>;
235+
}
236+
237+
trait ViaPanic<S> {
238+
type SelfT;
239+
type Rejection;
240+
241+
async fn extract(
242+
self,
243+
req: &mut http::request::Parts,
244+
state: &S,
245+
) -> Result<Self::SelfT, Self::Rejection>;
246+
}
247+
248+
impl<T, S> ViaRequestParts<S> for &&AutoDerefSpecialization<T>
249+
where
250+
T: FromRequestParts<S>,
251+
{
252+
type SelfT = T;
253+
type Rejection = T::Rejection;
254+
255+
async fn extract(
256+
self,
257+
req: &mut http::request::Parts,
258+
state: &S,
259+
) -> Result<Self::SelfT, Self::Rejection> {
260+
T::from_request_parts(req, state).await
261+
}
262+
}
263+
264+
impl<T, S> ViaStatelessRequestParts<S> for &&&AutoDerefSpecialization<T>
265+
where
266+
T: FromStatelessRequestParts,
267+
{
268+
type SelfT = T;
269+
type Rejection = T::Rejection;
270+
271+
async fn extract(
272+
self,
273+
req: &mut http::request::Parts,
274+
_state: &S,
275+
) -> Result<Self::SelfT, Self::Rejection> {
276+
T::from_request_parts(req).await
277+
}
278+
}
279+
280+
impl<T, S> ViaPanic<S> for &AutoDerefSpecialization<T> {
281+
type SelfT = T;
282+
type Rejection = ();
283+
284+
async fn extract(
285+
self,
286+
_req: &mut http::request::Parts,
287+
_state: &S,
288+
) -> Result<Self::SelfT, Self::Rejection> {
289+
panic!();
290+
}
291+
}
292+
293+
struct Foo;
294+
295+
impl FromStatelessRequestParts for Foo {
296+
type Rejection = ();
297+
298+
fn from_request_parts(
299+
_parts: &mut http::request::Parts,
300+
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
301+
std::future::ready(Ok(Foo))
302+
}
303+
}
304+
305+
#[test]
306+
fn foo() {
307+
let req = todo!();
308+
(&&AutoDerefSpecialization::<Foo>::new()).extract(req, &());
309+
}
310+
311+
trait Something<S, V> {}
312+
impl<T, S, V> Something<S, V> for T where T: FromRequestParts<S, V> {}
313+
206314
macro_rules! impl_handler {
207315
(
208316
[$($ty:ident),*], $last:ident
@@ -214,7 +322,7 @@ macro_rules! impl_handler {
214322
Fut: Future<Output = Res> + Send,
215323
S: Send + Sync + 'static,
216324
Res: IntoResponse,
217-
$( $ty: FromRequestParts<S> + Send, )*
325+
$( $ty: Send, )*
218326
$last: FromRequest<S, M> + Send,
219327
{
220328
type Future = Pin<Box<dyn Future<Output = Response> + Send>>;
@@ -223,7 +331,7 @@ macro_rules! impl_handler {
223331
let (mut parts, body) = req.into_parts();
224332
Box::pin(async move {
225333
$(
226-
let $ty = match $ty::from_request_parts(&mut parts, &state).await {
334+
let $ty = match (&&&AutoDerefSpecialization::<$ty>::new()).extract(&mut parts, &state).await {
227335
Ok(value) => value,
228336
Err(rejection) => return rejection.into_response(),
229337
};

examples/todos/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ async fn main() {
6868
)
6969
.with_state(db);
7070

71-
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
71+
let listener = tokio::net::TcpListener::bind("127.0.0.1:2000")
7272
.await
7373
.unwrap();
7474
tracing::debug!("listening on {}", listener.local_addr().unwrap());

0 commit comments

Comments
 (0)