Skip to content

Commit a8ddaf7

Browse files
committed
Add deflate compression support
1 parent 5e9a5bc commit a8ddaf7

File tree

6 files changed

+46
-14
lines changed

6 files changed

+46
-14
lines changed

tests/compression/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pin-project = "1.0"
1616
prost = "0.13"
1717
tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]}
1818
tokio-stream = "0.1"
19-
tonic = {path = "../../tonic", features = ["gzip", "zstd"]}
19+
tonic = {path = "../../tonic", features = ["gzip", "deflate", "zstd"]}
2020
tower = "0.5"
2121
tower-http = {version = "0.6", features = ["map-response-body", "map-request-body"]}
2222

tonic/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ version = "0.13.0"
2525
[features]
2626
codegen = ["dep:async-trait"]
2727
gzip = ["dep:flate2"]
28+
deflate = ["dep:flate2"]
2829
zstd = ["dep:zstd"]
2930
default = ["transport", "codegen", "prost"]
3031
prost = ["dep:prost"]

tonic/src/client/grpc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ impl GrpcConfig {
404404
.headers_mut()
405405
.insert(CONTENT_TYPE, GRPC_CONTENT_TYPE);
406406

407-
#[cfg(any(feature = "gzip", feature = "zstd"))]
407+
#[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))]
408408
if let Some(encoding) = self.send_compression_encodings {
409409
request.headers_mut().insert(
410410
crate::codec::compression::ENCODING_HEADER,

tonic/src/codec/compression.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::{metadata::MetadataValue, Status};
22
use bytes::{Buf, BufMut, BytesMut};
3+
#[cfg(feature = "deflate")]
4+
use flate2::read::{DeflateDecoder, DeflateEncoder};
35
#[cfg(feature = "gzip")]
46
use flate2::read::{GzDecoder, GzEncoder};
57
use std::fmt;
@@ -14,7 +16,7 @@ pub(crate) const ACCEPT_ENCODING_HEADER: &str = "grpc-accept-encoding";
1416
/// Represents an ordered list of compression encodings that are enabled.
1517
#[derive(Debug, Default, Clone, Copy)]
1618
pub struct EnabledCompressionEncodings {
17-
inner: [Option<CompressionEncoding>; 2],
19+
inner: [Option<CompressionEncoding>; 3],
1820
}
1921

2022
impl EnabledCompressionEncodings {
@@ -85,6 +87,9 @@ pub enum CompressionEncoding {
8587
#[cfg(feature = "gzip")]
8688
Gzip,
8789
#[allow(missing_docs)]
90+
#[cfg(feature = "deflate")]
91+
Deflate,
92+
#[allow(missing_docs)]
8893
#[cfg(feature = "zstd")]
8994
Zstd,
9095
}
@@ -93,6 +98,8 @@ impl CompressionEncoding {
9398
pub(crate) const ENCODINGS: &'static [CompressionEncoding] = &[
9499
#[cfg(feature = "gzip")]
95100
CompressionEncoding::Gzip,
101+
#[cfg(feature = "deflate")]
102+
CompressionEncoding::Deflate,
96103
#[cfg(feature = "zstd")]
97104
CompressionEncoding::Zstd,
98105
];
@@ -112,6 +119,8 @@ impl CompressionEncoding {
112119
split_by_comma(header_value_str).find_map(|value| match value {
113120
#[cfg(feature = "gzip")]
114121
"gzip" => Some(CompressionEncoding::Gzip),
122+
#[cfg(feature = "deflate")]
123+
"deflate" => Some(CompressionEncoding::Deflate),
115124
#[cfg(feature = "zstd")]
116125
"zstd" => Some(CompressionEncoding::Zstd),
117126
_ => None,
@@ -132,6 +141,10 @@ impl CompressionEncoding {
132141
b"gzip" if enabled_encodings.is_enabled(CompressionEncoding::Gzip) => {
133142
Ok(Some(CompressionEncoding::Gzip))
134143
}
144+
#[cfg(feature = "deflate")]
145+
b"deflate" if enabled_encodings.is_enabled(CompressionEncoding::Deflate) => {
146+
Ok(Some(CompressionEncoding::Deflate))
147+
}
135148
#[cfg(feature = "zstd")]
136149
b"zstd" if enabled_encodings.is_enabled(CompressionEncoding::Zstd) => {
137150
Ok(Some(CompressionEncoding::Zstd))
@@ -170,6 +183,8 @@ impl CompressionEncoding {
170183
match self {
171184
#[cfg(feature = "gzip")]
172185
CompressionEncoding::Gzip => "gzip",
186+
#[cfg(feature = "deflate")]
187+
CompressionEncoding::Deflate => "deflate",
173188
#[cfg(feature = "zstd")]
174189
CompressionEncoding::Zstd => "zstd",
175190
}
@@ -217,6 +232,15 @@ pub(crate) fn compress(
217232
);
218233
std::io::copy(&mut gzip_encoder, &mut out_writer)?;
219234
}
235+
#[cfg(feature = "deflate")]
236+
CompressionEncoding::Deflate => {
237+
let mut deflate_encoder = DeflateEncoder::new(
238+
&decompressed_buf[0..len],
239+
// FIXME: support customizing the compression level
240+
flate2::Compression::new(6),
241+
);
242+
std::io::copy(&mut deflate_encoder, &mut out_writer)?;
243+
}
220244
#[cfg(feature = "zstd")]
221245
CompressionEncoding::Zstd => {
222246
let mut zstd_encoder = Encoder::new(
@@ -247,7 +271,7 @@ pub(crate) fn decompress(
247271
((estimate_decompressed_len / buffer_growth_interval) + 1) * buffer_growth_interval;
248272
out_buf.reserve(capacity);
249273

250-
#[cfg(any(feature = "gzip", feature = "zstd"))]
274+
#[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))]
251275
let mut out_writer = out_buf.writer();
252276

253277
match settings.encoding {
@@ -256,6 +280,11 @@ pub(crate) fn decompress(
256280
let mut gzip_decoder = GzDecoder::new(&compressed_buf[0..len]);
257281
std::io::copy(&mut gzip_decoder, &mut out_writer)?;
258282
}
283+
#[cfg(feature = "deflate")]
284+
CompressionEncoding::Deflate => {
285+
let mut deflate_decoder = DeflateDecoder::new(&compressed_buf[0..len]);
286+
std::io::copy(&mut deflate_decoder, &mut out_writer)?;
287+
}
259288
#[cfg(feature = "zstd")]
260289
CompressionEncoding::Zstd => {
261290
let mut zstd_decoder = Decoder::new(&compressed_buf[0..len])?;
@@ -282,7 +311,7 @@ pub enum SingleMessageCompressionOverride {
282311

283312
#[cfg(test)]
284313
mod tests {
285-
#[cfg(any(feature = "gzip", feature = "zstd"))]
314+
#[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))]
286315
use http::HeaderValue;
287316

288317
use super::*;
@@ -300,13 +329,13 @@ mod tests {
300329
const GZIP: HeaderValue = HeaderValue::from_static("gzip,identity");
301330

302331
let encodings = EnabledCompressionEncodings {
303-
inner: [Some(CompressionEncoding::Gzip), None],
332+
inner: [Some(CompressionEncoding::Gzip), None, None],
304333
};
305334

306335
assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), GZIP);
307336

308337
let encodings = EnabledCompressionEncodings {
309-
inner: [None, Some(CompressionEncoding::Gzip)],
338+
inner: [None, None, Some(CompressionEncoding::Gzip)],
310339
};
311340

312341
assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), GZIP);
@@ -318,43 +347,45 @@ mod tests {
318347
const ZSTD: HeaderValue = HeaderValue::from_static("zstd,identity");
319348

320349
let encodings = EnabledCompressionEncodings {
321-
inner: [Some(CompressionEncoding::Zstd), None],
350+
inner: [Some(CompressionEncoding::Zstd), None, None],
322351
};
323352

324353
assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), ZSTD);
325354

326355
let encodings = EnabledCompressionEncodings {
327-
inner: [None, Some(CompressionEncoding::Zstd)],
356+
inner: [None, None, Some(CompressionEncoding::Zstd)],
328357
};
329358

330359
assert_eq!(encodings.into_accept_encoding_header_value().unwrap(), ZSTD);
331360
}
332361

333362
#[test]
334-
#[cfg(all(feature = "gzip", feature = "zstd"))]
363+
#[cfg(all(feature = "gzip", feature = "deflate", feature = "zstd"))]
335364
fn convert_gzip_and_zstd_into_header_value() {
336365
let encodings = EnabledCompressionEncodings {
337366
inner: [
338367
Some(CompressionEncoding::Gzip),
368+
Some(CompressionEncoding::Deflate),
339369
Some(CompressionEncoding::Zstd),
340370
],
341371
};
342372

343373
assert_eq!(
344374
encodings.into_accept_encoding_header_value().unwrap(),
345-
HeaderValue::from_static("gzip,zstd,identity"),
375+
HeaderValue::from_static("gzip,deflate,zstd,identity"),
346376
);
347377

348378
let encodings = EnabledCompressionEncodings {
349379
inner: [
350380
Some(CompressionEncoding::Zstd),
381+
Some(CompressionEncoding::Deflate),
351382
Some(CompressionEncoding::Gzip),
352383
],
353384
};
354385

355386
assert_eq!(
356387
encodings.into_accept_encoding_header_value().unwrap(),
357-
HeaderValue::from_static("zstd,gzip,identity"),
388+
HeaderValue::from_static("zstd,deflate,gzip,identity"),
358389
);
359390
}
360391
}

tonic/src/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl<T> Response<T> {
120120
/// **Note**: This only has effect on responses to unary requests and responses to client to
121121
/// server streams. Response streams (server to client stream and bidirectional streams) will
122122
/// still be compressed according to the configuration of the server.
123-
#[cfg(feature = "gzip")]
123+
#[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))]
124124
pub fn disable_compression(&mut self) {
125125
self.extensions_mut()
126126
.insert(crate::codec::compression::SingleMessageCompressionOverride::Disable);

tonic/src/server/grpc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ where
435435
.headers
436436
.insert(http::header::CONTENT_TYPE, GRPC_CONTENT_TYPE);
437437

438-
#[cfg(any(feature = "gzip", feature = "zstd"))]
438+
#[cfg(any(feature = "gzip", feature = "deflate", feature = "zstd"))]
439439
if let Some(encoding) = accept_encoding {
440440
// Set the content encoding
441441
parts.headers.insert(

0 commit comments

Comments
 (0)