Add more docs
This commit is contained in:
@@ -33,7 +33,7 @@ fn generate_unary(method: &Method, proto: &str, path: String) -> TokenStream {
|
||||
quote! {
|
||||
pub async fn #ident (&mut self, request: tonic::Request<#request>)
|
||||
-> Result<tonic::Response<#response>, tonic::Status> {
|
||||
self.inner.ready().await?;
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
self.inner.unary(request, path, codec).await
|
||||
@@ -49,7 +49,7 @@ fn generate_server_streaming(method: &Method, proto: &str, path: String) -> Toke
|
||||
quote! {
|
||||
pub async fn #ident (&mut self, request: tonic::Request<#request>)
|
||||
-> Result<tonic::Response<tonic::codec::Streaming<#response>>, tonic::Status> {
|
||||
self.inner.ready().await?;
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
self.inner.server_streaming(request, path, codec).await
|
||||
@@ -67,7 +67,7 @@ fn generate_client_streaming(method: &Method, proto: &str, path: String) -> Toke
|
||||
-> Result<tonic::Response<#response>, tonic::Status>
|
||||
where S: tonic::_codegen::Stream<Item = Result<#request, tonic::Status>> + Send + 'static,
|
||||
{
|
||||
self.inner.ready().await?;
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
let request = request.map(|s| Box::pin(s));
|
||||
@@ -86,7 +86,7 @@ fn generate_streaming(method: &Method, proto: &str, path: String) -> TokenStream
|
||||
-> Result<tonic::Response<tonic::codec::Streaming<#response>>, tonic::Status>
|
||||
where S: tonic::_codegen::Stream<Item = Result<#request, tonic::Status>> + Send + 'static,
|
||||
{
|
||||
self.inner.ready().await?;
|
||||
self.ready().await?;
|
||||
let codec = tonic::codec::ProstCodec::new();
|
||||
let path = http::uri::PathAndQuery::from_static(#path);
|
||||
let request = request.map(|s| Box::pin(s));
|
||||
|
||||
@@ -26,8 +26,9 @@ pub fn client(attr: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
impl<T> #service_ident <T>
|
||||
where T: tonic::GrpcService<tonic::body::BoxBody>,
|
||||
where T: tonic::client::GrpcService<tonic::BoxBody>,
|
||||
T::ResponseBody: tonic::body::Body + tonic::_codegen::HttpBody + Send + 'static,
|
||||
T::Error: Into<tonic::error::Error>,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Error: Into<tonic::error::Error> + Send,
|
||||
<T::ResponseBody as tonic::_codegen::HttpBody>::Data: Into<bytes::Bytes> + Send, {
|
||||
pub fn new(inner: T) -> Self {
|
||||
@@ -36,7 +37,9 @@ pub fn client(attr: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
pub async fn ready(&mut self) -> Result<(), tonic::Status> {
|
||||
self.inner.ready().await
|
||||
self.inner.ready().await.map_err(|e| {
|
||||
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
|
||||
})
|
||||
}
|
||||
|
||||
#methods
|
||||
|
||||
+43
-26
@@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
body::{Body, BoxBody},
|
||||
client::GrpcService,
|
||||
codec::{encode_client, Codec, Streaming},
|
||||
Code, GrpcService, Request, Response, Status,
|
||||
Code, Request, Response, Status,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures_core::Stream;
|
||||
@@ -11,33 +12,44 @@ use http::{
|
||||
uri::{Parts, PathAndQuery, Uri},
|
||||
};
|
||||
use http_body::Body as HttpBody;
|
||||
use std::fmt;
|
||||
|
||||
/// A gRPC client dispatcher.
|
||||
///
|
||||
/// This will wrap some inner [`GrpcService`] and will encode/decode
|
||||
/// messages via the provided codec.
|
||||
///
|
||||
/// Each request method takes a [`Request`], a [`PathAndQuery`], and a
|
||||
/// [`Codec`]. The request contains the message to send via the
|
||||
/// [`Codec::encoder`]. The path determines the fully qualified path
|
||||
/// that will be appened to the outgoing uri. The path must follow
|
||||
/// the convetions explained in the [gRPC protocol definition] under `Path →`. An
|
||||
/// example of this path could look like `/greeter.Greeter/SayHello`.
|
||||
///
|
||||
/// [gRPC protocol definition]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
|
||||
pub struct Grpc<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> Grpc<T> {
|
||||
/// Creates a new gRPC client with the provided [`GrpcService`].
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub async fn ready(&mut self) -> Result<(), Status>
|
||||
/// Check if the inner [`GrpcService`] is able to accept a new request.
|
||||
///
|
||||
/// This will call [`GrpcService::poll_ready`] until it returns ready or
|
||||
/// an error. If this returns ready the inner [`GrpcService`] is ready to
|
||||
/// accept one more request.
|
||||
pub async fn ready(&mut self) -> Result<(), T::Error>
|
||||
where
|
||||
T: GrpcService<BoxBody>,
|
||||
T::ResponseBody: Body + HttpBody + Send + 'static,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
|
||||
<T::ResponseBody as HttpBody>::Data: Send,
|
||||
{
|
||||
futures_util::future::poll_fn(|cx| self.inner.poll_ready(cx))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Status::new(
|
||||
Code::Unknown,
|
||||
format!("Unexpected connection error: {}", e.into()),
|
||||
)
|
||||
})
|
||||
future::poll_fn(|cx| self.inner.poll_ready(cx)).await
|
||||
}
|
||||
|
||||
/// Send a single unary gRPC request.
|
||||
pub async fn unary<M1, M2, C>(
|
||||
&mut self,
|
||||
request: Request<M1>,
|
||||
@@ -47,18 +59,18 @@ impl<T> Grpc<T> {
|
||||
where
|
||||
T: GrpcService<BoxBody>,
|
||||
T::ResponseBody: Body + HttpBody + Send + 'static,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes> + Send,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes>,
|
||||
C: Codec<Encode = M1, Decode = M2>,
|
||||
C::Encoder: Send + 'static,
|
||||
C::Decoder: Send + 'static,
|
||||
M1: Send + 'static,
|
||||
M2: Send + Unpin + 'static,
|
||||
{
|
||||
let request = request.map(|m| stream::once(future::ok(m)));
|
||||
self.client_streaming(request, path, codec).await
|
||||
}
|
||||
|
||||
/// Send a client side streaming gRPC request.
|
||||
pub async fn client_streaming<S, M1, M2, C>(
|
||||
&mut self,
|
||||
request: Request<S>,
|
||||
@@ -68,14 +80,13 @@ impl<T> Grpc<T> {
|
||||
where
|
||||
T: GrpcService<BoxBody>,
|
||||
T::ResponseBody: Body + HttpBody + Send + 'static,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes> + Send,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes>,
|
||||
S: Stream<Item = Result<M1, Status>> + Send + 'static,
|
||||
C: Codec<Encode = M1, Decode = M2>,
|
||||
C::Encoder: Send + 'static,
|
||||
C::Decoder: Send + 'static,
|
||||
M1: Send,
|
||||
M2: Send + Unpin + 'static,
|
||||
{
|
||||
let (mut parts, body) = self.streaming(request, path, codec).await?.into_parts();
|
||||
|
||||
@@ -93,6 +104,7 @@ impl<T> Grpc<T> {
|
||||
Ok(Response::from_parts(parts, message))
|
||||
}
|
||||
|
||||
/// Send a server side streaming gRPC request.
|
||||
pub async fn server_streaming<M1, M2, C>(
|
||||
&mut self,
|
||||
request: Request<M1>,
|
||||
@@ -102,18 +114,18 @@ impl<T> Grpc<T> {
|
||||
where
|
||||
T: GrpcService<BoxBody>,
|
||||
T::ResponseBody: Body + HttpBody + Send + 'static,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes> + Send,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes>,
|
||||
C: Codec<Encode = M1, Decode = M2>,
|
||||
C::Encoder: Send + 'static,
|
||||
C::Decoder: Send + 'static,
|
||||
M1: Send + 'static,
|
||||
M2: Send + Unpin + 'static,
|
||||
{
|
||||
let request = request.map(|m| stream::once(future::ok(m)));
|
||||
self.streaming(request, path, codec).await
|
||||
}
|
||||
|
||||
/// Send a bi-directional streaming gRPC request.
|
||||
pub async fn streaming<S, M1, M2, C>(
|
||||
&mut self,
|
||||
request: Request<S>,
|
||||
@@ -123,14 +135,13 @@ impl<T> Grpc<T> {
|
||||
where
|
||||
T: GrpcService<BoxBody>,
|
||||
T::ResponseBody: Body + HttpBody + Send + 'static,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error> + Send,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes> + Send,
|
||||
<T::ResponseBody as HttpBody>::Data: Into<Bytes>,
|
||||
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
|
||||
S: Stream<Item = Result<M1, Status>> + Send + 'static,
|
||||
C: Codec<Encode = M1, Decode = M2>,
|
||||
C::Encoder: Send + 'static,
|
||||
C::Decoder: Send + 'static,
|
||||
M1: Send,
|
||||
M2: Send + Unpin + 'static,
|
||||
{
|
||||
let mut parts = Parts::default();
|
||||
parts.path_and_query = Some(path);
|
||||
@@ -138,7 +149,7 @@ impl<T> Grpc<T> {
|
||||
let uri = Uri::from_parts(parts).expect("path_and_query only is valid Uri");
|
||||
|
||||
let request = request
|
||||
.map(|s| encode_client(codec.encoder(), Box::pin(s)))
|
||||
.map(|s| encode_client(codec.encoder(), s))
|
||||
.map(BoxBody::new);
|
||||
|
||||
let mut request = request.into_http(uri);
|
||||
@@ -194,3 +205,9 @@ impl<T: Clone> Clone for Grpc<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Grpc<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Grpc").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
//! gRPC over HTTP2 client implementation.
|
||||
//!
|
||||
//! This module contains the low level components to build a gRPC client. It
|
||||
//! provides a codec agnostic gRPC client dispatcher and a decorated tower
|
||||
//! service trait.
|
||||
//!
|
||||
//! This client is generally used by some code generation tool to provide stubs
|
||||
//! for the gRPC service. Thusly, they are a bit cumbersome to use by hand.
|
||||
|
||||
mod grpc;
|
||||
mod service;
|
||||
|
||||
pub use self::grpc::Grpc;
|
||||
pub use self::service::GrpcService;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
use crate::body::Body;
|
||||
use http_body::Body as HttpBody;
|
||||
use std::future::Future;
|
||||
use std::task::{Context, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
/// Definition of the gRPC trait alias for [`tower_service::Service`].
|
||||
///
|
||||
/// This trait enforces that all tower services provided to [`Grpc`] implements
|
||||
/// the correct traits.
|
||||
pub trait GrpcService<ReqBody> {
|
||||
/// Responses body given by the service.
|
||||
type ResponseBody: Body + HttpBody;
|
||||
/// Errors produced by the service.
|
||||
type Error: Into<crate::Error>;
|
||||
/// The future response value.
|
||||
type Future: Future<Output = Result<http::Response<Self::ResponseBody>, Self::Error>>;
|
||||
|
||||
/// Returns `Ready` when the service is able to process requests.
|
||||
///
|
||||
/// Reference [`Service::poll_ready`].
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
/// Process the request and return the response asynchronously.
|
||||
///
|
||||
/// Reference [`Service::call`].
|
||||
fn call(&mut self, request: http::Request<ReqBody>) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, ReqBody, ResBody> GrpcService<ReqBody> for T
|
||||
where
|
||||
T: Service<http::Request<ReqBody>, Response = http::Response<ResBody>>,
|
||||
T::Error: Into<crate::Error>,
|
||||
ResBody: Body + HttpBody,
|
||||
<ResBody as HttpBody>::Error: Into<crate::Error>,
|
||||
{
|
||||
type ResponseBody = ResBody;
|
||||
type Error = T::Error;
|
||||
type Future = T::Future;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Service::poll_ready(self, cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: http::Request<ReqBody>) -> Self::Future {
|
||||
Service::call(self, request)
|
||||
}
|
||||
}
|
||||
+1
-37
@@ -17,6 +17,7 @@ mod request;
|
||||
mod response;
|
||||
mod status;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use body::BoxBody;
|
||||
pub use request::Request;
|
||||
pub use response::Response;
|
||||
@@ -25,43 +26,6 @@ pub use tonic_macros::{client, server};
|
||||
|
||||
pub(crate) use error::Error;
|
||||
|
||||
use crate::body::Body;
|
||||
use http_body::Body as HttpBody;
|
||||
use std::future::Future;
|
||||
use std::task::{Context, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
pub trait GrpcService<ReqBody> {
|
||||
type ResponseBody: Body + HttpBody;
|
||||
type Error: Into<crate::Error>;
|
||||
|
||||
type Future: Future<Output = Result<http::Response<Self::ResponseBody>, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
|
||||
|
||||
fn call(&mut self, request: http::Request<ReqBody>) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<T, ReqBody, ResBody> GrpcService<ReqBody> for T
|
||||
where
|
||||
T: Service<http::Request<ReqBody>, Response = http::Response<ResBody>>,
|
||||
T::Error: Into<crate::Error>,
|
||||
ResBody: Body + HttpBody,
|
||||
<ResBody as HttpBody>::Error: Into<crate::Error>,
|
||||
{
|
||||
type ResponseBody = ResBody;
|
||||
type Error = T::Error;
|
||||
type Future = T::Future;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Service::poll_ready(self, cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, request: http::Request<ReqBody>) -> Self::Future {
|
||||
Service::call(self, request)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
||||
pub mod _codegen {
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{
|
||||
service::{BoxService, Connection, ServiceList},
|
||||
Endpoint,
|
||||
};
|
||||
use crate::{BoxBody, GrpcService};
|
||||
use crate::{client::GrpcService, BoxBody};
|
||||
use futures_util::try_future::{MapErr, TryFutureExt};
|
||||
use http::Uri;
|
||||
use hyper::{Request, Response};
|
||||
|
||||
Reference in New Issue
Block a user