Update http body

This commit is contained in:
Lucio Franco
2019-08-18 01:06:27 -04:00
parent 2ebad2d778
commit b2a9ab97d7
17 changed files with 146 additions and 369 deletions
+9 -1
View File
@@ -25,7 +25,7 @@ pub fn client(attr: TokenStream) -> TokenStream {
} }
impl<T> #service_ident <T> impl<T> #service_ident <T>
where T: tonic::GrpcService<tonic::body::BoxAsyncBody>, where T: tonic::GrpcService<tonic::body::BoxBody>,
T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static, T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static,
<T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send, <T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send,
<T::ResponseBody as tonic::_codegen::HttpBody>::Data: Send, { <T::ResponseBody as tonic::_codegen::HttpBody>::Data: Send, {
@@ -36,6 +36,14 @@ pub fn client(attr: TokenStream) -> TokenStream {
#methods #methods
} }
impl<T: Clone> Clone for #service_ident <T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
}; };
TokenStream::from(output) TokenStream::from(output)
+1 -1
View File
@@ -95,7 +95,7 @@ pub(crate) fn generate(service: ServiceDef) -> TokenStream {
} }
impl Service<http::Request<tower_h2::RecvBody>> for #service_server { impl Service<http::Request<tower_h2::RecvBody>> for #service_server {
type Response = http::Response<tonic::BoxAsyncBody>; type Response = http::Response<tonic::BoxBody>;
type Error = tonic::error::Never; type Error = tonic::error::Never;
type Future = BoxFuture<Self::Response, Self::Error>; type Future = BoxFuture<Self::Response, Self::Error>;
+2 -1
View File
@@ -19,7 +19,8 @@ percent-encoding = "1.0.1"
tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-future" } tower-service = { git = "https://github.com/tower-rs/tower", branch = "std-future" }
tokio-codec = "=0.2.0-alpha.1" tokio-codec = "=0.2.0-alpha.1"
async-stream = "0.1.0" 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] [dev-dependencies]
tokio = "=0.2.0-alpha.1" tokio = "=0.2.0-alpha.1"
+51 -77
View File
@@ -1,9 +1,10 @@
use crate::{Code, Error, Status}; use crate::{Code, Error, Status};
use bytes::{Buf, Bytes, IntoBuf}; use bytes::{Buf, Bytes, IntoBuf};
use futures_core::{Stream, TryStream}; use futures_core::Stream;
use futures_util::{ready, TryStreamExt}; use futures_util::{ready, TryStreamExt};
use http::HeaderMap; use http::HeaderMap;
use http_body::Body as HttpBody; use http_body::Body as HttpBody;
use pin_project::pin_project;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -13,12 +14,15 @@ pub trait Body: sealed::Sealed {
type Data: Buf; type Data: Buf;
type Error: Into<Error>; type Error: Into<Error>;
fn is_end_stream(&self) -> bool; fn is_end_stream(self: Pin<&mut Self>) -> bool;
fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>>; fn poll_data(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>>;
fn poll_trailers( fn poll_trailers(
&mut self, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>>; ) -> Poll<Result<Option<http::HeaderMap>, Self::Error>>;
} }
@@ -31,16 +35,19 @@ where
type Data = T::Data; type Data = T::Data;
type Error = T::Error; type Error = T::Error;
fn is_end_stream(&self) -> bool { fn is_end_stream(self: Pin<&mut Self>) -> bool {
HttpBody::is_end_stream(self) HttpBody::is_end_stream(self)
} }
fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>> { fn poll_data(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
HttpBody::poll_data(self, cx) HttpBody::poll_data(self, cx)
} }
fn poll_trailers( fn poll_trailers(
&mut self, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> { ) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
HttpBody::poll_trailers(self, cx) HttpBody::poll_trailers(self, cx)
@@ -59,17 +66,25 @@ mod sealed {
} }
pub struct BoxBody { pub struct BoxBody {
inner: Box<dyn Body<Data = BytesBuf, Error = Status> + Send>, inner: Pin<Box<dyn HttpBody<Data = BytesBuf, Error = Status> + Send + 'static>>,
} }
impl BoxBody { impl BoxBody {
pub fn from_stream<S>(s: S) -> Self
where
S: Stream<Item = Result<crate::body::BytesBuf, Status>> + Send + 'static,
{
let body = AsyncBody::new(s);
Self::map_from(body)
}
/// Create a new `BoxBody` mapping item and error to the default types. /// Create a new `BoxBody` mapping item and error to the default types.
pub fn map_from<B>(inner: B) -> Self pub fn map_from<B>(inner: B) -> Self
where where
B: Body<Data = BytesBuf, Error = Status> + Send + 'static, B: HttpBody<Data = BytesBuf, Error = Status> + Send + 'static,
{ {
BoxBody { BoxBody {
inner: Box::new(inner), inner: Box::pin(inner),
} }
} }
} }
@@ -78,85 +93,36 @@ impl HttpBody for BoxBody {
type Data = BytesBuf; type Data = BytesBuf;
type Error = Status; type Error = Status;
fn is_end_stream(&self) -> bool { fn is_end_stream(mut self: Pin<&mut Self>) -> bool {
self.inner.is_end_stream() HttpBody::is_end_stream(self.inner.as_mut())
} }
fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>> { fn poll_data(
self.inner.poll_data(cx) mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
HttpBody::poll_data(self.inner.as_mut(), cx)
} }
fn poll_trailers( fn poll_trailers(
&mut self, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> { ) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
self.inner.poll_trailers(cx) HttpBody::poll_trailers(self.inner.as_mut(), cx)
} }
} }
pub struct BoxAsyncBody { #[pin_project]
inner: Pin<Box<dyn Stream<Item = Result<BytesBuf, Status>> + Send>>,
error: Option<Status>,
}
impl BoxAsyncBody {
// pub fn new<S>(inner: S) -> Self
// where
// S: Stream<Item = Result<crate::body::BytesBuf, Status>> + Send + 'static,
// {
// Self {
// inner: Box::pin(inner),
// error: None,
// }
// }
pub fn new_try<S>(inner: S) -> Self
where
S: TryStream<Ok = BytesBuf, Error = Status> + 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<Option<Result<Self::Data, Self::Error>>> {
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<Result<Option<HeaderMap>, 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
#[derive(Debug)] #[derive(Debug)]
pub struct AsyncBody<S> { pub struct AsyncBody<S> {
#[pin]
inner: S, inner: S,
error: Option<Status>, error: Option<Status>,
} }
impl<S> AsyncBody<S> impl<S> AsyncBody<S>
where where
S: Stream<Item = Result<crate::body::BytesBuf, Status>> + Unpin, S: Stream<Item = Result<crate::body::BytesBuf, Status>>,
{ {
pub fn new(inner: S) -> Self { pub fn new(inner: S) -> Self {
Self { inner, error: None } Self { inner, error: None }
@@ -165,24 +131,32 @@ where
impl<S> HttpBody for AsyncBody<S> impl<S> HttpBody for AsyncBody<S>
where where
S: Stream<Item = Result<crate::body::BytesBuf, Status>> + Unpin, S: Stream<Item = Result<crate::body::BytesBuf, Status>>,
{ {
type Data = BytesBuf; type Data = BytesBuf;
type Error = Status; type Error = Status;
fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Self::Data, Self::Error>>> { fn poll_data(
match ready!(self.inner.try_poll_next_unpin(cx)) { self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
let mut self_proj = self.project();
match ready!(self_proj.inner.try_poll_next_unpin(cx)) {
Some(Ok(d)) => Some(Ok(d)).into(), Some(Ok(d)) => Some(Ok(d)).into(),
Some(Err(status)) => { Some(Err(status)) => {
self.error = Some(status); *self_proj.error = Some(status);
None.into() None.into()
} }
None => None.into(), None => None.into(),
} }
} }
fn poll_trailers(&mut self, _cx: &mut Context<'_>) -> Poll<Result<Option<HeaderMap>, Status>> { fn poll_trailers(
let status = if let Some(status) = self.error.take() { self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<Option<HeaderMap>, Status>> {
let self_proj = self.project();
let status = if let Some(status) = self_proj.error.take() {
status status
} else { } else {
Status::new(Code::Ok, "") Status::new(Code::Ok, "")
+15 -7
View File
@@ -1,5 +1,5 @@
use crate::{ use crate::{
body::{Body, BoxAsyncBody}, body::{Body, BoxBody},
codec::{decode, encode, Codec, Streaming}, codec::{decode, encode, Codec, Streaming},
Code, GrpcService, Request, Response, Status, Code, GrpcService, Request, Response, Status,
}; };
@@ -27,7 +27,7 @@ impl<T> Grpc<T> {
codec: C, codec: C,
) -> Result<Response<M2>, Status> ) -> Result<Response<M2>, Status>
where where
T: GrpcService<BoxAsyncBody>, T: GrpcService<BoxBody>,
T::ResponseBody: Body + HttpBody + Send + 'static, T::ResponseBody: Body + HttpBody + Send + 'static,
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send, <T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
<T::ResponseBody as HttpBody>::Data: Send, <T::ResponseBody as HttpBody>::Data: Send,
@@ -48,7 +48,7 @@ impl<T> Grpc<T> {
codec: C, codec: C,
) -> Result<Response<M2>, Status> ) -> Result<Response<M2>, Status>
where where
T: GrpcService<BoxAsyncBody>, T: GrpcService<BoxBody>,
T::ResponseBody: Body + HttpBody + Send + 'static, T::ResponseBody: Body + HttpBody + Send + 'static,
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send, <T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
<T::ResponseBody as HttpBody>::Data: Send, <T::ResponseBody as HttpBody>::Data: Send,
@@ -78,7 +78,7 @@ impl<T> Grpc<T> {
codec: C, codec: C,
) -> Result<Response<Streaming<M2>>, Status> ) -> Result<Response<Streaming<M2>>, Status>
where where
T: GrpcService<BoxAsyncBody>, T: GrpcService<BoxBody>,
T::ResponseBody: Body + HttpBody + Send + 'static, T::ResponseBody: Body + HttpBody + Send + 'static,
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send, <T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
<T::ResponseBody as HttpBody>::Data: Send, <T::ResponseBody as HttpBody>::Data: Send,
@@ -99,7 +99,7 @@ impl<T> Grpc<T> {
mut codec: C, mut codec: C,
) -> Result<Response<Streaming<M2>>, Status> ) -> Result<Response<Streaming<M2>>, Status>
where where
T: GrpcService<BoxAsyncBody>, T: GrpcService<BoxBody>,
T::ResponseBody: Body + HttpBody + Send + 'static, T::ResponseBody: Body + HttpBody + Send + 'static,
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send, <T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
<T::ResponseBody as HttpBody>::Data: Send, <T::ResponseBody as HttpBody>::Data: Send,
@@ -116,8 +116,8 @@ impl<T> Grpc<T> {
let uri = Uri::from_parts(parts).expect("path_and_query only is valid Uri"); let uri = Uri::from_parts(parts).expect("path_and_query only is valid Uri");
let request = request let request = request
.map(|s| encode(codec.encoder(), Box::pin(s))) .map(|s| encode(codec.encoder(), Box::pin(s)).into_stream())
.map(BoxAsyncBody::new_try); .map(BoxBody::from_stream);
let mut request = request.into_http(uri); let mut request = request.into_http(uri);
@@ -155,3 +155,11 @@ impl<T> Grpc<T> {
Ok(Response::from_http(response)) Ok(Response::from_http(response))
} }
} }
impl<T: Clone> Clone for Grpc<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
+2 -1
View File
@@ -60,7 +60,8 @@ where
yield Ok(item); 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(Ok(d)) => Some(d),
Some(Err(e)) => { Some(Err(e)) => {
let err = e.into(); let err = e.into();
+1 -1
View File
@@ -16,7 +16,7 @@ mod response;
mod service; mod service;
mod status; mod status;
pub use body::{BoxAsyncBody, BoxBody}; pub use body::BoxBody;
pub use request::Request; pub use request::Request;
pub use response::Response; pub use response::Response;
pub use service::GrpcService; pub use service::GrpcService;
+11 -11
View File
@@ -1,5 +1,5 @@
use crate::{ use crate::{
body::{BoxAsyncBody, BytesBuf}, body::{BytesBuf, BoxBody},
codec::{decode, encode, Codec, Streaming}, codec::{decode, encode, Codec, Streaming},
server::{ClientStreamingService, ServerStreamingService, StreamingService, UnaryService}, server::{ClientStreamingService, ServerStreamingService, StreamingService, UnaryService},
Code, Request, Response, Status, Code, Request, Response, Status,
@@ -31,7 +31,7 @@ where
&mut self, &mut self,
mut service: S, mut service: S,
req: http::Request<B>, req: http::Request<B>,
) -> http::Response<BoxAsyncBody> ) -> http::Response<BoxBody>
where where
S: UnaryService<T::Decode, Response = T::Encode>, S: UnaryService<T::Decode, Response = T::Encode>,
B: Body + Send + 'static, B: Body + Send + 'static,
@@ -45,7 +45,7 @@ where
.map_response::<stream::Once<future::Ready<Result<T::Encode, Status>>>>(Err( .map_response::<stream::Once<future::Ready<Result<T::Encode, Status>>>>(Err(
status, status,
)) ))
.map(BoxAsyncBody::new_try); .map(BoxBody::from_stream);
} }
}; };
@@ -54,14 +54,14 @@ where
.await .await
.map(|r| r.map(|m| stream::once(future::ok(m)))); .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<S, B>( pub async fn server_streaming<S, B>(
&mut self, &mut self,
mut service: S, mut service: S,
req: http::Request<B>, req: http::Request<B>,
) -> http::Response<BoxAsyncBody> ) -> http::Response<BoxBody>
where where
S: ServerStreamingService<T::Decode, Response = T::Encode>, S: ServerStreamingService<T::Decode, Response = T::Encode>,
S::ResponseStream: Send + 'static, S::ResponseStream: Send + 'static,
@@ -74,13 +74,13 @@ where
Err(status) => { Err(status) => {
return self return self
.map_response::<S::ResponseStream>(Err(status)) .map_response::<S::ResponseStream>(Err(status))
.map(BoxAsyncBody::new_try); .map(BoxBody::from_stream);
} }
}; };
let response = service.call(request).await; let response = service.call(request).await;
self.map_response(response).map(BoxAsyncBody::new_try) self.map_response(response).map(BoxBody::from_stream)
} }
//BoxStream<T::Decode>, //BoxStream<T::Decode>,
@@ -88,7 +88,7 @@ where
&mut self, &mut self,
mut service: S, mut service: S,
req: http::Request<B>, req: http::Request<B>,
) -> http::Response<BoxAsyncBody> ) -> http::Response<BoxBody>
where where
S: ClientStreamingService<Streaming<T::Decode>, Response = T::Encode>, S: ClientStreamingService<Streaming<T::Decode>, Response = T::Encode>,
T::Decode: Send + 'static, T::Decode: Send + 'static,
@@ -102,14 +102,14 @@ where
.call(request) .call(request)
.await .await
.map(|r| r.map(|m| stream::once(future::ok(m)))); .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<S, B>( pub async fn streaming<S, B>(
&mut self, &mut self,
mut service: S, mut service: S,
req: http::Request<B>, req: http::Request<B>,
) -> http::Response<BoxAsyncBody> ) -> http::Response<BoxBody>
where where
S: StreamingService<Streaming<T::Decode>, Response = T::Encode> + Send, S: StreamingService<Streaming<T::Decode>, Response = T::Encode> + Send,
S::ResponseStream: Send + 'static, S::ResponseStream: Send + 'static,
@@ -119,7 +119,7 @@ where
{ {
let request = self.map_request_streaming(req); let request = self.map_request_streaming(req);
let response = service.call(request).await; 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<B>( async fn map_request_unary<B>(
-143
View File
@@ -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<HelloRequest> for SayHello {
type Response = HelloReply;
type Future = impl Future<Output = Result<Response<Self::Response>, Status>>;
fn call(&mut self, request: Request<HelloRequest>) -> Self::Future {
async move {
println!("REQUEST = {:?}", request);
let reply = HelloReply {
message: "Zomg, it works!".to_string(),
};
Ok(Response::new(reply))
}
}
}
struct SayHelloStream;
impl<S> ClientStreamingService<S> for SayHelloStream
where
S: Stream<Item = Result<HelloRequest, Status>> + Unpin + Send + 'static,
{
type Response = HelloReply;
// type Future = impl Future<Output = Result<Response<Self::Response>, Status>>;
type Future =
Pin<Box<dyn Future<Output = Result<Response<Self::Response>, Status>> + Send + 'static>>;
fn call(&mut self, _req: Request<S>) -> 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<http::Request<RecvBody>> for Svc {
type Response = http::Response<body::BoxAsyncBody>;
type Error = tonic::error::Never;
// type Future = impl Future<Output = Result<Self::Response, Self::Error>>;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, req: http::Request<RecvBody>) -> 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<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, _: ()) -> Self::Future {
future::ok(Svc)
}
}
-97
View File
@@ -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<HelloRequest> for SayHello {
type Response = HelloReply;
type Future = impl Future<Output = Result<Response<Self::Response>, Status>>;
fn call(&mut self, _request: Request<HelloRequest>) -> Self::Future {
async move {
Ok(Response::new(HelloReply {
message: "hello".into(),
}))
}
}
}
struct SayHelloStream;
impl<S> ClientStreamingService<S> for SayHelloStream
where
S: Stream<Item = Result<HelloRequest, Status>> + Unpin + Send + 'static,
{
type Response = HelloReply;
// type Future = impl Future<Output = Result<Response<Self::Response>, Status>>;
type Future =
Pin<Box<dyn Future<Output = Result<Response<Self::Response>, Status>> + Send + 'static>>;
fn call(&mut self, _: Request<S>) -> 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<u8>);
impl From<Vec<u8>> for Body {
fn from(t: Vec<u8>) -> Self {
Body(t)
}
}
impl BufStream for Body {
type Item = std::io::Cursor<Vec<u8>>;
type Error = std::io::Error;
fn poll_buf(&mut self, _cx: &mut Context<'_>) -> Poll<Option<Result<Self::Item, Self::Error>>> {
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()
}
}
+1 -1
View File
@@ -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" } tower-util = { git = "http://github.com/tower-rs/tower", branch = "std-future" }
h2 = { git = "https://github.com/LucioFranco/h2", branch = "lucio/tower-h2-hack" } h2 = { git = "https://github.com/LucioFranco/h2", branch = "lucio/tower-h2-hack" }
http = "0.1" 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" log = "0.4"
[dev-dependencies] [dev-dependencies]
+14 -4
View File
@@ -3,8 +3,8 @@
use http::Request; use http::Request;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio_buf::BufStream;
use tower_h2::Connection; use tower_h2::Connection;
use std::pin::Pin;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -30,11 +30,14 @@ impl From<Vec<u8>> for Body {
} }
} }
impl BufStream for Body { impl http_body::Body for Body {
type Item = std::io::Cursor<Vec<u8>>; type Data = std::io::Cursor<Vec<u8>>;
type Error = std::io::Error; type Error = std::io::Error;
fn poll_buf(&mut self, _cx: &mut Context<'_>) -> Poll<Option<Result<Self::Item, Self::Error>>> { fn poll_data(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
if self.0.is_empty() { if self.0.is_empty() {
return None.into(); return None.into();
} }
@@ -46,4 +49,11 @@ impl BufStream for Body {
Some(Ok(buf)).into() Some(Ok(buf)).into()
} }
fn poll_trailers(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
Ok(None).into()
}
} }
+14 -4
View File
@@ -4,7 +4,7 @@ use futures_util::future;
use http::{Request, Response}; use http::{Request, Response};
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio_buf::BufStream; use std::pin::Pin;
use tower_h2::{RecvBody, Server}; use tower_h2::{RecvBody, Server};
use tower_service::Service; use tower_service::Service;
@@ -84,11 +84,14 @@ impl From<Vec<u8>> for Body {
} }
} }
impl BufStream for Body { impl http_body::Body for Body {
type Item = std::io::Cursor<Vec<u8>>; type Data = std::io::Cursor<Vec<u8>>;
type Error = std::io::Error; type Error = std::io::Error;
fn poll_buf(&mut self, _cx: &mut Context<'_>) -> Poll<Option<Result<Self::Item, Self::Error>>> { fn poll_data(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
if self.0.is_empty() { if self.0.is_empty() {
return None.into(); return None.into();
} }
@@ -100,4 +103,11 @@ impl BufStream for Body {
Some(Ok(buf)).into() Some(Ok(buf)).into()
} }
fn poll_trailers(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
Ok(None).into()
}
} }
+2 -2
View File
@@ -57,10 +57,10 @@ where
} }
fn call(&mut self, request: Request<B>) -> Self::Future { fn call(&mut self, request: Request<B>) -> Self::Future {
let (parts, body) = request.into_parts(); let (parts, mut body) = request.into_parts();
let request = Request::from_parts(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); let res = self.client.send_request(request, eos);
+14 -13
View File
@@ -13,7 +13,7 @@ where
S: Body, S: Body,
{ {
h2: SendStream<SendBuf<S::Data>>, h2: SendStream<SendBuf<S::Data>>,
body: S, body: Pin<Box<dyn Body<Data = S::Data, Error = S::Error> + Send + 'static>>,
state: FlushState, state: FlushState,
} }
@@ -32,13 +32,13 @@ enum DataOrTrailers<B> {
impl<S> Flush<S> impl<S> Flush<S>
where where
S: Body, S: Body + Send + 'static,
S::Error: Into<Box<dyn std::error::Error>>, S::Error: Into<Box<dyn std::error::Error>>,
{ {
pub fn new(src: S, dst: SendStream<SendBuf<S::Data>>) -> Self { pub fn new(src: S, dst: SendStream<SendBuf<S::Data>>) -> Self {
Flush { Flush {
h2: dst, h2: dst,
body: src, body: Box::pin(src),
state: FlushState::Data, state: FlushState::Data,
} }
} }
@@ -50,7 +50,7 @@ where
loop { loop {
match ready!(self.poll_body(cx)) { match ready!(self.poll_body(cx)) {
Some(Ok(Data(buf))) => { 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)?; 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(Ok(d)) => Some(d),
Some(Err(err)) => { Some(Err(err)) => {
let err = err.into(); let err = err.into();
@@ -162,13 +162,14 @@ where
// before we get a RST_STREAM. // before we get a RST_STREAM.
} }
} }
let trailers = ready!(self.body.poll_trailers(cx).map_err(|err| { let trailers =
let err = err.into(); ready!(Pin::new(&mut self.body).poll_trailers(cx).map_err(|err| {
debug!("user body error from poll_trailers: {}", err); let err = err.into();
let reason = crate::error::reason_from_dyn_error(&*err); debug!("user body error from poll_trailers: {}", err);
self.h2.send_reset(reason); let reason = crate::error::reason_from_dyn_error(&*err);
reason self.h2.send_reset(reason);
}))?; reason
}))?;
self.state = FlushState::Done; self.state = FlushState::Done;
if let Some(trailers) = trailers { if let Some(trailers) = trailers {
return Some(Ok(DataOrTrailers::Trailers(trailers))).into(); return Some(Ok(DataOrTrailers::Trailers(trailers))).into();
@@ -182,7 +183,7 @@ where
impl<S> Future for Flush<S> impl<S> Future for Flush<S>
where where
S: Body + Unpin, S: Body + Send + 'static,
S::Error: Into<Box<dyn std::error::Error>>, S::Error: Into<Box<dyn std::error::Error>>,
{ {
type Output = Result<(), ()>; type Output = Result<(), ()>;
+7 -3
View File
@@ -1,6 +1,7 @@
use bytes::{Buf, Bytes, BytesMut}; use bytes::{Buf, Bytes, BytesMut};
use futures_util::TryStreamExt; use futures_util::TryStreamExt;
use http_body::Body; use http_body::Body;
use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
/// Allows a stream to be read from the remote. /// Allows a stream to be read from the remote.
@@ -33,11 +34,14 @@ impl Body for RecvBody {
type Data = Data; type Data = Data;
type Error = h2::Error; type Error = h2::Error;
fn is_end_stream(&self) -> bool { fn is_end_stream(self: Pin<&mut Self>) -> bool {
self.inner.is_end_stream() self.inner.is_end_stream()
} }
fn poll_data(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<Self::Data, h2::Error>>> { fn poll_data(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Data, h2::Error>>> {
let data = match futures_util::ready!(self.inner.try_poll_next_unpin(cx)) { let data = match futures_util::ready!(self.inner.try_poll_next_unpin(cx)) {
Some(Ok(bytes)) => { Some(Ok(bytes)) => {
self.inner self.inner
@@ -54,7 +58,7 @@ impl Body for RecvBody {
} }
fn poll_trailers( fn poll_trailers(
&mut self, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<Result<Option<http::HeaderMap>, h2::Error>> { ) -> Poll<Result<Option<http::HeaderMap>, h2::Error>> {
match futures_util::ready!(self.inner.poll_trailers(cx)) { match futures_util::ready!(self.inner.poll_trailers(cx)) {
+2 -2
View File
@@ -86,10 +86,10 @@ pub async fn handle_request<B>(
B::Data: Unpin, B::Data: Unpin,
B::Error: Into<Box<dyn std::error::Error>>, B::Error: Into<Box<dyn std::error::Error>>,
{ {
let (parts, body) = response.into_parts(); let (parts, mut body) = response.into_parts();
// Check if the response is imemdiately an end-of-stream. // 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, ()); let response = Response::from_parts(parts, ());