diff --git a/tonic-macros/src/lib.rs b/tonic-macros/src/lib.rs index 79e1610..5dc375d 100644 --- a/tonic-macros/src/lib.rs +++ b/tonic-macros/src/lib.rs @@ -25,7 +25,7 @@ pub fn client(attr: TokenStream) -> TokenStream { } impl #service_ident - where T: tonic::GrpcService, + where T: tonic::GrpcService, T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static, ::Error: Into + Send, ::Data: Send, { @@ -36,6 +36,14 @@ pub fn client(attr: TokenStream) -> TokenStream { #methods } + + impl Clone for #service_ident { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } + } }; TokenStream::from(output) diff --git a/tonic-macros/src/service.rs b/tonic-macros/src/service.rs index 32d1477..563062e 100644 --- a/tonic-macros/src/service.rs +++ b/tonic-macros/src/service.rs @@ -95,7 +95,7 @@ pub(crate) fn generate(service: ServiceDef) -> TokenStream { } impl Service> for #service_server { - type Response = http::Response; + type Response = http::Response; type Error = tonic::error::Never; type Future = BoxFuture; diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index 63ee16f..d93cb7e 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -19,7 +19,8 @@ percent-encoding = "1.0.1" tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-future" } tokio-codec = "=0.2.0-alpha.1" async-stream = "0.1.0" -http-body = { git = "https://github.com/hyperium/http-body", branch = "std-future" } +http-body = { git = "https://github.com/hyperium/http-body", branch = "lucio/pin" } +pin-project = "0.4.0-alpha.2" [dev-dependencies] tokio = "=0.2.0-alpha.1" diff --git a/tonic/src/body.rs b/tonic/src/body.rs index 53d824e..5b56d7b 100644 --- a/tonic/src/body.rs +++ b/tonic/src/body.rs @@ -1,9 +1,10 @@ use crate::{Code, Error, Status}; use bytes::{Buf, Bytes, IntoBuf}; -use futures_core::{Stream, TryStream}; +use futures_core::Stream; use futures_util::{ready, TryStreamExt}; use http::HeaderMap; use http_body::Body as HttpBody; +use pin_project::pin_project; use std::pin::Pin; use std::task::{Context, Poll}; @@ -13,12 +14,15 @@ pub trait Body: sealed::Sealed { type Data: Buf; type Error: Into; - fn is_end_stream(&self) -> bool; + fn is_end_stream(self: Pin<&mut Self>) -> bool; - fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>>; + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>>; fn poll_trailers( - &mut self, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>>; } @@ -31,16 +35,19 @@ where type Data = T::Data; type Error = T::Error; - fn is_end_stream(&self) -> bool { + fn is_end_stream(self: Pin<&mut Self>) -> bool { HttpBody::is_end_stream(self) } - fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>> { + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { HttpBody::poll_data(self, cx) } fn poll_trailers( - &mut self, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>> { HttpBody::poll_trailers(self, cx) @@ -59,17 +66,25 @@ mod sealed { } pub struct BoxBody { - inner: Box + Send>, + inner: Pin + Send + 'static>>, } impl BoxBody { + pub fn from_stream(s: S) -> Self + where + S: Stream> + Send + 'static, + { + let body = AsyncBody::new(s); + Self::map_from(body) + } + /// Create a new `BoxBody` mapping item and error to the default types. pub fn map_from(inner: B) -> Self where - B: Body + Send + 'static, + B: HttpBody + Send + 'static, { BoxBody { - inner: Box::new(inner), + inner: Box::pin(inner), } } } @@ -78,85 +93,36 @@ impl HttpBody for BoxBody { type Data = BytesBuf; type Error = Status; - fn is_end_stream(&self) -> bool { - self.inner.is_end_stream() + fn is_end_stream(mut self: Pin<&mut Self>) -> bool { + HttpBody::is_end_stream(self.inner.as_mut()) } - fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>> { - self.inner.poll_data(cx) + fn poll_data( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + HttpBody::poll_data(self.inner.as_mut(), cx) } fn poll_trailers( - &mut self, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, Self::Error>> { - self.inner.poll_trailers(cx) + HttpBody::poll_trailers(self.inner.as_mut(), cx) } } -pub struct BoxAsyncBody { - inner: Pin> + Send>>, - error: Option, -} - -impl BoxAsyncBody { - // pub fn new(inner: S) -> Self - // where - // S: Stream> + Send + 'static, - // { - // Self { - // inner: Box::pin(inner), - // error: None, - // } - // } - - pub fn new_try(inner: S) -> Self - where - S: TryStream + Send + 'static, - { - Self { - inner: Box::pin(inner.into_stream()), - error: None, - } - } -} - -impl HttpBody for BoxAsyncBody { - type Data = BytesBuf; - type Error = Status; - - fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>> { - match ready!(self.inner.try_poll_next_unpin(cx)) { - Some(Ok(d)) => Some(Ok(d)).into(), - Some(Err(status)) => { - self.error = Some(status); - None.into() - } - None => None.into(), - } - } - - fn poll_trailers(&mut self, _cx: &mut Context<'_>) -> Poll, Status>> { - let status = if let Some(status) = self.error.take() { - status - } else { - Status::new(Code::Ok, "") - }; - - Poll::Ready(Ok(Some(status.to_header_map()?))) - } -} - -// TODO: refactor this to accept an !Unpin stream +#[pin_project] #[derive(Debug)] pub struct AsyncBody { + #[pin] inner: S, error: Option, } impl AsyncBody where - S: Stream> + Unpin, + S: Stream>, { pub fn new(inner: S) -> Self { Self { inner, error: None } @@ -165,24 +131,32 @@ where impl HttpBody for AsyncBody where - S: Stream> + Unpin, + S: Stream>, { type Data = BytesBuf; type Error = Status; - fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>> { - match ready!(self.inner.try_poll_next_unpin(cx)) { + fn poll_data( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + let mut self_proj = self.project(); + match ready!(self_proj.inner.try_poll_next_unpin(cx)) { Some(Ok(d)) => Some(Ok(d)).into(), Some(Err(status)) => { - self.error = Some(status); + *self_proj.error = Some(status); None.into() } None => None.into(), } } - fn poll_trailers(&mut self, _cx: &mut Context<'_>) -> Poll, Status>> { - let status = if let Some(status) = self.error.take() { + fn poll_trailers( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Status>> { + let self_proj = self.project(); + let status = if let Some(status) = self_proj.error.take() { status } else { Status::new(Code::Ok, "") diff --git a/tonic/src/client/grpc.rs b/tonic/src/client/grpc.rs index 98d9152..f5aa01b 100644 --- a/tonic/src/client/grpc.rs +++ b/tonic/src/client/grpc.rs @@ -1,5 +1,5 @@ use crate::{ - body::{Body, BoxAsyncBody}, + body::{Body, BoxBody}, codec::{decode, encode, Codec, Streaming}, Code, GrpcService, Request, Response, Status, }; @@ -27,7 +27,7 @@ impl Grpc { codec: C, ) -> Result, Status> where - T: GrpcService, + T: GrpcService, T::ResponseBody: Body + HttpBody + Send + 'static, ::Error: Into + Send, ::Data: Send, @@ -48,7 +48,7 @@ impl Grpc { codec: C, ) -> Result, Status> where - T: GrpcService, + T: GrpcService, T::ResponseBody: Body + HttpBody + Send + 'static, ::Error: Into + Send, ::Data: Send, @@ -78,7 +78,7 @@ impl Grpc { codec: C, ) -> Result>, Status> where - T: GrpcService, + T: GrpcService, T::ResponseBody: Body + HttpBody + Send + 'static, ::Error: Into + Send, ::Data: Send, @@ -99,7 +99,7 @@ impl Grpc { mut codec: C, ) -> Result>, Status> where - T: GrpcService, + T: GrpcService, T::ResponseBody: Body + HttpBody + Send + 'static, ::Error: Into + Send, ::Data: Send, @@ -116,8 +116,8 @@ impl Grpc { let uri = Uri::from_parts(parts).expect("path_and_query only is valid Uri"); let request = request - .map(|s| encode(codec.encoder(), Box::pin(s))) - .map(BoxAsyncBody::new_try); + .map(|s| encode(codec.encoder(), Box::pin(s)).into_stream()) + .map(BoxBody::from_stream); let mut request = request.into_http(uri); @@ -155,3 +155,11 @@ impl Grpc { Ok(Response::from_http(response)) } } + +impl Clone for Grpc { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} diff --git a/tonic/src/codec/decode.rs b/tonic/src/codec/decode.rs index d20db81..f4e8420 100644 --- a/tonic/src/codec/decode.rs +++ b/tonic/src/codec/decode.rs @@ -60,7 +60,8 @@ where yield Ok(item); } - let chunk = match future::poll_fn(|cx| source.poll_data(cx)).await { + // FIXME: Figure out how to verify that this is safe + let chunk = match future::poll_fn(|cx| unsafe { std::pin::Pin::new_unchecked(&mut source) }.poll_data(cx)).await { Some(Ok(d)) => Some(d), Some(Err(e)) => { let err = e.into(); diff --git a/tonic/src/lib.rs b/tonic/src/lib.rs index 12c2908..ab50e25 100644 --- a/tonic/src/lib.rs +++ b/tonic/src/lib.rs @@ -16,7 +16,7 @@ mod response; mod service; mod status; -pub use body::{BoxAsyncBody, BoxBody}; +pub use body::BoxBody; pub use request::Request; pub use response::Response; pub use service::GrpcService; diff --git a/tonic/src/server/grpc.rs b/tonic/src/server/grpc.rs index 6478cfa..9f4e448 100644 --- a/tonic/src/server/grpc.rs +++ b/tonic/src/server/grpc.rs @@ -1,5 +1,5 @@ use crate::{ - body::{BoxAsyncBody, BytesBuf}, + body::{BytesBuf, BoxBody}, codec::{decode, encode, Codec, Streaming}, server::{ClientStreamingService, ServerStreamingService, StreamingService, UnaryService}, Code, Request, Response, Status, @@ -31,7 +31,7 @@ where &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: UnaryService, B: Body + Send + 'static, @@ -45,7 +45,7 @@ where .map_response::>>>(Err( status, )) - .map(BoxAsyncBody::new_try); + .map(BoxBody::from_stream); } }; @@ -54,14 +54,14 @@ where .await .map(|r| r.map(|m| stream::once(future::ok(m)))); - self.map_response(response).map(BoxAsyncBody::new_try) + self.map_response(response).map(BoxBody::from_stream) } pub async fn server_streaming( &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: ServerStreamingService, S::ResponseStream: Send + 'static, @@ -74,13 +74,13 @@ where Err(status) => { return self .map_response::(Err(status)) - .map(BoxAsyncBody::new_try); + .map(BoxBody::from_stream); } }; let response = service.call(request).await; - self.map_response(response).map(BoxAsyncBody::new_try) + self.map_response(response).map(BoxBody::from_stream) } //BoxStream, @@ -88,7 +88,7 @@ where &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: ClientStreamingService, Response = T::Encode>, T::Decode: Send + 'static, @@ -102,14 +102,14 @@ where .call(request) .await .map(|r| r.map(|m| stream::once(future::ok(m)))); - self.map_response(response).map(BoxAsyncBody::new_try) + self.map_response(response).map(BoxBody::from_stream) } pub async fn streaming( &mut self, mut service: S, req: http::Request, - ) -> http::Response + ) -> http::Response where S: StreamingService, Response = T::Encode> + Send, S::ResponseStream: Send + 'static, @@ -119,7 +119,7 @@ where { let request = self.map_request_streaming(req); let response = service.call(request).await; - self.map_response(response).map(BoxAsyncBody::new_try) + self.map_response(response).map(BoxBody::from_stream) } async fn map_request_unary( diff --git a/tonic/tests/h2.rs b/tonic/tests/h2.rs deleted file mode 100644 index d118be4..0000000 --- a/tonic/tests/h2.rs +++ /dev/null @@ -1,143 +0,0 @@ -#![feature(async_await, type_alias_impl_trait)] - -use futures_core::Stream; -use futures_util::future; -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tokio::net::TcpListener; -use tonic::{ - body, - server::{ClientStreamingService, Grpc, UnaryService}, - Request, Response, Status, -}; -use tower_h2::{RecvBody, Server}; -use tower_service::Service; - -#[derive(Clone, PartialEq, prost::Message)] -pub struct HelloRequest { - #[prost(string, tag = "1")] - pub name: std::string::String, -} -/// The response message containing the greetings -#[derive(Clone, PartialEq, prost::Message)] -pub struct HelloReply { - #[prost(string, tag = "1")] - pub message: std::string::String, -} - -struct SayHello; - -impl UnaryService for SayHello { - type Response = HelloReply; - type Future = impl Future, Status>>; - - fn call(&mut self, request: Request) -> Self::Future { - async move { - println!("REQUEST = {:?}", request); - - let reply = HelloReply { - message: "Zomg, it works!".to_string(), - }; - - Ok(Response::new(reply)) - } - } -} - -struct SayHelloStream; - -impl ClientStreamingService for SayHelloStream -where - S: Stream> + Unpin + Send + 'static, -{ - type Response = HelloReply; - // type Future = impl Future, Status>>; - type Future = - Pin, Status>> + Send + 'static>>; - - fn call(&mut self, _req: Request) -> Self::Future { - let fut = async move { - Ok(Response::new(HelloReply { - message: "hello".into(), - })) - }; - Box::pin(fut) - } -} - -#[tokio::test] -async fn main() { - let addr = "[::1]:50051".parse().unwrap(); - let mut bind = TcpListener::bind(&addr).unwrap(); - - let mut server = Server::new(MakeSvc, Default::default()); - - while let Ok((sock, _addr)) = bind.accept().await { - if let Err(e) = sock.set_nodelay(true) { - panic!("error: {}", e); - } - - if let Err(e) = server.serve(sock).await { - println!("H2 ERROR: {}", e); - } - } -} - -#[derive(Debug)] -pub struct Svc; - -impl Service> for Svc { - type Response = http::Response; - type Error = tonic::error::Never; - // type Future = impl Future>; - type Future = Pin>>>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Ok(()).into() - } - - fn call(&mut self, req: http::Request) -> Self::Future { - match req.uri().path() { - "/greeter.Helloworld/SayHello" => { - let fut = async move { - let codec = tonic::codec::ProstCodec::new(); - let mut grpc = Grpc::new(codec); - let response = grpc.unary(SayHello, req).await; - Ok(response) - }; - - Box::pin(fut) - } - - "/greeter.Helloworld/SayHelloStreaming" => { - let fut = async move { - let codec = tonic::codec::ProstCodec::new(); - let mut grpc = Grpc::new(codec); - let response = grpc.client_streaming(SayHelloStream, req).await; - Ok(response) - }; - - Box::pin(fut) - } - - _ => unimplemented!(), - } - } -} - -pub struct MakeSvc; - -impl Service<()> for MakeSvc { - type Response = Svc; - type Error = std::io::Error; - type Future = future::Ready>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Ok(()).into() - } - - fn call(&mut self, _: ()) -> Self::Future { - future::ok(Svc) - } -} diff --git a/tonic/tests/server.rs b/tonic/tests/server.rs deleted file mode 100644 index b22554d..0000000 --- a/tonic/tests/server.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![feature(async_await, type_alias_impl_trait)] - -use futures_core::Stream; -use std::future::Future; -use std::pin::Pin; -use std::task::{Context, Poll}; -use tokio_buf::BufStream; -use tonic::codec::ProstCodec; -use tonic::server::*; -use tonic::{Request, Response, Status}; - -#[derive(Clone, PartialEq, prost::Message)] -pub struct HelloRequest { - #[prost(string, tag = "1")] - pub name: std::string::String, -} -/// The response message containing the greetings -#[derive(Clone, PartialEq, prost::Message)] -pub struct HelloReply { - #[prost(string, tag = "1")] - pub message: std::string::String, -} - -struct SayHello; - -impl UnaryService for SayHello { - type Response = HelloReply; - type Future = impl Future, Status>>; - - fn call(&mut self, _request: Request) -> Self::Future { - async move { - Ok(Response::new(HelloReply { - message: "hello".into(), - })) - } - } -} - -struct SayHelloStream; - -impl ClientStreamingService for SayHelloStream -where - S: Stream> + Unpin + Send + 'static, -{ - type Response = HelloReply; - // type Future = impl Future, Status>>; - type Future = - Pin, Status>> + Send + 'static>>; - - fn call(&mut self, _: Request) -> Self::Future { - let fut = async move { - Ok(Response::new(HelloReply { - message: "hello".into(), - })) - }; - Box::pin(fut) - } -} - -#[tokio::test] -async fn say_hello() { - let codec = ProstCodec::new(); - let mut grpc = Grpc::new(codec); - - let request = http::Request::new(Body(Vec::new())); - grpc.unary(SayHello, request).await; - - let request = http::Request::new(Body(Vec::new())); - grpc.client_streaming(SayHelloStream, request).await; -} - -#[derive(Debug, Default, Clone)] -struct Body(Vec); - -impl From> for Body { - fn from(t: Vec) -> Self { - Body(t) - } -} - -impl BufStream for Body { - type Item = std::io::Cursor>; - type Error = std::io::Error; - - fn poll_buf(&mut self, _cx: &mut Context<'_>) -> Poll>> { - if self.0.is_empty() { - return None.into(); - } - - use std::{io, mem}; - - let bytes = mem::replace(&mut self.0, Default::default()); - let buf = io::Cursor::new(bytes); - - Some(Ok(buf)).into() - } -} diff --git a/tower-h2/Cargo.toml b/tower-h2/Cargo.toml index ee1f254..026cff9 100644 --- a/tower-h2/Cargo.toml +++ b/tower-h2/Cargo.toml @@ -14,7 +14,7 @@ tower-service = { git = "http://github.com/tower-rs/tower", branch = "std-future tower-util = { git = "http://github.com/tower-rs/tower", branch = "std-future" } h2 = { git = "https://github.com/LucioFranco/h2", branch = "lucio/tower-h2-hack" } http = "0.1" -http-body = { git = "https://github.com/hyperium/http-body", branch = "std-future" } +http-body = { git = "https://github.com/hyperium/http-body", branch = "lucio/pin" } log = "0.4" [dev-dependencies] diff --git a/tower-h2/examples/client.rs b/tower-h2/examples/client.rs index fbba759..529fa54 100644 --- a/tower-h2/examples/client.rs +++ b/tower-h2/examples/client.rs @@ -3,8 +3,8 @@ use http::Request; use std::task::{Context, Poll}; use tokio::net::TcpStream; -use tokio_buf::BufStream; use tower_h2::Connection; +use std::pin::Pin; #[tokio::main] async fn main() -> Result<(), Box> { @@ -30,11 +30,14 @@ impl From> for Body { } } -impl BufStream for Body { - type Item = std::io::Cursor>; +impl http_body::Body for Body { + type Data = std::io::Cursor>; type Error = std::io::Error; - fn poll_buf(&mut self, _cx: &mut Context<'_>) -> Poll>> { + fn poll_data( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll>> { if self.0.is_empty() { return None.into(); } @@ -46,4 +49,11 @@ impl BufStream for Body { Some(Ok(buf)).into() } + + fn poll_trailers( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Ok(None).into() + } } diff --git a/tower-h2/examples/server.rs b/tower-h2/examples/server.rs index 69ffd63..0d3acf1 100644 --- a/tower-h2/examples/server.rs +++ b/tower-h2/examples/server.rs @@ -4,7 +4,7 @@ use futures_util::future; use http::{Request, Response}; use std::task::{Context, Poll}; use tokio::net::TcpListener; -use tokio_buf::BufStream; +use std::pin::Pin; use tower_h2::{RecvBody, Server}; use tower_service::Service; @@ -84,11 +84,14 @@ impl From> for Body { } } -impl BufStream for Body { - type Item = std::io::Cursor>; +impl http_body::Body for Body { + type Data = std::io::Cursor>; type Error = std::io::Error; - fn poll_buf(&mut self, _cx: &mut Context<'_>) -> Poll>> { + fn poll_data( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll>> { if self.0.is_empty() { return None.into(); } @@ -100,4 +103,11 @@ impl BufStream for Body { Some(Ok(buf)).into() } + + fn poll_trailers( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll, Self::Error>> { + Ok(None).into() + } } diff --git a/tower-h2/src/client.rs b/tower-h2/src/client.rs index e0213be..ed517de 100644 --- a/tower-h2/src/client.rs +++ b/tower-h2/src/client.rs @@ -57,10 +57,10 @@ where } fn call(&mut self, request: Request) -> Self::Future { - let (parts, body) = request.into_parts(); + let (parts, mut body) = request.into_parts(); let request = Request::from_parts(parts, ()); - let eos = body.is_end_stream(); + let eos = Pin::new(&mut body).is_end_stream(); let res = self.client.send_request(request, eos); diff --git a/tower-h2/src/flush.rs b/tower-h2/src/flush.rs index 4759430..b22f06c 100644 --- a/tower-h2/src/flush.rs +++ b/tower-h2/src/flush.rs @@ -13,7 +13,7 @@ where S: Body, { h2: SendStream>, - body: S, + body: Pin + Send + 'static>>, state: FlushState, } @@ -32,13 +32,13 @@ enum DataOrTrailers { impl Flush where - S: Body, + S: Body + Send + 'static, S::Error: Into>, { pub fn new(src: S, dst: SendStream>) -> Self { Flush { h2: dst, - body: src, + body: Box::pin(src), state: FlushState::Data, } } @@ -50,7 +50,7 @@ where loop { match ready!(self.poll_body(cx)) { Some(Ok(Data(buf))) => { - let eos = self.body.is_end_stream(); + let eos = Pin::new(&mut self.body).is_end_stream(); self.h2.send_data(SendBuf::new(buf), eos)?; @@ -125,7 +125,7 @@ where } } - let item = match ready!(self.body.poll_data(cx)) { + let item = match ready!(Pin::new(&mut self.body).poll_data(cx)) { Some(Ok(d)) => Some(d), Some(Err(err)) => { let err = err.into(); @@ -162,13 +162,14 @@ where // before we get a RST_STREAM. } } - let trailers = ready!(self.body.poll_trailers(cx).map_err(|err| { - let err = err.into(); - debug!("user body error from poll_trailers: {}", err); - let reason = crate::error::reason_from_dyn_error(&*err); - self.h2.send_reset(reason); - reason - }))?; + let trailers = + ready!(Pin::new(&mut self.body).poll_trailers(cx).map_err(|err| { + let err = err.into(); + debug!("user body error from poll_trailers: {}", err); + let reason = crate::error::reason_from_dyn_error(&*err); + self.h2.send_reset(reason); + reason + }))?; self.state = FlushState::Done; if let Some(trailers) = trailers { return Some(Ok(DataOrTrailers::Trailers(trailers))).into(); @@ -182,7 +183,7 @@ where impl Future for Flush where - S: Body + Unpin, + S: Body + Send + 'static, S::Error: Into>, { type Output = Result<(), ()>; diff --git a/tower-h2/src/recv_body.rs b/tower-h2/src/recv_body.rs index 231fed4..0b7e6c5 100644 --- a/tower-h2/src/recv_body.rs +++ b/tower-h2/src/recv_body.rs @@ -1,6 +1,7 @@ use bytes::{Buf, Bytes, BytesMut}; use futures_util::TryStreamExt; use http_body::Body; +use std::pin::Pin; use std::task::{Context, Poll}; /// Allows a stream to be read from the remote. @@ -33,11 +34,14 @@ impl Body for RecvBody { type Data = Data; type Error = h2::Error; - fn is_end_stream(&self) -> bool { + fn is_end_stream(self: Pin<&mut Self>) -> bool { self.inner.is_end_stream() } - fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll>> { + fn poll_data( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { let data = match futures_util::ready!(self.inner.try_poll_next_unpin(cx)) { Some(Ok(bytes)) => { self.inner @@ -54,7 +58,7 @@ impl Body for RecvBody { } fn poll_trailers( - &mut self, + mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll, h2::Error>> { match futures_util::ready!(self.inner.poll_trailers(cx)) { diff --git a/tower-h2/src/server.rs b/tower-h2/src/server.rs index fed4437..d17a762 100644 --- a/tower-h2/src/server.rs +++ b/tower-h2/src/server.rs @@ -86,10 +86,10 @@ pub async fn handle_request( B::Data: Unpin, B::Error: Into>, { - let (parts, body) = response.into_parts(); + let (parts, mut body) = response.into_parts(); // Check if the response is imemdiately an end-of-stream. - let eos = body.is_end_stream(); + let eos = std::pin::Pin::new(&mut body).is_end_stream(); let response = Response::from_parts(parts, ());