diff --git a/Cargo.toml b/Cargo.toml index c76403a..664b3aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ members = [ "tonic", "tonic-macros", + "tonic-examples", "tower-h2" ] diff --git a/tonic-examples/Cargo.toml b/tonic-examples/Cargo.toml new file mode 100644 index 0000000..daf4c9d --- /dev/null +++ b/tonic-examples/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "tonic-examples" +version = "0.1.0" +authors = ["Lucio Franco "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +name = "helloworld-server" +path = "src/helloworld/server.rs" + +# [[bin]] +# name = "helloworld-client" +# path = "src/helloworld/client.rs" + +[dependencies] +tonic = { path = "../tonic" } +tower-h2 = { path = "../tower-h2" } +futures-preview = { version = "=0.3.0-alpha.17", default-features = false, features = ["alloc"]} +tokio = "=0.2.0-alpha.1" +prost = "0.5" +prost-derive = "0.5" +bytes = "0.4" diff --git a/tonic-examples/build.rs b/tonic-examples/build.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tonic-examples/build.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tonic-examples/proto/helloworld/helloworld.proto b/tonic-examples/proto/helloworld/helloworld.proto new file mode 100644 index 0000000..8de5d08 --- /dev/null +++ b/tonic-examples/proto/helloworld/helloworld.proto @@ -0,0 +1,37 @@ +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/tonic-examples/src/helloworld/server.rs b/tonic-examples/src/helloworld/server.rs new file mode 100644 index 0000000..971fa15 --- /dev/null +++ b/tonic-examples/src/helloworld/server.rs @@ -0,0 +1,67 @@ +#![feature(async_await)] + +use std::time::Duration; +use tokio::{timer::Delay, net::TcpListener}; +use tonic::{Request, Response, Status}; +use tower_h2::Server; + +mod proto { + #[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, + } +} + +#[derive(Default, Clone)] +pub struct MyGreeter { + data: String, +} + +#[tonic::server(service = "helloworld.Greeter", proto = "proto")] +impl MyGreeter { + pub async fn say_hello(&self, request: Request) -> Result, Status> { + println!("Got a request: {:?}", request); + + let string = &self.data; + + let when = tokio::clock::now() + Duration::from_millis(100); + Delay::new(when).await; + + println!("My data: {:?}", string); + + Delay::new(when).await; + + let reply = HelloReply { + message: "Zomg, it works!".into(), + }; + Ok(Response::new(reply)) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let addr = "[::1]:50051".parse().unwrap(); + let mut bind = TcpListener::bind(&addr)?; + + let greeter = MyGreeter::default(); + let mut server = Server::new(GrpcServer::new(greeter), Default::default()); + + while let Ok((sock, _addr)) = bind.accept().await { + if let Err(e) = sock.set_nodelay(true) { + return Err(e.into()); + } + + if let Err(e) = server.serve(sock).await { + println!("H2 ERROR: {}", e); + } + } + + Ok(()) +} diff --git a/tonic-macros/src/lib.rs b/tonic-macros/src/lib.rs index f648d84..a1eb8f9 100644 --- a/tonic-macros/src/lib.rs +++ b/tonic-macros/src/lib.rs @@ -41,14 +41,15 @@ pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream { let ts = quote! { use tonic::_codegen; + use proto::*; #[derive(Clone)] pub struct GrpcServer { inner: std::sync::Arc<#s>, } - impl From<#s> for GrpcServer { - fn from(t: #s) -> Self { + impl GrpcServer { + fn new(t: #s) -> Self { Self { inner: std::sync::Arc::new(t) } } } @@ -67,8 +68,8 @@ pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream { } } - impl _codegen::Service<_codegen::http::Request<()>> for GrpcServer { - type Response = tonic::Response; + impl _codegen::Service<_codegen::http::Request> for GrpcServer { + type Response = _codegen::http::Response; type Error = tonic::error::Never; type Future = _codegen::ResponseFuture2; @@ -76,40 +77,48 @@ pub fn server(attr: TokenStream, item: TokenStream) -> TokenStream { Ok(()).into() } - fn call(&mut self, request: _codegen::http::Request<()>) -> Self::Future { + fn call(&mut self, request: _codegen::http::Request) -> Self::Future { let inner = self.inner.clone(); match request.uri().path() { "/helloworld.Greeter/SayHello" => { + use tonic::_codegen::*; + use tonic::*; + + pub struct SayHello(pub std::sync::Arc<#s>); + + impl tonic::server::UnaryService for SayHello { + type Response = HelloReply; + type Future = Pin, Status>> + Send + 'static>>; + + fn call(&mut self, request: Request) -> Self::Future { + let inner = self.0.clone(); + let fut = async move { + inner.#m_ident(request).await + }; + Box::pin(fut) + } + } + let inner = self.inner.clone(); + + let fut = async move { - let codec = tonic::codec::UnitCodec::default(); + let method = SayHello(inner); + let codec = tonic::codec::ProstCodec::new(); let mut grpc = tonic::server::Grpc::new(codec); - - let response = match grpc.unary_request(request).await { - Ok(request) => inner.#m_ident(request).await, - Err(status) => Err(status), - }; - - let response = grpc.unary_response(response.unwrap()) - .await - .map(|b| tonic::body::BoxAsyncBody::new(b)); - // .map(|b| tonic::body::BoxBody::map(b)) - - Ok(response) + let res = grpc.unary(method, request).await; + Ok(res) }; - Box::pin(fut) }, - - "helloworld.Greeter/SayHelloStream" => { - unimplemented!() - }, _ => unimplemented!("use grpc unimplemented") } } } + + }; original.extend(TokenStream::from(ts)); diff --git a/tonic-macros/tests/server.rs b/tonic-macros/tests/server.rs deleted file mode 100644 index fdf7b26..0000000 --- a/tonic-macros/tests/server.rs +++ /dev/null @@ -1,52 +0,0 @@ -#![feature(async_await)] - -use futures::Stream; -use std::time::Duration; -use tokio::timer::Delay; -use tonic::{Request, Response, Status}; - -// #[derive(Debug)] -// struct HelloRequest; -// #[derive(Debug)] -// struct HelloResponse; - -#[derive(Default, Clone)] -pub struct MyGreeter { - data: String, -} - -#[tonic::server(service = "proto/helloworld.proto")] -impl MyGreeter { - pub async fn say_hello(&self, request: Request<()>) -> Result, Status> { - println!("Got a request: {:?}", request); - - let string = &self.data; - - let when = tokio::clock::now() + Duration::from_millis(100); - Delay::new(when).await; - - println!("My data: {:?}", string); - - Delay::new(when).await; - - Ok(Response::new(())) - } - - // pub async fn streaming(&self, request: Request) -> Result, Status> { - // unimplemented!() - // } - - // pub async fn server_stream(&self, request: Request<()>) -> Result { - // unimplemented!() - // } - - // pub async fn client_stream(&self, request: Request) -> Result<(), Status> { - // unimplemented!() - // } -} - -#[tokio::test] -async fn grpc() { - let svc = MyGreeter::default(); - let mut _server = GrpcServer::from(svc); -} diff --git a/tonic/src/codec.rs b/tonic/src/codec.rs index 1064dd1..c166218 100644 --- a/tonic/src/codec.rs +++ b/tonic/src/codec.rs @@ -1,10 +1,8 @@ -#![allow(dead_code)] - use crate::{body::BytesBuf, Code, Status}; use async_stream::stream; use bytes::{Buf, BufMut, BytesMut, IntoBuf}; -use futures_core::TryStream; -use futures_util::{future, TryStreamExt}; +use futures_core::{Stream, TryStream}; +use futures_util::{future, StreamExt}; use http_body::Body; use prost::Message; use std::marker::PhantomData; @@ -27,16 +25,14 @@ pub trait Codec { pub fn encode(mut encoder: T, mut source: U) -> impl TryStream where T: Encoder, - U: TryStream + Unpin, + U: Stream> + Unpin, { stream! { let mut buf = BytesMut::with_capacity(1024); loop { - match source.try_next().await { - Ok(Some(item)) => { - - + match source.next().await { + Some(Ok(item)) => { buf.reserve(5); unsafe { buf.advance_mut(5); @@ -54,8 +50,8 @@ where yield Ok(buf.split_to(len + 5).freeze().into_buf()); }, - Ok(None) => break, - Err(status) => yield Err(status), + Some(Err(status)) => yield Err(status), + None => break, } } } @@ -241,7 +237,7 @@ impl Decoder for ProstDecoder { fn from_decode_error(error: prost::DecodeError) -> crate::Status { // Map Protobuf parse errors to an INTERNAL status code, as per // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md - crate::Status::new(crate::Code::Internal, error.to_string()) + Status::new(Code::Internal, error.to_string()) } #[derive(Default)] @@ -291,5 +287,4 @@ impl Decoder for UnitDecoder { enum State { ReadHeader, ReadBody { compression: bool, len: usize }, - Done, } diff --git a/tonic/src/request.rs b/tonic/src/request.rs index b420eb9..529c007 100644 --- a/tonic/src/request.rs +++ b/tonic/src/request.rs @@ -40,15 +40,19 @@ impl Request { self.message } - /// Convert an HTTP request to a gRPC request - pub fn from_http(http: http::Request) -> Self { - let (head, message) = http.into_parts(); + pub(crate) fn from_http_parts(parts: http::request::Parts, message: T) -> Self { Request { - metadata: MetadataMap::from_headers(head.headers), + metadata: MetadataMap::from_headers(parts.headers), message, } } + /// Convert an HTTP request to a gRPC request + pub fn from_http(http: http::Request) -> Self { + let (parts, message) = http.into_parts(); + Request::from_http_parts(parts, message) + } + pub fn into_http(self, uri: http::Uri) -> http::Request { let mut request = http::Request::new(self.message); diff --git a/tonic/src/response.rs b/tonic/src/response.rs index 7afca99..8905804 100644 --- a/tonic/src/response.rs +++ b/tonic/src/response.rs @@ -16,7 +16,6 @@ impl Response { } } - /// Get a reference to the message pub fn get_ref(&self) -> &T { &self.message } diff --git a/tonic/src/server/grpc.rs b/tonic/src/server/grpc.rs new file mode 100644 index 0000000..b108119 --- /dev/null +++ b/tonic/src/server/grpc.rs @@ -0,0 +1,201 @@ +use crate::{ + body::{BoxAsyncBody, BytesBuf}, + codec::{decode, encode, Codec}, + server::{ClientStreamingService, ServerStreamingService, StreamingService, UnaryService}, + Code, Request, Response, Status, +}; +use futures_core::{Stream, TryStream}; +use futures_util::{future, stream, StreamExt, TryStreamExt}; +use http_body::Body; +use std::pin::Pin; + +type BoxStream = Pin> + Send + 'static>>; + +pub struct Grpc { + codec: T, +} + +impl Grpc +where + T: Codec, + T::Decoder: Send + 'static, + T::Decode: Send + Unpin + 'static, + T::Encoder: Send + 'static, + T::Encode: Send + Unpin + 'static, +{ + pub fn new(codec: T) -> Self { + Self { codec } + } + + pub async fn unary( + &mut self, + mut service: S, + req: http::Request, + ) -> http::Response + where + S: UnaryService, + B: Body + Send + 'static, + B::Data: Send, + B::Error: Into + Send, + { + let request = match self.map_request_unary(req).await { + Ok(r) => r, + Err(status) => { + return self + .map_response::>>>(Err( + status, + )) + .map(BoxAsyncBody::new_try); + } + }; + + let response = service + .call(request) + .await + .map(|r| r.map(|m| stream::once(future::ok(m)))); + + self.map_response(response).map(BoxAsyncBody::new_try) + } + + pub async fn server_streaming( + &mut self, + mut service: S, + req: http::Request, + ) -> http::Response + where + S: ServerStreamingService, + S::ResponseStream: Send + 'static, + B: Body + Send + 'static, + B::Data: Send, + B::Error: Into + Send, + { + let request = match self.map_request_unary(req).await { + Ok(r) => r, + Err(status) => { + return self + .map_response::(Err(status)) + .map(BoxAsyncBody::new_try); + } + }; + + let response = service.call(request).await; + + self.map_response(response).map(BoxAsyncBody::new_try) + } + + pub async fn client_streaming( + &mut self, + mut service: S, + req: http::Request, + ) -> http::Response + where + S: ClientStreamingService, Response = T::Encode>, + T::Decode: Send, + T::Decoder: Send + 'static, + B: Body + Send + 'static, + B::Data: Send, + B::Error: Into + Send, + { + let request = self.map_request_streaming(req); + let response = service + .call(request) + .await + .map(|r| r.map(|m| stream::once(future::ok(m)))); + self.map_response(response).map(BoxAsyncBody::new_try) + } + + pub async fn streaming( + &mut self, + mut service: S, + req: http::Request, + ) -> http::Response + where + S: StreamingService, Response = T::Encode> + Send, + S::ResponseStream: Send + 'static, + B: Body + Send + 'static, + B::Data: Send, + B::Error: Into + Send, + { + let request = self.map_request_streaming(req); + let response = service.call(request).await; + self.map_response(response).map(BoxAsyncBody::new_try) + } + + async fn map_request_unary( + &mut self, + request: http::Request, + ) -> Result, Status> + where + B: Body + Send + 'static, + B::Data: Send, + B::Error: Into + Send, + { + let (parts, body) = request.into_parts(); + let stream = decode(self.codec.decoder(), body).into_stream(); + + futures_util::pin_mut!(stream); + + let message = stream + .try_next() + .await? + .ok_or(Status::new(Code::Internal, "Missing request message."))?; + + Ok(Request::from_http_parts(parts, message)) + } + + fn map_request_streaming( + &mut self, + request: http::Request, + ) -> Request> + where + B: Body + Send + 'static, + B::Data: Send, + B::Error: Into + Send, + { + Request::from_http( + request.map(|b| { + decode(self.codec.decoder(), b).into_stream().boxed() as BoxStream + }), + ) + } + + fn map_response( + &mut self, + response: Result, Status>, + ) -> http::Response> + where + B: TryStream + Send + 'static, + { + match response { + Ok(r) => { + let (mut parts, body) = r.into_http().into_parts(); + + // Set the content type + parts.headers.insert( + http::header::CONTENT_TYPE, + http::header::HeaderValue::from_static(T::CONTENT_TYPE), + ); + + // TODO: find way to pin this to the stack instead + let body = Box::pin(body.into_stream()); + let body = encode(self.codec.encoder(), body).into_stream(); + + let body = Box::pin(body) as BoxStream; + http::Response::from_parts(parts, body) + } + Err(status) => { + let status = stream::once(future::err(status)); + let body = encode(self.codec.encoder(), status).into_stream(); + let (mut parts, _body) = Response::new(()).into_http().into_parts(); + + parts.headers.insert( + http::header::CONTENT_TYPE, + http::header::HeaderValue::from_static(T::CONTENT_TYPE), + ); + + let body = Box::pin(body) as BoxStream; + http::Response::from_parts(parts, body) + } + } + } +} diff --git a/tonic/src/server/mod.rs b/tonic/src/server/mod.rs index e15d455..da4c64f 100644 --- a/tonic/src/server/mod.rs +++ b/tonic/src/server/mod.rs @@ -1,215 +1,7 @@ -use crate::{ - body::{BoxAsyncBody, BytesBuf}, - codec::{self, Codec}, - Request, Response, Status, +mod grpc; +mod service; + +pub use self::grpc::Grpc; +pub use self::service::{ + ClientStreamingService, ServerStreamingService, StreamingService, UnaryService, }; -use futures_core::{Future, Stream, TryStream}; -use futures_util::{future, stream, TryStreamExt}; -use http_body::Body; -use std::pin::Pin; - -type BoxStream = Pin> + Send + 'static>>; - -pub struct Grpc { - codec: T, -} - -// type UnaryFuture = Once>>; -// type ResponseBody = impl Stream>; - -pub trait UnaryService { - /// Protobuf response message type - type Response; - - /// Response future - type Future: Future, Status>>; - - /// Call the service - fn call(&mut self, request: Request) -> Self::Future; -} - -pub trait ServerStreamingService { - /// Protobuf response message type - type Response; - - /// Stream of outbound response messages - type ResponseStream: TryStream + Unpin; - - /// Response future - type Future: Future, crate::Status>>; - - /// Call the service - fn call(&mut self, request: Request) -> Self::Future; -} - -pub trait ClientStreamingService { - /// Protobuf response message type - type Response; - - /// Response future - type Future: Future, Status>>; - - /// Call the service - fn call(&mut self, request: Request) -> Self::Future; -} - -pub trait StreamingService { - /// Protobuf response message type - type Response; - - /// Stream of outbound response messages - type ResponseStream: TryStream + Unpin; - - /// Response future - type Future: Future, crate::Status>>; - - /// Call the service - fn call(&mut self, request: Request) -> Self::Future; -} - -impl Grpc -where - T: Codec, - T::Decode: Unpin + 'static, - T::Encoder: Send + 'static, - T::Encode: Send + Unpin + 'static, -{ - pub fn new(codec: T) -> Self { - Self { codec } - } - - pub async fn unary( - &mut self, - mut service: S, - req: http::Request, - ) -> http::Response - where - S: UnaryService, - B: Body, - B::Error: Into, - { - let (_parts, body) = req.into_parts(); - let stream = codec::decode(self.codec.decoder(), body).into_stream(); - futures_util::pin_mut!(stream); - let message = stream.try_next().await.unwrap().unwrap(); - let request = Request::new(message); - let response = service - .call(request) - .await - .map(|r| r.map(|m| stream::once(future::ok(m)))); - - self.map_response(response).map(BoxAsyncBody::new_try) - } - - pub async fn server_streaming( - &mut self, - mut service: S, - req: http::Request, - ) -> http::Response - where - S: ServerStreamingService, - S::ResponseStream: Send + 'static, - B: Body, - B::Error: Into, - { - let (_parts, body) = req.into_parts(); - let stream = codec::decode(self.codec.decoder(), body).into_stream(); - futures_util::pin_mut!(stream); - let message = stream.try_next().await.unwrap().unwrap(); - let request = Request::new(message); - let response = service.call(request).await; - - self.map_response(response).map(BoxAsyncBody::new_try) - } - - pub async fn client_streaming( - &mut self, - mut service: S, - req: http::Request, - ) -> http::Response> - where - S: ClientStreamingService, Response = T::Encode>, - T::Decode: Send, - T::Decoder: Send + 'static, - B: Body + Send + 'static, - B::Data: Send, - B::Error: Into + Send, - { - let (_parts, body) = req.into_parts(); - let stream = codec::decode(self.codec.decoder(), body).into_stream(); - let stream = Box::pin(stream) as BoxStream; - let request = Request::new(stream); - let response = service.call(request).await.unwrap(); - let message = response.into_inner(); - let source = stream::once(future::ok(message)); - let body = codec::encode(self.codec.encoder(), source); - - http::Response::new(body) - } - - pub async fn streaming( - &mut self, - mut service: S, - req: http::Request, - ) -> http::Response> - where - S: StreamingService, Response = T::Encode>, - T::Decode: Send, - T::Decoder: Send + 'static, - B: Body + Send + 'static, - B::Data: Send, - B::Error: Into + Send, - { - let (_parts, body) = req.into_parts(); - let stream = codec::decode(self.codec.decoder(), body).into_stream(); - let stream = Box::pin(stream) as BoxStream; - let request = Request::new(stream); - let response = service.call(request).await.unwrap(); - let source = response.into_inner(); - let body = codec::encode(self.codec.encoder(), source); - - http::Response::new(body) - } - - // fn map_request(&mut self, request: http::Request) -> Request { - // Request::from_http(request.map(|b| codec::decode(self.codec.decoder(), b))) - // } - - fn map_response( - &mut self, - response: Result, Status>, - ) -> http::Response> - where - B: TryStream + Send + Unpin + 'static, - { - match response { - Ok(r) => { - let (mut parts, body) = r.into_http().into_parts(); - - // Set the content type - parts.headers.insert( - http::header::CONTENT_TYPE, - http::header::HeaderValue::from_static(T::CONTENT_TYPE), - ); - - let body = codec::encode(self.codec.encoder(), body).into_stream(); - - let body = Box::pin(body) as BoxStream; - http::Response::from_parts(parts, body) - } - Err(status) => { - let status = stream::once(future::err(status)); - let body = codec::encode(self.codec.encoder(), status).into_stream(); - let (mut parts, _body) = Response::new(()).into_http().into_parts(); - - parts.headers.insert( - http::header::CONTENT_TYPE, - http::header::HeaderValue::from_static(T::CONTENT_TYPE), - ); - - let body = Box::pin(body) as BoxStream; - http::Response::from_parts(parts, body) - } - } - } -} diff --git a/tonic/src/server/service.rs b/tonic/src/server/service.rs new file mode 100644 index 0000000..a9b956e --- /dev/null +++ b/tonic/src/server/service.rs @@ -0,0 +1,53 @@ +use crate::{Request, Response, Status}; +use futures_core::Stream; +use std::future::Future; + +pub trait UnaryService { + /// Protobuf response message type + type Response; + + /// Response future + type Future: Future, Status>>; + + /// Call the service + fn call(&mut self, request: Request) -> Self::Future; +} + +pub trait ServerStreamingService { + /// Protobuf response message type + type Response; + + /// Stream of outbound response messages + type ResponseStream: Stream> + Unpin; + + /// Response future + type Future: Future, Status>>; + + /// Call the service + fn call(&mut self, request: Request) -> Self::Future; +} + +pub trait ClientStreamingService { + /// Protobuf response message type + type Response; + + /// Response future + type Future: Future, Status>>; + + /// Call the service + fn call(&mut self, request: Request) -> Self::Future; +} + +pub trait StreamingService { + /// Protobuf response message type + type Response; + + /// Stream of outbound response messages + type ResponseStream: Stream> + Unpin; + + /// Response future + type Future: Future, Status>>; + + /// Call the service + fn call(&mut self, request: Request) -> Self::Future; +}