feat(codec): compression support (#692)

* Initial compression support

* Support configuring compression on `Server`

* Minor clean up

* Test that compression is actually happening

* Clean up some todos

* channels compressing requests

* Move compression to be on the codecs

* Test sending compressed request to server that doesn't support it

* Clean up a bit

* Compress server streams

* Compress client streams

* Bidirectional streaming compression

* Handle receiving unsupported encoding

* Clean up

* Add note to future self

* Support disabling compression for individual responses

* Add docs

* Add compression examples

* Disable compression behind feature flag

* Add some docs

* Make flate2 optional dependency

* Fix docs wording

* Format

* Reply with which encodings are supported

* Convert tests to use mocked io

* Fix lints

* Use separate counters

* Don't make a long stream

* Address review feedback
This commit is contained in:
David Pedersen
2021-07-02 11:25:03 -04:00
committed by GitHub
parent 7677ad6476
commit 0583cff80f
30 changed files with 2191 additions and 98 deletions
+21 -21
View File
@@ -20,8 +20,6 @@ pub fn generate<T: Service>(
let connect = generate_connect(&service_ident);
let service_doc = generate_doc_comments(service.comment());
let struct_debug = format!("{} {{{{ ... }}}}", &service_ident);
quote! {
/// Generated client implementations.
pub mod #client_mod {
@@ -29,6 +27,7 @@ pub fn generate<T: Service>(
use tonic::codegen::*;
#service_doc
#[derive(Debug, Clone)]
pub struct #service_ident<T> {
inner: tonic::client::Grpc<T>,
}
@@ -59,22 +58,23 @@ pub fn generate<T: Service>(
#service_ident::new(InterceptedService::new(inner, interceptor))
}
/// Compress requests with `gzip`.
///
/// This requires the server to support it otherwise it might respond with an
/// error.
pub fn send_gzip(mut self) -> Self {
self.inner = self.inner.send_gzip();
self
}
/// Enable decompressing responses with `gzip`.
pub fn accept_gzip(mut self) -> Self {
self.inner = self.inner.accept_gzip();
self
}
#methods
}
impl<T: Clone> Clone for #service_ident<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T> std::fmt::Debug for #service_ident<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, #struct_debug)
}
}
}
}
}
@@ -153,10 +153,10 @@ fn generate_unary<T: Method>(
&mut self,
request: impl tonic::IntoRequest<#request>,
) -> Result<tonic::Response<#response>, tonic::Status> {
self.inner.ready().await.map_err(|e| {
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
})?;
let codec = #codec_name::default();
self.inner.ready().await.map_err(|e| {
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
})?;
let codec = #codec_name::default();
let path = http::uri::PathAndQuery::from_static(#path);
self.inner.unary(request.into_request(), path, codec).await
}
@@ -204,7 +204,7 @@ fn generate_client_streaming<T: Method>(
pub async fn #ident(
&mut self,
request: impl tonic::IntoStreamingRequest<Message = #request>
) -> Result<tonic::Response<#response>, tonic::Status> {
) -> Result<tonic::Response<#response>, tonic::Status> where T: std::fmt::Debug {
self.inner.ready().await.map_err(|e| {
tonic::Status::new(tonic::Code::Unknown, format!("Service was not ready: {}", e.into()))
})?;
+56 -7
View File
@@ -36,6 +36,32 @@ pub fn generate<T: Service>(
);
let transport = generate_transport(&server_service, &server_trait, &path);
let compression_enabled = cfg!(feature = "compression");
let compression_config_ty = if compression_enabled {
quote! { EnabledCompressionEncodings }
} else {
quote! { () }
};
let configure_compression_methods = if compression_enabled {
quote! {
/// Enable decompressing requests with `gzip`.
pub fn accept_gzip(mut self) -> Self {
self.accept_compression_encodings.enable_gzip();
self
}
/// Compress responses with `gzip`, if the client supports it.
pub fn send_gzip(mut self) -> Self {
self.send_compression_encodings.enable_gzip();
self
}
}
} else {
quote! {}
};
quote! {
/// Generated server implementations.
pub mod #server_mod {
@@ -48,6 +74,8 @@ pub fn generate<T: Service>(
#[derive(Debug)]
pub struct #server_service<T: #server_trait> {
inner: _Inner<T>,
accept_compression_encodings: #compression_config_ty,
send_compression_encodings: #compression_config_ty,
}
struct _Inner<T>(Arc<T>);
@@ -56,7 +84,11 @@ pub fn generate<T: Service>(
pub fn new(inner: T) -> Self {
let inner = Arc::new(inner);
let inner = _Inner(inner);
Self { inner }
Self {
inner,
accept_compression_encodings: Default::default(),
send_compression_encodings: Default::default(),
}
}
pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F>
@@ -65,6 +97,8 @@ pub fn generate<T: Service>(
{
InterceptedService::new(Self::new(inner), interceptor)
}
#configure_compression_methods
}
impl<T, B> Service<http::Request<B>> for #server_service<T>
@@ -102,7 +136,11 @@ pub fn generate<T: Service>(
impl<T: #server_trait> Clone for #server_service<T> {
fn clone(&self) -> Self {
let inner = self.inner.clone();
Self { inner }
Self {
inner,
accept_compression_encodings: self.accept_compression_encodings,
send_compression_encodings: self.send_compression_encodings,
}
}
}
@@ -335,13 +373,16 @@ fn generate_unary<T: Method>(
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = #service_ident(inner);
let codec = #codec_name::default();
let mut grpc = tonic::server::Grpc::new(codec);
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(accept_compression_encodings, send_compression_encodings);
let res = grpc.unary(method, req).await;
Ok(res)
@@ -379,19 +420,21 @@ fn generate_server_streaming<T: Method>(
let inner = self.0.clone();
let fut = async move {
(*inner).#method_ident(request).await
};
Box::pin(fut)
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = #service_ident(inner);
let codec = #codec_name::default();
let mut grpc = tonic::server::Grpc::new(codec);
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(accept_compression_encodings, send_compression_encodings);
let res = grpc.server_streaming(method, req).await;
Ok(res)
@@ -432,13 +475,16 @@ fn generate_client_streaming<T: Method>(
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = #service_ident(inner);
let codec = #codec_name::default();
let mut grpc = tonic::server::Grpc::new(codec);
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(accept_compression_encodings, send_compression_encodings);
let res = grpc.client_streaming(method, req).await;
Ok(res)
@@ -482,13 +528,16 @@ fn generate_streaming<T: Method>(
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let inner = self.inner.clone();
let fut = async move {
let inner = inner.0;
let method = #service_ident(inner);
let codec = #codec_name::default();
let mut grpc = tonic::server::Grpc::new(codec);
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(accept_compression_encodings, send_compression_encodings);
let res = grpc.streaming(method, req).await;
Ok(res)