Skip to content

Commit e67f0b1

Browse files
authored
Fix decoding of empty response streams. (#444)
Previously we were returning "zstd stream did not finish", but now we notice we got no data and transition directly to the done state.
1 parent f5e532e commit e67f0b1

File tree

3 files changed

+34
-1
lines changed

3 files changed

+34
-1
lines changed

crates/async-compression/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ required-features = ["zlib", "tokio"]
139139
name = "zstd_gzip"
140140
required-features = ["zstd", "gzip", "tokio"]
141141

142+
[[test]]
143+
name = "empty_stream"
144+
required-features = ["zstd", "tokio"]
145+
142146
[[example]]
143147
name = "lzma_filters"
144148
required-features = ["xz", "tokio"]

crates/async-compression/src/generic/bufread/decoder.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ enum State {
1717
pub struct Decoder {
1818
state: State,
1919
multiple_members: bool,
20+
received_data: bool,
2021
}
2122

2223
impl Default for Decoder {
2324
fn default() -> Self {
2425
Self {
2526
state: State::Decoding,
2627
multiple_members: false,
28+
received_data: false,
2729
}
2830
}
2931
}
@@ -48,8 +50,16 @@ impl Decoder {
4850
// reader has returned EOF.
4951
self.multiple_members = false;
5052

51-
State::Flushing
53+
// Empty stream (no data received) - return empty output
54+
if !self.received_data {
55+
State::Done
56+
} else {
57+
State::Flushing
58+
}
5259
} else {
60+
if !input.unwritten().is_empty() {
61+
self.received_data = true;
62+
}
5363
match decoder.decode(input, output) {
5464
Ok(true) => State::Flushing,
5565
// ignore the first error, occurs when input is empty
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Test that bufread decoders handle empty input streams (immediate EOF).
2+
3+
#[macro_use]
4+
mod utils;
5+
6+
#[tokio::test]
7+
async fn zstd_empty_stream() {
8+
use async_compression::tokio::bufread::ZstdDecoder;
9+
use std::io::Cursor;
10+
use tokio::io::AsyncReadExt;
11+
12+
let empty: &[u8] = &[];
13+
let mut decoder = ZstdDecoder::new(Cursor::new(empty));
14+
let mut output = Vec::new();
15+
let result = decoder.read_to_end(&mut output).await;
16+
// Empty input should return Ok(0), not error with "zstd stream did not finish"
17+
assert!(result.is_ok(), "empty stream failed: {:?}", result);
18+
assert!(output.is_empty());
19+
}

0 commit comments

Comments
 (0)