Skip to content

Commit dedb17b

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

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ test_cases!(gzip);
66
#[allow(unused)]
77
use utils::{algos::gzip::sync, InputStream};
88

9+
#[allow(unused)]
10+
use ntest::assert_true;
11+
912
#[cfg(feature = "futures-io")]
1013
use utils::algos::gzip::futures::bufread;
1114

@@ -51,3 +54,28 @@ fn gzip_bufread_chunks_decompress_with_extra_header() {
5154

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

0 commit comments

Comments
 (0)