Skip to content

Commit d8118bc

Browse files
authored
fix(decoder): fix decoding extra empty frame (#234)
from upstream: seanmonstar/reqwest#2508
1 parent 0de340c commit d8118bc

File tree

1 file changed

+100
-28
lines changed

1 file changed

+100
-28
lines changed

src/client/decoder.rs

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ use std::future::Future;
99
use std::pin::Pin;
1010
use std::task::{Context, Poll};
1111

12+
#[cfg(any(
13+
feature = "gzip",
14+
feature = "zstd",
15+
feature = "brotli",
16+
feature = "deflate"
17+
))]
18+
use futures_util::stream::Fuse;
19+
1220
#[cfg(feature = "gzip")]
1321
use async_compression::tokio::bufread::GzipDecoder;
1422

@@ -92,19 +100,19 @@ enum Inner {
92100

93101
/// A `Gzip` decoder will uncompress the gzipped response content before returning it.
94102
#[cfg(feature = "gzip")]
95-
Gzip(Pin<Box<FramedRead<GzipDecoder<PeekableIoStreamReader>, BytesCodec>>>),
103+
Gzip(Pin<Box<Fuse<FramedRead<GzipDecoder<PeekableIoStreamReader>, BytesCodec>>>>),
96104

97105
/// A `Brotli` decoder will uncompress the brotlied response content before returning it.
98106
#[cfg(feature = "brotli")]
99-
Brotli(Pin<Box<FramedRead<BrotliDecoder<PeekableIoStreamReader>, BytesCodec>>>),
107+
Brotli(Pin<Box<Fuse<FramedRead<BrotliDecoder<PeekableIoStreamReader>, BytesCodec>>>>),
100108

101109
/// A `Zstd` decoder will uncompress the zstd compressed response content before returning it.
102110
#[cfg(feature = "zstd")]
103-
Zstd(Pin<Box<FramedRead<ZstdDecoder<PeekableIoStreamReader>, BytesCodec>>>),
111+
Zstd(Pin<Box<Fuse<FramedRead<ZstdDecoder<PeekableIoStreamReader>, BytesCodec>>>>),
104112

105113
/// A `Deflate` decoder will uncompress the deflated response content before returning it.
106114
#[cfg(feature = "deflate")]
107-
Deflate(Pin<Box<FramedRead<ZlibDecoder<PeekableIoStreamReader>, BytesCodec>>>),
115+
Deflate(Pin<Box<Fuse<FramedRead<ZlibDecoder<PeekableIoStreamReader>, BytesCodec>>>>),
108116

109117
/// A decoder that doesn't have a value yet.
110118
#[cfg(any(
@@ -336,34 +344,58 @@ impl HttpBody for Decoder {
336344
}
337345
#[cfg(feature = "gzip")]
338346
Inner::Gzip(ref mut decoder) => {
339-
match futures_core::ready!(Pin::new(decoder).poll_next(cx)) {
347+
match futures_core::ready!(Pin::new(&mut *decoder).poll_next(cx)) {
340348
Some(Ok(bytes)) => Poll::Ready(Some(Ok(Frame::data(bytes.freeze())))),
341349
Some(Err(err)) => Poll::Ready(Some(Err(crate::error::decode_io(err)))),
342-
None => Poll::Ready(None),
350+
None => {
351+
// poll inner connection until EOF after gzip stream is finished
352+
poll_inner_should_be_empty(
353+
decoder.get_mut().get_mut().get_mut().get_mut(),
354+
cx,
355+
)
356+
}
343357
}
344358
}
345359
#[cfg(feature = "brotli")]
346360
Inner::Brotli(ref mut decoder) => {
347-
match futures_core::ready!(Pin::new(decoder).poll_next(cx)) {
361+
match futures_core::ready!(Pin::new(&mut *decoder).poll_next(cx)) {
348362
Some(Ok(bytes)) => Poll::Ready(Some(Ok(Frame::data(bytes.freeze())))),
349363
Some(Err(err)) => Poll::Ready(Some(Err(crate::error::decode_io(err)))),
350-
None => Poll::Ready(None),
364+
None => {
365+
// poll inner connection until EOF after brotli stream is finished
366+
poll_inner_should_be_empty(
367+
decoder.get_mut().get_mut().get_mut().get_mut(),
368+
cx,
369+
)
370+
}
351371
}
352372
}
353373
#[cfg(feature = "zstd")]
354374
Inner::Zstd(ref mut decoder) => {
355-
match futures_core::ready!(Pin::new(decoder).poll_next(cx)) {
375+
match futures_core::ready!(Pin::new(&mut *decoder).poll_next(cx)) {
356376
Some(Ok(bytes)) => Poll::Ready(Some(Ok(Frame::data(bytes.freeze())))),
357377
Some(Err(err)) => Poll::Ready(Some(Err(crate::error::decode_io(err)))),
358-
None => Poll::Ready(None),
378+
None => {
379+
// poll inner connection until EOF after zstd stream is finished
380+
poll_inner_should_be_empty(
381+
decoder.get_mut().get_mut().get_mut().get_mut(),
382+
cx,
383+
)
384+
}
359385
}
360386
}
361387
#[cfg(feature = "deflate")]
362388
Inner::Deflate(ref mut decoder) => {
363-
match futures_core::ready!(Pin::new(decoder).poll_next(cx)) {
389+
match futures_core::ready!(Pin::new(&mut *decoder).poll_next(cx)) {
364390
Some(Ok(bytes)) => Poll::Ready(Some(Ok(Frame::data(bytes.freeze())))),
365391
Some(Err(err)) => Poll::Ready(Some(Err(crate::error::decode_io(err)))),
366-
None => Poll::Ready(None),
392+
None => {
393+
// poll inner connection until EOF after deflate stream is finished
394+
poll_inner_should_be_empty(
395+
decoder.get_mut().get_mut().get_mut().get_mut(),
396+
cx,
397+
)
398+
}
367399
}
368400
}
369401
}
@@ -384,6 +416,34 @@ impl HttpBody for Decoder {
384416
}
385417
}
386418

419+
#[cfg(any(
420+
feature = "gzip",
421+
feature = "zstd",
422+
feature = "brotli",
423+
feature = "deflate"
424+
))]
425+
fn poll_inner_should_be_empty(
426+
inner: &mut PeekableIoStream,
427+
cx: &mut Context,
428+
) -> Poll<Option<Result<Frame<Bytes>, crate::Error>>> {
429+
// poll inner connection until EOF after deflate stream is finished
430+
// loop in case of empty frames
431+
let mut inner = Pin::new(inner);
432+
loop {
433+
match futures_core::ready!(inner.as_mut().poll_next(cx)) {
434+
// ignore any empty frames
435+
Some(Ok(bytes)) if bytes.is_empty() => continue,
436+
Some(Ok(_)) => {
437+
return Poll::Ready(Some(Err(crate::error::decode(
438+
"there are extra bytes after body has been decompressed",
439+
))))
440+
}
441+
Some(Err(err)) => return Poll::Ready(Some(Err(crate::error::decode_io(err)))),
442+
None => return Poll::Ready(None),
443+
}
444+
}
445+
}
446+
387447
#[cfg(any(
388448
feature = "gzip",
389449
feature = "zstd",
@@ -426,25 +486,37 @@ impl Future for Pending {
426486

427487
match self.1 {
428488
#[cfg(feature = "brotli")]
429-
DecoderType::Brotli => Poll::Ready(Ok(Inner::Brotli(Box::pin(FramedRead::new(
430-
BrotliDecoder::new(StreamReader::new(_body)),
431-
BytesCodec::new(),
432-
))))),
489+
DecoderType::Brotli => Poll::Ready(Ok(Inner::Brotli(Box::pin(
490+
FramedRead::new(
491+
BrotliDecoder::new(StreamReader::new(_body)),
492+
BytesCodec::new(),
493+
)
494+
.fuse(),
495+
)))),
433496
#[cfg(feature = "zstd")]
434-
DecoderType::Zstd => Poll::Ready(Ok(Inner::Zstd(Box::pin(FramedRead::new(
435-
ZstdDecoder::new(StreamReader::new(_body)),
436-
BytesCodec::new(),
437-
))))),
497+
DecoderType::Zstd => Poll::Ready(Ok(Inner::Zstd(Box::pin(
498+
FramedRead::new(
499+
ZstdDecoder::new(StreamReader::new(_body)),
500+
BytesCodec::new(),
501+
)
502+
.fuse(),
503+
)))),
438504
#[cfg(feature = "gzip")]
439-
DecoderType::Gzip => Poll::Ready(Ok(Inner::Gzip(Box::pin(FramedRead::new(
440-
GzipDecoder::new(StreamReader::new(_body)),
441-
BytesCodec::new(),
442-
))))),
505+
DecoderType::Gzip => Poll::Ready(Ok(Inner::Gzip(Box::pin(
506+
FramedRead::new(
507+
GzipDecoder::new(StreamReader::new(_body)),
508+
BytesCodec::new(),
509+
)
510+
.fuse(),
511+
)))),
443512
#[cfg(feature = "deflate")]
444-
DecoderType::Deflate => Poll::Ready(Ok(Inner::Deflate(Box::pin(FramedRead::new(
445-
ZlibDecoder::new(StreamReader::new(_body)),
446-
BytesCodec::new(),
447-
))))),
513+
DecoderType::Deflate => Poll::Ready(Ok(Inner::Deflate(Box::pin(
514+
FramedRead::new(
515+
ZlibDecoder::new(StreamReader::new(_body)),
516+
BytesCodec::new(),
517+
)
518+
.fuse(),
519+
)))),
448520
}
449521
}
450522
}

0 commit comments

Comments
 (0)