@@ -9,6 +9,9 @@ use async_compression::tokio::bufread::GzipDecoder;
9
9
#[ cfg( feature = "brotli" ) ]
10
10
use async_compression:: tokio:: bufread:: BrotliDecoder ;
11
11
12
+ #[ cfg( feature = "zstd" ) ]
13
+ use async_compression:: tokio:: bufread:: ZstdDecoder ;
14
+
12
15
#[ cfg( feature = "deflate" ) ]
13
16
use async_compression:: tokio:: bufread:: ZlibDecoder ;
14
17
@@ -19,9 +22,19 @@ use http::HeaderMap;
19
22
use hyper:: body:: Body as HttpBody ;
20
23
use hyper:: body:: Frame ;
21
24
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
+ ) ) ]
23
31
use 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
+ ) ) ]
25
38
use tokio_util:: io:: StreamReader ;
26
39
27
40
use super :: body:: ResponseBody ;
@@ -33,6 +46,8 @@ pub(super) struct Accepts {
33
46
pub ( super ) gzip : bool ,
34
47
#[ cfg( feature = "brotli" ) ]
35
48
pub ( super ) brotli : bool ,
49
+ #[ cfg( feature = "zstd" ) ]
50
+ pub ( super ) zstd : bool ,
36
51
#[ cfg( feature = "deflate" ) ]
37
52
pub ( super ) deflate : bool ,
38
53
}
@@ -44,6 +59,8 @@ impl Accepts {
44
59
gzip : false ,
45
60
#[ cfg( feature = "brotli" ) ]
46
61
brotli : false ,
62
+ #[ cfg( feature = "zstd" ) ]
63
+ zstd : false ,
47
64
#[ cfg( feature = "deflate" ) ]
48
65
deflate : false ,
49
66
}
@@ -59,7 +76,12 @@ pub(crate) struct Decoder {
59
76
60
77
type PeekableIoStream = Peekable < IoStream > ;
61
78
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
+ ) ) ]
63
85
type PeekableIoStreamReader = StreamReader < PeekableIoStream , Bytes > ;
64
86
65
87
enum Inner {
@@ -74,12 +96,21 @@ enum Inner {
74
96
#[ cfg( feature = "brotli" ) ]
75
97
Brotli ( Pin < Box < FramedRead < BrotliDecoder < PeekableIoStreamReader > , BytesCodec > > > ) ,
76
98
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
+
77
103
/// A `Deflate` decoder will uncompress the deflated response content before returning it.
78
104
#[ cfg( feature = "deflate" ) ]
79
105
Deflate ( Pin < Box < FramedRead < ZlibDecoder < PeekableIoStreamReader > , BytesCodec > > > ) ,
80
106
81
107
/// 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
+ ) ) ]
83
114
Pending ( Pin < Box < Pending > > ) ,
84
115
}
85
116
@@ -93,6 +124,8 @@ enum DecoderType {
93
124
Gzip ,
94
125
#[ cfg( feature = "brotli" ) ]
95
126
Brotli ,
127
+ #[ cfg( feature = "zstd" ) ]
128
+ Zstd ,
96
129
#[ cfg( feature = "deflate" ) ]
97
130
Deflate ,
98
131
}
@@ -155,6 +188,21 @@ impl Decoder {
155
188
}
156
189
}
157
190
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
+
158
206
/// A deflate decoder.
159
207
///
160
208
/// This decoder will buffer and decompress chunks that are deflated.
@@ -170,7 +218,12 @@ impl Decoder {
170
218
}
171
219
}
172
220
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
+ ) ) ]
174
227
fn detect_encoding ( headers : & mut HeaderMap , encoding_str : & str ) -> bool {
175
228
use http:: header:: { CONTENT_ENCODING , CONTENT_LENGTH , TRANSFER_ENCODING } ;
176
229
use log:: warn;
@@ -225,6 +278,13 @@ impl Decoder {
225
278
}
226
279
}
227
280
281
+ #[ cfg( feature = "zstd" ) ]
282
+ {
283
+ if _accepts. zstd && Decoder :: detect_encoding ( _headers, "zstd" ) {
284
+ return Decoder :: zstd ( body) ;
285
+ }
286
+ }
287
+
228
288
#[ cfg( feature = "deflate" ) ]
229
289
{
230
290
if _accepts. deflate && Decoder :: detect_encoding ( _headers, "deflate" ) {
@@ -245,7 +305,12 @@ impl HttpBody for Decoder {
245
305
cx : & mut Context ,
246
306
) -> Poll < Option < Result < Frame < Self :: Data > , Self :: Error > > > {
247
307
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
+ ) ) ]
249
314
Inner :: Pending ( ref mut future) => match Pin :: new ( future) . poll ( cx) {
250
315
Poll :: Ready ( Ok ( inner) ) => {
251
316
self . inner = inner;
@@ -277,6 +342,14 @@ impl HttpBody for Decoder {
277
342
None => Poll :: Ready ( None ) ,
278
343
}
279
344
}
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
+ }
280
353
#[ cfg( feature = "deflate" ) ]
281
354
Inner :: Deflate ( ref mut decoder) => {
282
355
match futures_core:: ready!( Pin :: new( decoder) . poll_next( cx) ) {
@@ -292,7 +365,12 @@ impl HttpBody for Decoder {
292
365
match self . inner {
293
366
Inner :: PlainText ( ref body) => HttpBody :: size_hint ( body) ,
294
367
// 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
+ ) ) ]
296
374
_ => http_body:: SizeHint :: default ( ) ,
297
375
}
298
376
}
@@ -332,6 +410,11 @@ impl Future for Pending {
332
410
BrotliDecoder :: new ( StreamReader :: new ( _body) ) ,
333
411
BytesCodec :: new ( ) ,
334
412
) ) ) ) ) ,
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
+ ) ) ) ) ) ,
335
418
#[ cfg( feature = "gzip" ) ]
336
419
DecoderType :: Gzip => Poll :: Ready ( Ok ( Inner :: Gzip ( Box :: pin ( FramedRead :: new (
337
420
GzipDecoder :: new ( StreamReader :: new ( _body) ) ,
@@ -381,22 +464,37 @@ impl Accepts {
381
464
gzip: false,
382
465
#[cfg(feature = "brotli")]
383
466
brotli: false,
467
+ #[cfg(feature = "zstd")]
468
+ zstd: false,
384
469
#[cfg(feature = "deflate")]
385
470
deflate: false,
386
471
}
387
472
}
388
473
*/
389
474
390
475
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 ,
400
498
}
401
499
}
402
500
@@ -424,6 +522,18 @@ impl Accepts {
424
522
}
425
523
}
426
524
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
+
427
537
fn is_deflate ( & self ) -> bool {
428
538
#[ cfg( feature = "deflate" ) ]
429
539
{
@@ -444,6 +554,8 @@ impl Default for Accepts {
444
554
gzip : true ,
445
555
#[ cfg( feature = "brotli" ) ]
446
556
brotli : true ,
557
+ #[ cfg( feature = "zstd" ) ]
558
+ zstd : true ,
447
559
#[ cfg( feature = "deflate" ) ]
448
560
deflate : true ,
449
561
}
0 commit comments