Skip to content

Commit bb750a2

Browse files
committed
fix: accept an empty gzip as a valid input
1 parent 4d41947 commit bb750a2

File tree

4 files changed

+55
-11
lines changed

4 files changed

+55
-11
lines changed

src/codec/gzip/decoder.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use crate::{
55
},
66
util::PartialBuffer,
77
};
8-
use std::io::{Error, ErrorKind, Result};
9-
108
use flate2::Crc;
9+
use std::io::{Error, ErrorKind, Read, Result};
10+
use std::ops::Deref;
1111

1212
#[derive(Debug)]
1313
enum State {
@@ -163,16 +163,22 @@ impl Decode for GzipDecoder {
163163

164164
fn finish(
165165
&mut self,
166-
_output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
166+
output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
167167
) -> Result<bool> {
168-
// Because of the footer we have to have already flushed all the data out before we get here
169-
if let State::Done = self.state {
170-
Ok(true)
171-
} else {
172-
Err(Error::new(
173-
ErrorKind::UnexpectedEof,
174-
"unexpected end of file",
175-
))
168+
match &mut self.state {
169+
State::Done => return Ok(true),
170+
State::Header(parser) => {
171+
// In this case, the input was an empty gzip. Exit successfully with an empty gzip.
172+
if parser.has_no_content() {
173+
return Ok(true);
174+
}
175+
}
176+
_ => {}
176177
}
178+
179+
Err(Error::new(
180+
ErrorKind::UnexpectedEof,
181+
"unexpected end of file",
182+
))
177183
}
178184
}

src/codec/gzip/header.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,11 @@ impl Parser {
161161
};
162162
}
163163
}
164+
165+
pub(super) fn has_no_content(&self) -> bool {
166+
match &self.state {
167+
State::Fixed(data) => data.buffer().iter().all(|&b| b == 0),
168+
_ => false,
169+
}
170+
}
164171
}

src/util.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ impl<B: AsRef<[u8]>> PartialBuffer<B> {
2020
&self.buffer.as_ref()[self.index..]
2121
}
2222

23+
pub(crate) fn buffer(&self) -> &[u8] {
24+
self.buffer.as_ref()
25+
}
26+
2327
pub(crate) fn advance(&mut self, amount: usize) {
2428
self.index += amount;
2529
}

tests/gzip.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ test_cases!(gzip);
55

66
#[allow(unused)]
77
use utils::{algos::gzip::sync, InputStream};
8+
#[allow(unused)]
9+
use ntest::assert_true;
810

911
#[cfg(feature = "futures-io")]
1012
use utils::algos::gzip::futures::bufread;
@@ -51,3 +53,28 @@ fn gzip_bufread_chunks_decompress_with_extra_header() {
5153

5254
assert_eq!(output, &[1, 2, 3, 4, 5, 6][..]);
5355
}
56+
57+
#[test]
58+
#[ntest::timeout(1000)]
59+
#[cfg(feature = "futures-io")]
60+
fn gzip_empty() {
61+
let bytes = Vec::new();
62+
63+
let input = InputStream::from(bytes.chunks(2));
64+
let output = bufread::decompress(bufread::from(&input));
65+
66+
assert_eq!(output, &[][..]);
67+
}
68+
69+
#[test]
70+
#[ntest::timeout(1000)]
71+
#[cfg(feature = "futures-io")]
72+
fn invalid_gzip() {
73+
let bytes = [0, 0, 0, 1, 0, 0];
74+
75+
let input = InputStream::from(bytes.chunks(2));
76+
77+
let result = std::panic::catch_unwind(|| bufread::decompress(bufread::from(&input)));
78+
79+
assert_true!(result.is_err());
80+
}

0 commit comments

Comments
 (0)