@@ -9,6 +9,9 @@ use async_compression::tokio::bufread::GzipDecoder;
99#[ cfg( feature = "brotli" ) ]
1010use async_compression:: tokio:: bufread:: BrotliDecoder ;
1111
12+ #[ cfg( feature = "zstd" ) ]
13+ use async_compression:: tokio:: bufread:: ZstdDecoder ;
14+
1215#[ cfg( feature = "deflate" ) ]
1316use async_compression:: tokio:: bufread:: ZlibDecoder ;
1417
@@ -19,9 +22,19 @@ use http::HeaderMap;
1922use hyper:: body:: Body as HttpBody ;
2023use hyper:: body:: Frame ;
2124
22- #[ cfg( any( feature = "gzip" , feature = "brotli" , feature = "deflate" ) ) ]
25+ #[ cfg( any(
26+ feature = "gzip" ,
27+ feature = "brotli" ,
28+ feature = "zstd" ,
29+ feature = "deflate"
30+ ) ) ]
2331use tokio_util:: codec:: { BytesCodec , FramedRead } ;
24- #[ cfg( any( feature = "gzip" , feature = "brotli" , feature = "deflate" ) ) ]
32+ #[ cfg( any(
33+ feature = "gzip" ,
34+ feature = "brotli" ,
35+ feature = "zstd" ,
36+ feature = "deflate"
37+ ) ) ]
2538use tokio_util:: io:: StreamReader ;
2639
2740use super :: body:: ResponseBody ;
@@ -33,6 +46,8 @@ pub(super) struct Accepts {
3346 pub ( super ) gzip : bool ,
3447 #[ cfg( feature = "brotli" ) ]
3548 pub ( super ) brotli : bool ,
49+ #[ cfg( feature = "zstd" ) ]
50+ pub ( super ) zstd : bool ,
3651 #[ cfg( feature = "deflate" ) ]
3752 pub ( super ) deflate : bool ,
3853}
@@ -44,6 +59,8 @@ impl Accepts {
4459 gzip : false ,
4560 #[ cfg( feature = "brotli" ) ]
4661 brotli : false ,
62+ #[ cfg( feature = "zstd" ) ]
63+ zstd : false ,
4764 #[ cfg( feature = "deflate" ) ]
4865 deflate : false ,
4966 }
@@ -59,7 +76,12 @@ pub(crate) struct Decoder {
5976
6077type PeekableIoStream = Peekable < IoStream > ;
6178
62- #[ cfg( any( feature = "gzip" , feature = "brotli" , feature = "deflate" ) ) ]
79+ #[ cfg( any(
80+ feature = "gzip" ,
81+ feature = "zstd" ,
82+ feature = "brotli" ,
83+ feature = "deflate"
84+ ) ) ]
6385type PeekableIoStreamReader = StreamReader < PeekableIoStream , Bytes > ;
6486
6587enum Inner {
@@ -74,12 +96,21 @@ enum Inner {
7496 #[ cfg( feature = "brotli" ) ]
7597 Brotli ( Pin < Box < FramedRead < BrotliDecoder < PeekableIoStreamReader > , BytesCodec > > > ) ,
7698
99+ /// A `Zstd` decoder will uncompress the zstd compressed response content before returning it.
100+ #[ cfg( feature = "zstd" ) ]
101+ Zstd ( Pin < Box < FramedRead < ZstdDecoder < PeekableIoStreamReader > , BytesCodec > > > ) ,
102+
77103 /// A `Deflate` decoder will uncompress the deflated response content before returning it.
78104 #[ cfg( feature = "deflate" ) ]
79105 Deflate ( Pin < Box < FramedRead < ZlibDecoder < PeekableIoStreamReader > , BytesCodec > > > ) ,
80106
81107 /// A decoder that doesn't have a value yet.
82- #[ cfg( any( feature = "brotli" , feature = "gzip" , feature = "deflate" ) ) ]
108+ #[ cfg( any(
109+ feature = "brotli" ,
110+ feature = "zstd" ,
111+ feature = "gzip" ,
112+ feature = "deflate"
113+ ) ) ]
83114 Pending ( Pin < Box < Pending > > ) ,
84115}
85116
@@ -93,6 +124,8 @@ enum DecoderType {
93124 Gzip ,
94125 #[ cfg( feature = "brotli" ) ]
95126 Brotli ,
127+ #[ cfg( feature = "zstd" ) ]
128+ Zstd ,
96129 #[ cfg( feature = "deflate" ) ]
97130 Deflate ,
98131}
@@ -155,6 +188,21 @@ impl Decoder {
155188 }
156189 }
157190
191+ /// A zstd decoder.
192+ ///
193+ /// This decoder will buffer and decompress chunks that are zstd compressed.
194+ #[ cfg( feature = "zstd" ) ]
195+ fn zstd ( body : ResponseBody ) -> Decoder {
196+ use futures_util:: StreamExt ;
197+
198+ Decoder {
199+ inner : Inner :: Pending ( Box :: pin ( Pending (
200+ IoStream ( body) . peekable ( ) ,
201+ DecoderType :: Zstd ,
202+ ) ) ) ,
203+ }
204+ }
205+
158206 /// A deflate decoder.
159207 ///
160208 /// This decoder will buffer and decompress chunks that are deflated.
@@ -170,7 +218,12 @@ impl Decoder {
170218 }
171219 }
172220
173- #[ cfg( any( feature = "brotli" , feature = "gzip" , feature = "deflate" ) ) ]
221+ #[ cfg( any(
222+ feature = "brotli" ,
223+ feature = "zstd" ,
224+ feature = "gzip" ,
225+ feature = "deflate"
226+ ) ) ]
174227 fn detect_encoding ( headers : & mut HeaderMap , encoding_str : & str ) -> bool {
175228 use http:: header:: { CONTENT_ENCODING , CONTENT_LENGTH , TRANSFER_ENCODING } ;
176229 use log:: warn;
@@ -225,6 +278,13 @@ impl Decoder {
225278 }
226279 }
227280
281+ #[ cfg( feature = "zstd" ) ]
282+ {
283+ if _accepts. zstd && Decoder :: detect_encoding ( _headers, "zstd" ) {
284+ return Decoder :: zstd ( body) ;
285+ }
286+ }
287+
228288 #[ cfg( feature = "deflate" ) ]
229289 {
230290 if _accepts. deflate && Decoder :: detect_encoding ( _headers, "deflate" ) {
@@ -245,7 +305,12 @@ impl HttpBody for Decoder {
245305 cx : & mut Context ,
246306 ) -> Poll < Option < Result < Frame < Self :: Data > , Self :: Error > > > {
247307 match self . inner {
248- #[ cfg( any( feature = "brotli" , feature = "gzip" , feature = "deflate" ) ) ]
308+ #[ cfg( any(
309+ feature = "brotli" ,
310+ feature = "zstd" ,
311+ feature = "gzip" ,
312+ feature = "deflate"
313+ ) ) ]
249314 Inner :: Pending ( ref mut future) => match Pin :: new ( future) . poll ( cx) {
250315 Poll :: Ready ( Ok ( inner) ) => {
251316 self . inner = inner;
@@ -277,6 +342,14 @@ impl HttpBody for Decoder {
277342 None => Poll :: Ready ( None ) ,
278343 }
279344 }
345+ #[ cfg( feature = "zstd" ) ]
346+ Inner :: Zstd ( ref mut decoder) => {
347+ match futures_core:: ready!( Pin :: new( decoder) . poll_next( cx) ) {
348+ Some ( Ok ( bytes) ) => Poll :: Ready ( Some ( Ok ( Frame :: data ( bytes. freeze ( ) ) ) ) ) ,
349+ Some ( Err ( err) ) => Poll :: Ready ( Some ( Err ( crate :: error:: decode_io ( err) ) ) ) ,
350+ None => Poll :: Ready ( None ) ,
351+ }
352+ }
280353 #[ cfg( feature = "deflate" ) ]
281354 Inner :: Deflate ( ref mut decoder) => {
282355 match futures_core:: ready!( Pin :: new( decoder) . poll_next( cx) ) {
@@ -292,7 +365,12 @@ impl HttpBody for Decoder {
292365 match self . inner {
293366 Inner :: PlainText ( ref body) => HttpBody :: size_hint ( body) ,
294367 // the rest are "unknown", so default
295- #[ cfg( any( feature = "brotli" , feature = "gzip" , feature = "deflate" ) ) ]
368+ #[ cfg( any(
369+ feature = "brotli" ,
370+ feature = "zstd" ,
371+ feature = "gzip" ,
372+ feature = "deflate"
373+ ) ) ]
296374 _ => http_body:: SizeHint :: default ( ) ,
297375 }
298376 }
@@ -332,6 +410,11 @@ impl Future for Pending {
332410 BrotliDecoder :: new ( StreamReader :: new ( _body) ) ,
333411 BytesCodec :: new ( ) ,
334412 ) ) ) ) ) ,
413+ #[ cfg( feature = "zstd" ) ]
414+ DecoderType :: Zstd => Poll :: Ready ( Ok ( Inner :: Zstd ( Box :: pin ( FramedRead :: new (
415+ ZstdDecoder :: new ( StreamReader :: new ( _body) ) ,
416+ BytesCodec :: new ( ) ,
417+ ) ) ) ) ) ,
335418 #[ cfg( feature = "gzip" ) ]
336419 DecoderType :: Gzip => Poll :: Ready ( Ok ( Inner :: Gzip ( Box :: pin ( FramedRead :: new (
337420 GzipDecoder :: new ( StreamReader :: new ( _body) ) ,
@@ -381,22 +464,37 @@ impl Accepts {
381464 gzip: false,
382465 #[cfg(feature = "brotli")]
383466 brotli: false,
467+ #[cfg(feature = "zstd")]
468+ zstd: false,
384469 #[cfg(feature = "deflate")]
385470 deflate: false,
386471 }
387472 }
388473 */
389474
390475 pub ( super ) fn as_str ( & self ) -> Option < & ' static str > {
391- match ( self . is_gzip ( ) , self . is_brotli ( ) , self . is_deflate ( ) ) {
392- ( true , true , true ) => Some ( "gzip, br, deflate" ) ,
393- ( true , true , false ) => Some ( "gzip, br" ) ,
394- ( true , false , true ) => Some ( "gzip, deflate" ) ,
395- ( false , true , true ) => Some ( "br, deflate" ) ,
396- ( true , false , false ) => Some ( "gzip" ) ,
397- ( false , true , false ) => Some ( "br" ) ,
398- ( false , false , true ) => Some ( "deflate" ) ,
399- ( false , false , false ) => None ,
476+ match (
477+ self . is_gzip ( ) ,
478+ self . is_brotli ( ) ,
479+ self . is_zstd ( ) ,
480+ self . is_deflate ( ) ,
481+ ) {
482+ ( true , true , true , true ) => Some ( "gzip, br, zstd, deflate" ) ,
483+ ( true , true , false , true ) => Some ( "gzip, br, deflate" ) ,
484+ ( true , true , true , false ) => Some ( "gzip, br, zstd" ) ,
485+ ( true , true , false , false ) => Some ( "gzip, br" ) ,
486+ ( true , false , true , true ) => Some ( "gzip, zstd, deflate" ) ,
487+ ( true , false , false , true ) => Some ( "gzip, zstd, deflate" ) ,
488+ ( false , true , true , true ) => Some ( "br, zstd, deflate" ) ,
489+ ( false , true , false , true ) => Some ( "br, zstd, deflate" ) ,
490+ ( true , false , true , false ) => Some ( "gzip, zstd" ) ,
491+ ( true , false , false , false ) => Some ( "gzip" ) ,
492+ ( false , true , true , false ) => Some ( "br, zstd" ) ,
493+ ( false , true , false , false ) => Some ( "br" ) ,
494+ ( false , false , true , true ) => Some ( "zstd, deflate" ) ,
495+ ( false , false , true , false ) => Some ( "zstd" ) ,
496+ ( false , false , false , true ) => Some ( "deflate" ) ,
497+ ( false , false , false , false ) => None ,
400498 }
401499 }
402500
@@ -424,6 +522,18 @@ impl Accepts {
424522 }
425523 }
426524
525+ fn is_zstd ( & self ) -> bool {
526+ #[ cfg( feature = "zstd" ) ]
527+ {
528+ self . zstd
529+ }
530+
531+ #[ cfg( not( feature = "zstd" ) ) ]
532+ {
533+ false
534+ }
535+ }
536+
427537 fn is_deflate ( & self ) -> bool {
428538 #[ cfg( feature = "deflate" ) ]
429539 {
@@ -444,6 +554,8 @@ impl Default for Accepts {
444554 gzip : true ,
445555 #[ cfg( feature = "brotli" ) ]
446556 brotli : true ,
557+ #[ cfg( feature = "zstd" ) ]
558+ zstd : true ,
447559 #[ cfg( feature = "deflate" ) ]
448560 deflate : true ,
449561 }
0 commit comments