-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Provide way to handle a request with a MethodRouter without requiring an exclusive reference #3004
Comments
I'm trying to understand your needs. Could you maybe provide some code? There's an example in the tests how to route a request using a axum/axum/src/routing/method_routing.rs Lines 1609 to 1631 in 280d16a
I guess this does not help? |
This has to do with writing a Rough pseudocode of a
In our particular case, we have multiple The line If there was a way to do this, we could store an The core issue seems to be that tower's One alternative solution, short of exposing a way to route via a The tl;dr is that exposing ways for routing facilities to be invoked via a shared reference will make deep clones of routing structures per request unnecessary. |
I've tried to reproduce your example, but I'm missing some types, and guessing has not helped for now. Maybe you could provide a complete example that compiles. use axum::extract::Request;
use axum::routing::future::InfallibleRouteFuture;
use axum::routing::MethodRouter;
use tower::Service;
pub struct MyService {
method_router: MethodRouter,
}
impl MyService {
fn call(&mut self, req: Request) -> InfallibleRouteFuture {
let mut method_router = self.method_router.clone();
async move {
let _ = something_async().await;
method_router.call(req).await
}
.boxed()
}
}
async fn something_async() {
// do something async
} |
Here's a minimal example. The details of how a key in the map is selected isn't relevant so I've hidden that behind a trait. use std::{
collections::HashMap,
convert::Infallible,
marker::PhantomData,
task::{Context, Poll},
};
use axum::{
body::Body,
extract::Request,
http::StatusCode,
response::{IntoResponse, Response},
routing::MethodRouter,
};
use futures::{future::BoxFuture, FutureExt};
use tower::Service;
trait CommandFromBody {
fn command_from_body(body: &[u8]) -> Option<&str>;
}
struct ExampleService<C> {
routes: HashMap<String, MethodRouter>,
_phantom_c: PhantomData<fn() -> C>,
}
impl<C> Service<Request> for ExampleService<C>
where
C: CommandFromBody,
{
type Error = Infallible;
type Response = Response;
type Future = BoxFuture<'static, Result<Response, Infallible>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request) -> Self::Future {
let mut cmds = self.routes.clone();
async move {
let (parts, body) = req.into_parts();
let Ok(bytes) = axum::body::to_bytes(body, usize::MAX).await else {
return Ok(StatusCode::INTERNAL_SERVER_ERROR.into_response());
};
match C::command_from_body(&bytes).and_then(|cmd| cmds.get_mut(cmd)) {
Some(router) => {
let req = Request::from_parts(parts, Body::from(bytes));
router.call(req).await
}
None => Ok(StatusCode::NOT_FOUND.into_response()),
}
}
.boxed()
}
} The line |
thanks a lot. But this still cannot work. It's not possible to say that the reference is living long enough for the future. Personally, I'd go with the |
Right, the idea is to wrap the |
You could then have But I generally agree that we can expose a method for calling the router without requiring exclusive ownership. I'm just not sure if we want to expose |
Yes, you are right. Not sure how I missed that. |
Feature Request
Motivation
When writing a
Service
implementation that wraps aMethodRouter
, and when the wrapping service'scall
method needs to do some async work before forwarding the request toMethodRouter
, there is no way to avoid cloning the entireMethodRouter
(or putting it behind a mutex, which is almost certainly worse). This is becauseMethodRouter
does not expose any way to handle a request without a&mut MethodRouter
, and futures returned byService::call
(understandably) cannot capture&mut self
.Proposal
Adding a method to
MethodRouter
that routes a request with a&MethodRouter
would allow the wrapping service to hold anArc<MethodRouter>
. ChangingMethodRouter::call_with_state
frompub(crate)
topub
would resolve this, though whether this is the correct way to resolve this request is likely a question better answered by the axum maintainers.Alternatives
Any other approach that allows me to route a request using a
&MethodRouter
would be totally acceptable.The text was updated successfully, but these errors were encountered: