Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppState::from_ref(...) inside a impl<S> FromRequest<S> breaks trait bounds axum::serve #2721

Closed
1 task done
samuela opened this issue Apr 24, 2024 · 2 comments
Closed
1 task done

Comments

@samuela
Copy link

samuela commented Apr 24, 2024

  • 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?

@samuela
Copy link
Author

samuela commented Apr 24, 2024

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.

@samuela
Copy link
Author

samuela commented Apr 24, 2024

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();
}

@tokio-rs tokio-rs locked and limited conversation to collaborators Apr 24, 2024
@jplatte jplatte converted this issue into discussion #2722 Apr 24, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant