-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
feat(body): add Body::wrap_body
#2913
Conversation
0414a3d
to
3b95a6e
Compare
3b95a6e
to
96eb6c6
Compare
Wouldn't |
No, I wish 😞 I want to support I'll try an elaborate. This is a simplified version of what we have in axum today (everything is generic over the request body): #![allow(warnings)]
use http::{Request, Response};
use http_body::combinators::UnsyncBoxBody;
use hyper::{body::Bytes, Body};
use std::{
collections::HashMap,
convert::Infallible,
future::Future,
pin::Pin,
task::{Context, Poll},
};
use tower::{util::BoxCloneService, Layer, Service, ServiceExt};
type BoxBody = UnsyncBoxBody<Bytes, hyper::Error>;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
type BoxError = Box<dyn std::error::Error + Send + Sync>;
struct Route<B> {
inner: BoxCloneService<Request<B>, Response<BoxBody>, Infallible>,
}
impl<B> Clone for Route<B> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<B> Service<Request<B>> for Route<B> {
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
todo!()
}
fn call(&mut self, req: Request<B>) -> Self::Future {
todo!()
}
}
struct Router<B> {
routes: HashMap<String, Route<B>>,
}
impl<B> Router<B> {
fn new() -> Self {
Router {
routes: HashMap::new(),
}
}
pub fn layer<L, NewReqBody, NewResBody>(self, _layer: L) -> Router<NewReqBody>
where
L: Layer<Route<B>>,
L::Service:
Service<Request<NewReqBody>, Response = Response<NewResBody>> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewResBody: http_body::Body<Data = Bytes> + Send + 'static,
NewResBody::Error: Into<BoxError>,
{
todo!()
}
}
impl<B> Service<Request<B>> for Router<B> {
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<B>) -> Self::Future {
todo!()
}
}
#[test]
fn a_test() {
use http_body::Limited;
use tower_http::limit::RequestBodyLimitLayer;
let router: Router<Limited<Body>> = Router::new().layer(RequestBodyLimitLayer::new(1024));
} We want struct Route<B> {
inner: BoxCloneService<Request<B>, Response<BoxBody>, Infallible>,
} If we pick pub fn layer<L, NewResBody>(self, _layer: L) -> Router
where
L: Layer<Route>,
// this says that the service the layer produces must accept `Request<Body>`
L::Service:
Service<Request<Body>, Response = Response<NewResBody>> + Clone + Send + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
NewResBody: http_body::Body<Data = Bytes> + Send + 'static,
NewResBody::Error: Into<BoxError>,
{
...
} and we are forced to implement impl Service<Request<Body>> for Route { ... }
impl Service<Request<Body>> for Router { ... } However those two ( let router = Router::new().layer(RequestBodyLimitLayer::new(1024)); Then If we try to make the impl<B> Service<Request<B>> for Route {
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<B>) -> Self::Future {
// but this doesn't work either because `Route` contains a
// `BoxCloneService<Request<Body>, Response<BoxBody>, Infallible>`
// which we must call with a `Request<Body>` but we have no way to convert
// `B` into a `Body`
todo!()
}
} So that is why You're right that I could "just" use I hope you're able to see some magic trick that makes this all work without |
96eb6c6
to
355b6fc
Compare
355b6fc
to
5be673f
Compare
axum is moving to its own body type (tokio-rs/axum#1584) so we don't actually need this anymore. Things have also changed for hyper 1.0 so I'll close this for now. |
This adds
Body::wrap_body
which creates aBody
from anyimpl http_body::Body
. This is required in axum for tokio-rs/axum#1154.Note that I made the PR against
0.14.x
because theBody
struct is undergoing some big changes onmaster
. If you want me to make this PR againstmaster
instead and backport this to0.14.x
then just let me know 😊