`AppState::from_ref(...)` inside a `impl<S> FromRequest<S>` breaks trait bounds `axum::serve`
samuela opened this issue · comments
Samuel Ainsworth commented
- I have looked for existing issues (including closed) about this
Bug Report
Version
root@b62e2017d34d:/workspaces/bitbop/cherami# cargo tree | grep axum
├── axum v0.7.5
│ ├── axum-core v0.4.3
Platform
root@b62e2017d34d:/workspaces/bitbop/cherami# uname -a
Linux b62e2017d34d 6.6.22-linuxkit #1 SMP Fri Mar 29 12:21:27 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux
Description
Following this example code and these docs, I have written the following:
use axum::async_trait;
use axum::body::Body;
use axum::extract::FromRef;
use axum::extract::FromRequest;
use axum::extract::Request;
use axum::response::Response;
use axum::routing::post;
use axum::Extension;
use axum::Router;
use std::sync::Arc;
// Clone results in an auto-impl FromRef<AppState> due to https://docs.rs/axum/latest/axum/extract/trait.FromRef.html#impl-FromRef%3CT%3E-for-T.
#[derive(Clone)]
struct AppState {
stripe_webhook_secret: String,
}
// See https://github.com/arlyon/async-stripe/blob/master/examples/webhook-axum.rs
struct StripeEvent(stripe::Event);
#[async_trait]
impl<S> FromRequest<S> for StripeEvent
where
String: FromRequest<S>,
AppState: FromRef<S>,
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let whsec = AppState::from_ref(state).stripe_webhook_secret;
todo!()
}
}
async fn stripe_webhook(Extension(_): Extension<Arc<AppState>>, StripeEvent(_): StripeEvent) -> &'static str {
todo!()
}
#[tokio::main]
async fn main() {
let state = {
let stripe_webhook_secret = std::env::var("STRIPE_WEBHOOK_SECRET").expect("expected STRIPE_WEBHOOK_SECRET");
Arc::new(AppState { stripe_webhook_secret })
};
let app = Router::new()
.route("/stripe_webhook", post(stripe_webhook))
.layer(Extension(state));
let listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
However adding where AppState: FromRef<S>
seems to break trait bounds necessary for axum::serve
:
root@b62e2017d34d:/workspaces/bitbop/cherami# cargo check
Checking cherami v0.0.0 (/workspaces/bitbop/cherami)
error[E0277]: the trait bound `for<'a> Router<AppState>: tower_service::Service<IncomingStream<'a>>` is not satisfied
--> src/bin/api.rs:50:25
|
50 | axum::serve(listener, app).await.unwrap();
| ----------- ^^^ the trait `for<'a> tower_service::Service<IncomingStream<'a>>` is not implemented for `Router<AppState>`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `tower_service::Service<Request>`:
<Router as tower_service::Service<axum::http::Request<B>>>
<Router as tower_service::Service<IncomingStream<'_>>>
note: required by a bound in `serve`
--> /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/serve.rs:97:8
|
95 | pub fn serve<M, S>(tcp_listener: TcpListener, make_service: M) -> Serve<M, S>
| ----- required by a bound in this function
96 | where
97 | M: for<'a> Service<IncomingStream<'a>, Error = Infallible, Response = S>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `serve`
error[E0277]: the trait bound `Serve<Router<AppState>, _>: IntoFuture` is not satisfied
--> src/bin/api.rs:50:30
|
50 | axum::serve(listener, app).await.unwrap();
| ---------------------------^^^^^
| | ||
| | |the trait `IntoFuture` is not implemented for `Serve<Router<AppState>, _>`
| | help: remove the `.await`
| this call returns `Serve<Router<AppState>, _>`
|
= help: the trait `IntoFuture` is implemented for `Serve<M, S>`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `cherami` (bin "api") due to 2 previous errors
It is not at all clear how this error message relates to the actual code written. Am I doing something wrong here?
Samuel Ainsworth commented
Replacing the impl
with
#[async_trait]
impl FromRequest<AppState> for StripeEvent
where
String: FromRequest<AppState>,
{
type Rejection = Response;
async fn from_request(req: Request<Body>, state: &AppState) -> Result<Self, Self::Rejection> {
let whsec = state.stripe_webhook_secret;
todo!()
}
}
still results in the same compile error.
Samuel Ainsworth commented
Ok I think this is the smallest repro i can cook up:
use axum::async_trait;
use axum::body::Body;
use axum::extract::FromRequest;
use axum::extract::Request;
use axum::response::Response;
use axum::routing::post;
use axum::Extension;
use axum::Router;
use std::sync::Arc;
// Clone results in an auto-impl FromRef<AppState> due to https://docs.rs/axum/latest/axum/extract/trait.FromRef.html#impl-FromRef%3CT%3E-for-T.
#[derive(Clone)]
struct AppState {}
// See https://github.com/arlyon/async-stripe/blob/master/examples/webhook-axum.rs
struct StripeEvent(());
#[async_trait]
impl FromRequest<AppState> for StripeEvent {
type Rejection = Response;
async fn from_request(req: Request<Body>, state: &AppState) -> Result<Self, Self::Rejection> {
Ok(StripeEvent(()))
}
}
async fn stripe_webhook(StripeEvent(_): StripeEvent) -> &'static str {
"ok"
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/stripe_webhook", post(stripe_webhook))
.layer(Extension(Arc::new(AppState {})));
let listener = tokio::net::TcpListener::bind("127.0.0.1:8000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}