Skip to content

Commit 8fde492

Browse files
committed
On error the decoder now emits everything it was able to decode.
Before this we would return the error and whatever we had in our decoded buffer would be swallowed. Fixes #453.
1 parent 0370b47 commit 8fde492

File tree

2 files changed

+51
-4
lines changed

2 files changed

+51
-4
lines changed

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::{
22
codecs::DecodeV2,
33
core::util::{PartialBuffer, WriteBuffer},
44
};
5-
65
use std::{io::Result, ops::ControlFlow};
76

87
#[derive(Debug)]
@@ -11,6 +10,7 @@ enum State {
1110
Flushing,
1211
Done,
1312
Next,
13+
Error(std::io::Error),
1414
}
1515

1616
#[derive(Debug)]
@@ -54,7 +54,14 @@ impl Decoder {
5454
Ok(true) => State::Flushing,
5555
// ignore the first error, occurs when input is empty
5656
// but we need to run decode to flush
57-
Err(err) if !first => return ControlFlow::Break(Err(err)),
57+
Err(err) if !first => {
58+
self.state = State::Error(err);
59+
if output.written_len() > 0 {
60+
return ControlFlow::Break(Ok(()));
61+
} else {
62+
continue;
63+
}
64+
}
5865
// poll for more data for the next decode
5966
_ => break,
6067
}
@@ -66,7 +73,12 @@ impl Decoder {
6673
Ok(true) => {
6774
if self.multiple_members {
6875
if let Err(err) = decoder.reinit() {
69-
return ControlFlow::Break(Err(err));
76+
self.state = State::Error(err);
77+
if output.written_len() > 0 {
78+
return ControlFlow::Break(Ok(()));
79+
} else {
80+
continue;
81+
}
7082
}
7183

7284
// The decode stage might consume all the input,
@@ -78,7 +90,14 @@ impl Decoder {
7890
}
7991
}
8092
Ok(false) => State::Flushing,
81-
Err(err) => return ControlFlow::Break(Err(err)),
93+
Err(err) => {
94+
self.state = State::Error(err);
95+
if output.written_len() > 0 {
96+
return ControlFlow::Break(Ok(()));
97+
} else {
98+
continue;
99+
}
100+
}
82101
}
83102
}
84103

@@ -95,6 +114,13 @@ impl Decoder {
95114
State::Decoding
96115
}
97116
}
117+
118+
State::Error(_) => {
119+
let State::Error(err) = std::mem::replace(&mut self.state, State::Done) else {
120+
unreachable!()
121+
};
122+
return ControlFlow::Break(Err(err));
123+
}
98124
};
99125

100126
if output.has_no_spare_space() {

crates/async-compression/tests/gzip.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,24 @@ fn gzip_bufread_chunks_decompress_with_extra_header() {
5151

5252
assert_eq!(output, &[1, 2, 3, 4, 5, 6][..]);
5353
}
54+
55+
#[test]
56+
#[ntest::timeout(1000)]
57+
#[cfg(feature = "futures-io")]
58+
fn gzip_bufread_chunks_decompress_without_footer_emits_all_payload() {
59+
use flate2::bufread::GzDecoder;
60+
use std::io::Read;
61+
62+
let mut bytes = compress_with_header(&[1, 2, 3, 4, 5, 6]);
63+
64+
// Remove the footer.
65+
bytes.truncate(bytes.len() - 8);
66+
67+
let mut decoder = GzDecoder::new(bytes.as_slice());
68+
69+
let mut output = vec![];
70+
let result = decoder.read_to_end(&mut output);
71+
72+
assert!(result.is_err());
73+
assert_eq!(output, &[1, 2, 3, 4, 5, 6][..]);
74+
}

0 commit comments

Comments
 (0)