fix(codec): Enforce encoders/decoders are Sync (#84)

Closes #81
This commit is contained in:
Lucio Franco
2019-10-30 15:00:31 -04:00
committed by GitHub
parent 5b4f4689a2
commit 3ce61d9860
11 changed files with 62 additions and 53 deletions
+2 -2
View File
@@ -124,7 +124,7 @@ fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
quote! { quote! {
#stream_doc #stream_doc
type #stream: Stream<Item = Result<#res_message, tonic::Status>> + Send + 'static; type #stream: Stream<Item = Result<#res_message, tonic::Status>> + Send + Sync + 'static;
#method_doc #method_doc
async fn #name(&self, request: tonic::Request<#req_message>) async fn #name(&self, request: tonic::Request<#req_message>)
@@ -142,7 +142,7 @@ fn generate_trait_methods(service: &Service, proto_path: &str) -> TokenStream {
quote! { quote! {
#stream_doc #stream_doc
type #stream: Stream<Item = Result<#res_message, tonic::Status>> + Send + 'static; type #stream: Stream<Item = Result<#res_message, tonic::Status>> + Send + Sync + 'static;
#method_doc #method_doc
async fn #name(&self, request: tonic::Request<tonic::Streaming<#req_message>>) async fn #name(&self, request: tonic::Request<tonic::Streaming<#req_message>>)
+3 -2
View File
@@ -108,7 +108,8 @@ impl server::RouteGuide for RouteGuide {
Ok(Response::new(summary)) Ok(Response::new(summary))
} }
type RouteChatStream = Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>>; type RouteChatStream =
Pin<Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + Sync + 'static>>;
async fn route_chat( async fn route_chat(
&self, &self,
@@ -138,7 +139,7 @@ impl server::RouteGuide for RouteGuide {
Ok(Response::new(Box::pin(output) Ok(Response::new(Box::pin(output)
as Pin< as Pin<
Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + 'static>, Box<dyn Stream<Item = Result<RouteNote, Status>> + Send + Sync + 'static>,
>)) >))
} }
} }
+3 -2
View File
@@ -12,8 +12,9 @@ pub struct TestService;
type Result<T> = std::result::Result<Response<T>, Status>; type Result<T> = std::result::Result<Response<T>, Status>;
type Streaming<T> = Request<tonic::Streaming<T>>; type Streaming<T> = Request<tonic::Streaming<T>>;
type Stream<T> = type Stream<T> = Pin<
Pin<Box<dyn futures_core::Stream<Item = std::result::Result<T, Status>> + Send + 'static>>; Box<dyn futures_core::Stream<Item = std::result::Result<T, Status>> + Send + Sync + 'static>,
>;
#[tonic::async_trait] #[tonic::async_trait]
impl pb::server::TestService for TestService { impl pb::server::TestService for TestService {
+5 -4
View File
@@ -41,10 +41,6 @@ tls = []
name = "bench_main" name = "bench_main"
harness = false harness = false
[dev-dependencies]
rand = "0.7.2"
criterion = "0.3"
[dependencies] [dependencies]
bytes = "0.4" bytes = "0.4"
futures-core-preview = "=0.3.0-alpha.19" futures-core-preview = "=0.3.0-alpha.19"
@@ -83,6 +79,11 @@ openssl1 = { package = "openssl", version = "0.10", optional = true }
# rustls # rustls
tokio-rustls = { version = "=0.12.0-alpha.4", optional = true } tokio-rustls = { version = "=0.12.0-alpha.4", optional = true }
[dev-dependencies]
static_assertions = "1.0"
rand = "0.7.2"
criterion = "0.3"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--cfg", "docsrs"] rustdoc-args = ["--cfg", "docsrs"]
+5 -5
View File
@@ -15,7 +15,7 @@ use std::{
pub(crate) type BytesBuf = <Bytes as IntoBuf>::Buf; pub(crate) type BytesBuf = <Bytes as IntoBuf>::Buf;
/// A trait alias for [`http_body::Body`]. /// A trait alias for [`http_body::Body`].
pub trait Body: sealed::Sealed { pub trait Body: sealed::Sealed + Send + Sync {
/// The body data type. /// The body data type.
type Data: Buf; type Data: Buf;
/// The errors produced from the body. /// The errors produced from the body.
@@ -45,7 +45,7 @@ pub trait Body: sealed::Sealed {
impl<T> Body for T impl<T> Body for T
where where
T: HttpBody, T: HttpBody + Send + Sync + 'static,
T::Error: Into<Error>, T::Error: Into<Error>,
{ {
type Data = T::Data; type Data = T::Data;
@@ -83,7 +83,7 @@ mod sealed {
/// A type erased http body. /// A type erased http body.
pub struct BoxBody { pub struct BoxBody {
inner: Pin<Box<dyn Body<Data = BytesBuf, Error = Status> + Send + 'static>>, inner: Pin<Box<dyn Body<Data = BytesBuf, Error = Status> + Send + Sync + 'static>>,
} }
struct MapBody<B>(B); struct MapBody<B>(B);
@@ -92,7 +92,7 @@ impl BoxBody {
/// 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 new<B>(inner: B) -> Self pub fn new<B>(inner: B) -> Self
where where
B: Body<Data = BytesBuf, Error = Status> + Send + 'static, B: Body<Data = BytesBuf, Error = Status> + Send + Sync + 'static,
{ {
BoxBody { BoxBody {
inner: Box::pin(inner), inner: Box::pin(inner),
@@ -102,7 +102,7 @@ impl BoxBody {
/// 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 + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes>, B::Data: Into<Bytes>,
B::Error: Into<crate::Error>, B::Error: Into<crate::Error>,
{ {
+10 -10
View File
@@ -62,8 +62,8 @@ impl<T> Grpc<T> {
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>, <T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
<T::ResponseBody as HttpBody>::Data: Into<Bytes>, <T::ResponseBody as HttpBody>::Data: Into<Bytes>,
C: Codec<Encode = M1, Decode = M2>, C: Codec<Encode = M1, Decode = M2>,
M1: Send + 'static, M1: Send + Sync + 'static,
M2: Send + 'static, M2: Send + Sync + 'static,
{ {
let request = request.map(|m| stream::once(future::ready(m))); let request = request.map(|m| stream::once(future::ready(m)));
self.client_streaming(request, path, codec).await self.client_streaming(request, path, codec).await
@@ -81,10 +81,10 @@ impl<T> Grpc<T> {
T::ResponseBody: Body + HttpBody + Send + 'static, T::ResponseBody: Body + HttpBody + Send + 'static,
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>, <T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
<T::ResponseBody as HttpBody>::Data: Into<Bytes>, <T::ResponseBody as HttpBody>::Data: Into<Bytes>,
S: Stream<Item = M1> + Send + 'static, S: Stream<Item = M1> + Send + Sync + 'static,
C: Codec<Encode = M1, Decode = M2>, C: Codec<Encode = M1, Decode = M2>,
M1: Send + 'static, M1: Send + Sync + 'static,
M2: Send + 'static, M2: Send + Sync + 'static,
{ {
let (mut parts, body) = self.streaming(request, path, codec).await?.into_parts(); let (mut parts, body) = self.streaming(request, path, codec).await?.into_parts();
@@ -115,8 +115,8 @@ impl<T> Grpc<T> {
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>, <T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
<T::ResponseBody as HttpBody>::Data: Into<Bytes>, <T::ResponseBody as HttpBody>::Data: Into<Bytes>,
C: Codec<Encode = M1, Decode = M2>, C: Codec<Encode = M1, Decode = M2>,
M1: Send + 'static, M1: Send + Sync + 'static,
M2: Send + 'static, M2: Send + Sync + 'static,
{ {
let request = request.map(|m| stream::once(future::ready(m))); let request = request.map(|m| stream::once(future::ready(m)));
self.streaming(request, path, codec).await self.streaming(request, path, codec).await
@@ -134,10 +134,10 @@ impl<T> Grpc<T> {
T::ResponseBody: Body + HttpBody + Send + 'static, T::ResponseBody: Body + HttpBody + Send + 'static,
<T::ResponseBody as HttpBody>::Data: Into<Bytes>, <T::ResponseBody as HttpBody>::Data: Into<Bytes>,
<T::ResponseBody as HttpBody>::Error: Into<crate::Error>, <T::ResponseBody as HttpBody>::Error: Into<crate::Error>,
S: Stream<Item = M1> + Send + 'static, S: Stream<Item = M1> + Send + Sync + 'static,
C: Codec<Encode = M1, Decode = M2>, C: Codec<Encode = M1, Decode = M2>,
M1: Send + 'static, M1: Send + Sync + 'static,
M2: Send + 'static, M2: Send + Sync + 'static,
{ {
let mut parts = Parts::default(); let mut parts = Parts::default();
parts.path_and_query = Some(path); parts.path_and_query = Some(path);
+12 -9
View File
@@ -19,7 +19,7 @@ const BUFFER_SIZE: usize = 8 * 1024;
/// This will wrap some inner [`Body`] and [`Decoder`] and provide an interface /// This will wrap some inner [`Body`] and [`Decoder`] and provide an interface
/// to fetch the message stream and trailing metadata /// to fetch the message stream and trailing metadata
pub struct Streaming<T> { pub struct Streaming<T> {
decoder: Box<dyn Decoder<Item = T, Error = Status> + Send + 'static>, decoder: Box<dyn Decoder<Item = T, Error = Status> + Send + Sync + 'static>,
body: BoxBody, body: BoxBody,
state: State, state: State,
direction: Direction, direction: Direction,
@@ -45,40 +45,40 @@ enum Direction {
impl<T> Streaming<T> { impl<T> Streaming<T> {
pub(crate) fn new_response<B, D>(decoder: D, body: B, status_code: StatusCode) -> Self pub(crate) fn new_response<B, D>(decoder: D, body: B, status_code: StatusCode) -> Self
where where
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes>, B::Data: Into<Bytes>,
B::Error: Into<crate::Error>, B::Error: Into<crate::Error>,
D: Decoder<Item = T, Error = Status> + Send + 'static, D: Decoder<Item = T, Error = Status> + Send + Sync + 'static,
{ {
Self::new(decoder, body, Direction::Response(status_code)) Self::new(decoder, body, Direction::Response(status_code))
} }
pub(crate) fn new_empty<B, D>(decoder: D, body: B) -> Self pub(crate) fn new_empty<B, D>(decoder: D, body: B) -> Self
where where
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes>, B::Data: Into<Bytes>,
B::Error: Into<crate::Error>, B::Error: Into<crate::Error>,
D: Decoder<Item = T, Error = Status> + Send + 'static, D: Decoder<Item = T, Error = Status> + Send + Sync + 'static,
{ {
Self::new(decoder, body, Direction::EmptyResponse) Self::new(decoder, body, Direction::EmptyResponse)
} }
pub(crate) fn new_request<B, D>(decoder: D, body: B) -> Self pub(crate) fn new_request<B, D>(decoder: D, body: B) -> Self
where where
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes>, B::Data: Into<Bytes>,
B::Error: Into<crate::Error>, B::Error: Into<crate::Error>,
D: Decoder<Item = T, Error = Status> + Send + 'static, D: Decoder<Item = T, Error = Status> + Send + Sync + 'static,
{ {
Self::new(decoder, body, Direction::Request) Self::new(decoder, body, Direction::Request)
} }
fn new<B, D>(decoder: D, body: B, direction: Direction) -> Self fn new<B, D>(decoder: D, body: B, direction: Direction) -> Self
where where
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes>, B::Data: Into<Bytes>,
B::Error: Into<crate::Error>, B::Error: Into<crate::Error>,
D: Decoder<Item = T, Error = Status> + Send + 'static, D: Decoder<Item = T, Error = Status> + Send + Sync + 'static,
{ {
Self { Self {
decoder: Box::new(decoder), decoder: Box::new(decoder),
@@ -291,3 +291,6 @@ impl<T> fmt::Debug for Streaming<T> {
f.debug_struct("Streaming").finish() f.debug_struct("Streaming").finish()
} }
} }
#[cfg(test)]
static_assertions::assert_impl_all!(Streaming<()>: Send, Sync);
+7 -5
View File
@@ -16,8 +16,9 @@ pub(crate) fn encode_server<T, U>(
source: U, source: U,
) -> EncodeBody<impl Stream<Item = Result<BytesBuf, Status>>> ) -> EncodeBody<impl Stream<Item = Result<BytesBuf, Status>>>
where where
T: Encoder<Error = Status>, T: Encoder<Error = Status> + Send + Sync + 'static,
U: Stream<Item = Result<T::Item, Status>>, T::Item: Send + Sync,
U: Stream<Item = Result<T::Item, Status>> + Send + Sync + 'static,
{ {
let stream = encode(encoder, source).into_stream(); let stream = encode(encoder, source).into_stream();
EncodeBody::new_server(stream) EncodeBody::new_server(stream)
@@ -28,8 +29,9 @@ pub(crate) fn encode_client<T, U>(
source: U, source: U,
) -> EncodeBody<impl Stream<Item = Result<BytesBuf, Status>>> ) -> EncodeBody<impl Stream<Item = Result<BytesBuf, Status>>>
where where
T: Encoder<Error = Status>, T: Encoder<Error = Status> + Send + Sync + 'static,
U: Stream<Item = T::Item>, T::Item: Send + Sync,
U: Stream<Item = T::Item> + Send + Sync + 'static,
{ {
let stream = encode(encoder, source.map(|x| Ok(x))).into_stream(); let stream = encode(encoder, source.map(|x| Ok(x))).into_stream();
EncodeBody::new_client(stream) EncodeBody::new_client(stream)
@@ -88,7 +90,7 @@ pub(crate) struct EncodeBody<S> {
impl<S> EncodeBody<S> impl<S> EncodeBody<S>
where where
S: Stream<Item = Result<crate::body::BytesBuf, Status>>, S: Stream<Item = Result<crate::body::BytesBuf, Status>> + Send + Sync + 'static,
{ {
pub(crate) fn new_client(inner: S) -> Self { pub(crate) fn new_client(inner: S) -> Self {
Self { Self {
+2 -2
View File
@@ -28,9 +28,9 @@ pub trait Codec: Default {
type Decode: Send + 'static; type Decode: Send + 'static;
/// The encoder that can encode a message. /// The encoder that can encode a message.
type Encoder: Encoder<Item = Self::Encode, Error = Status> + Send + 'static; type Encoder: Encoder<Item = Self::Encode, Error = Status> + Send + Sync + 'static;
/// The encoder that can decode a message. /// The encoder that can decode a message.
type Decoder: Decoder<Item = Self::Decode, Error = Status> + Send + 'static; type Decoder: Decoder<Item = Self::Decode, Error = Status> + Send + Sync + 'static;
/// Fetch the encoder. /// Fetch the encoder.
fn encoder(&mut self) -> Self::Encoder; fn encoder(&mut self) -> Self::Encoder;
+3 -3
View File
@@ -77,7 +77,7 @@ pub trait IntoRequest<T>: sealed::Sealed {
/// ``` /// ```
pub trait IntoStreamingRequest: sealed::Sealed { pub trait IntoStreamingRequest: sealed::Sealed {
/// The RPC request stream type /// The RPC request stream type
type Stream: Stream<Item = Self::Message> + Send + 'static; type Stream: Stream<Item = Self::Message> + Send + Sync + 'static;
/// The RPC request type /// The RPC request type
type Message; type Message;
@@ -182,7 +182,7 @@ impl<T> IntoRequest<T> for Request<T> {
impl<T> IntoStreamingRequest for T impl<T> IntoStreamingRequest for T
where where
T: Stream + Send + 'static, T: Stream + Send + Sync + 'static,
{ {
type Stream = T; type Stream = T;
type Message = T::Item; type Message = T::Item;
@@ -194,7 +194,7 @@ where
impl<T> IntoStreamingRequest for Request<T> impl<T> IntoStreamingRequest for Request<T>
where where
T: Stream + Send + 'static, T: Stream + Send + Sync + 'static,
{ {
type Stream = T; type Stream = T;
type Message = T::Item; type Message = T::Item;
+10 -9
View File
@@ -26,6 +26,7 @@ pub struct Grpc<T> {
impl<T> Grpc<T> impl<T> Grpc<T>
where where
T: Codec, T: Codec,
T::Encode: Sync,
{ {
/// Creates a new gRPC client with the provided [`Codec`]. /// Creates a new gRPC client with the provided [`Codec`].
pub fn new(codec: T) -> Self { pub fn new(codec: T) -> Self {
@@ -40,7 +41,7 @@ where
) -> http::Response<BoxBody> ) -> 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 + Sync + 'static,
B::Data: Into<Bytes> + Send, B::Data: Into<Bytes> + Send,
B::Error: Into<crate::Error> + Send, B::Error: Into<crate::Error> + Send,
{ {
@@ -70,8 +71,8 @@ where
) -> http::Response<BoxBody> ) -> 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 + Sync + 'static,
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes> + Send, B::Data: Into<Bytes> + Send,
B::Error: Into<crate::Error> + Send, B::Error: Into<crate::Error> + Send,
{ {
@@ -95,7 +96,7 @@ where
) -> http::Response<BoxBody> ) -> http::Response<BoxBody>
where where
S: ClientStreamingService<T::Decode, Response = T::Encode>, S: ClientStreamingService<T::Decode, Response = T::Encode>,
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes> + Send + 'static, B::Data: Into<Bytes> + Send + 'static,
B::Error: Into<crate::Error> + Send + 'static, B::Error: Into<crate::Error> + Send + 'static,
{ {
@@ -115,8 +116,8 @@ where
) -> http::Response<BoxBody> ) -> http::Response<BoxBody>
where where
S: StreamingService<T::Decode, Response = T::Encode> + Send, S: StreamingService<T::Decode, Response = T::Encode> + Send,
S::ResponseStream: Send + 'static, S::ResponseStream: Send + Sync + 'static,
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes> + Send, B::Data: Into<Bytes> + Send,
B::Error: Into<crate::Error> + Send, B::Error: Into<crate::Error> + Send,
{ {
@@ -130,7 +131,7 @@ where
request: http::Request<B>, request: http::Request<B>,
) -> Result<Request<T::Decode>, Status> ) -> Result<Request<T::Decode>, Status>
where where
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes> + Send, B::Data: Into<Bytes> + Send,
B::Error: Into<crate::Error> + Send, B::Error: Into<crate::Error> + Send,
{ {
@@ -158,7 +159,7 @@ where
request: http::Request<B>, request: http::Request<B>,
) -> Request<Streaming<T::Decode>> ) -> Request<Streaming<T::Decode>>
where where
B: Body + Send + 'static, B: Body + Send + Sync + 'static,
B::Data: Into<Bytes> + Send, B::Data: Into<Bytes> + Send,
B::Error: Into<crate::Error> + Send, B::Error: Into<crate::Error> + Send,
{ {
@@ -170,7 +171,7 @@ where
response: Result<crate::Response<B>, Status>, response: Result<crate::Response<B>, Status>,
) -> http::Response<BoxBody> ) -> http::Response<BoxBody>
where where
B: TryStream<Ok = T::Encode, Error = Status> + Send + 'static, B: TryStream<Ok = T::Encode, Error = Status> + Send + Sync + 'static,
{ {
match response { match response {
Ok(r) => { Ok(r) => {