http1: use a blocking cursor for decompression

Kind of as is done by HTTP2 which limits input data instead of
output data

Ticket: 7732
pull/13361/head
Philippe Antoine 5 months ago committed by Victor Julien
parent 7c88d37570
commit 16fee33368

@ -199,6 +199,55 @@ pub(crate) enum HtpContentEncoding {
Brotli,
}
//a cursor turning EOF into blocking errors
#[derive(Debug)]
struct BlockingCursor {
pub cursor: Cursor<Vec<u8>>,
}
impl BlockingCursor {
fn new() -> BlockingCursor {
BlockingCursor {
cursor: Cursor::new(Vec::with_capacity(ENCODING_CHUNK_SIZE)),
}
}
pub fn set_position(&mut self, pos: u64) {
self.cursor.set_position(pos)
}
fn position(&self) -> u64 {
self.cursor.position()
}
pub fn get_ref(&self) -> &Vec<u8> {
self.cursor.get_ref()
}
}
// we need to implement this as flate2 and brotli crates
// will read from this object
impl Write for BlockingCursor {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
//use the cursor, except it turns eof into blocking error
let r = self.cursor.write(buf);
match r {
Err(ref err) => {
if err.kind() == std::io::ErrorKind::UnexpectedEof {
return Err(std::io::ErrorKind::WouldBlock.into());
}
}
Ok(0) => {
//regular EOF turned into blocking error
return Err(std::io::ErrorKind::WouldBlock.into());
}
Ok(_n) => {}
}
r
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
/// The outer decompressor tracks the number of callbacks and time spent
/// decompressing.
pub(crate) struct Decompressor {
@ -327,11 +376,11 @@ impl std::fmt::Debug for Decompressor {
/// Trait that represents the decompression writers (gzip, deflate, etc.) and
/// methods needed to write to a temporary buffer.
pub(crate) trait BufWriter: Write {
trait BufWriter: Write {
/// Get a mutable reference to the buffer.
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>>;
fn get_mut(&mut self) -> Option<&mut BlockingCursor>;
/// Notify end of data.
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>>;
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor>;
/// Attempt to finish this output stream, writing out final chunks of data.
fn try_finish(&mut self) -> std::io::Result<()>;
}
@ -339,7 +388,7 @@ pub(crate) trait BufWriter: Write {
/// A BufWriter that doesn't consume any data.
///
/// This should be used exclusively with passthrough mode.
struct NullBufWriter(Cursor<Box<[u8]>>);
struct NullBufWriter(BlockingCursor);
impl Write for NullBufWriter {
fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
@ -352,11 +401,11 @@ impl Write for NullBufWriter {
}
impl BufWriter for NullBufWriter {
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
fn get_mut(&mut self) -> Option<&mut BlockingCursor> {
Some(&mut self.0)
}
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor> {
Ok(self.0)
}
@ -388,12 +437,12 @@ struct GzipBufWriter {
buffer: Vec<u8>,
flags: u8,
xlen: u16,
inner: flate2::write::DeflateDecoder<Cursor<Box<[u8]>>>,
inner: flate2::write::DeflateDecoder<BlockingCursor>,
state: GzState,
}
impl GzipBufWriter {
fn new(buf: Cursor<Box<[u8]>>) -> Self {
fn new(buf: BlockingCursor) -> Self {
GzipBufWriter {
buffer: Vec::with_capacity(10),
flags: 0,
@ -562,11 +611,11 @@ impl Write for GzipBufWriter {
}
impl BufWriter for GzipBufWriter {
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
fn get_mut(&mut self) -> Option<&mut BlockingCursor> {
Some(self.inner.get_mut())
}
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor> {
self.inner.finish()
}
@ -576,7 +625,7 @@ impl BufWriter for GzipBufWriter {
}
/// Simple wrapper around a deflate implementation
struct DeflateBufWriter(flate2::write::DeflateDecoder<Cursor<Box<[u8]>>>);
struct DeflateBufWriter(flate2::write::DeflateDecoder<BlockingCursor>);
impl Write for DeflateBufWriter {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
@ -589,11 +638,11 @@ impl Write for DeflateBufWriter {
}
impl BufWriter for DeflateBufWriter {
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
fn get_mut(&mut self) -> Option<&mut BlockingCursor> {
Some(self.0.get_mut())
}
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor> {
self.0.finish()
}
@ -603,7 +652,7 @@ impl BufWriter for DeflateBufWriter {
}
/// Simple wrapper around a zlib implementation
struct ZlibBufWriter(flate2::write::ZlibDecoder<Cursor<Box<[u8]>>>);
struct ZlibBufWriter(flate2::write::ZlibDecoder<BlockingCursor>);
impl Write for ZlibBufWriter {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
@ -616,11 +665,11 @@ impl Write for ZlibBufWriter {
}
impl BufWriter for ZlibBufWriter {
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
fn get_mut(&mut self) -> Option<&mut BlockingCursor> {
Some(self.0.get_mut())
}
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor> {
self.0.finish()
}
@ -630,7 +679,7 @@ impl BufWriter for ZlibBufWriter {
}
/// Simple wrapper around an lzma implementation
struct LzmaBufWriter(lzma_rs::decompress::Stream<Cursor<Box<[u8]>>>);
struct LzmaBufWriter(lzma_rs::decompress::Stream<BlockingCursor>);
impl Write for LzmaBufWriter {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
@ -643,11 +692,11 @@ impl Write for LzmaBufWriter {
}
impl BufWriter for LzmaBufWriter {
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
fn get_mut(&mut self) -> Option<&mut BlockingCursor> {
self.0.get_output_mut()
}
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor> {
self.0.finish().map_err(|e| match e {
lzma_rs::error::Error::IoError(e) => e,
lzma_rs::error::Error::HeaderTooShort(e) => {
@ -665,7 +714,7 @@ impl BufWriter for LzmaBufWriter {
}
/// Simple wrapper around an lzma implementation
struct BrotliBufWriter(brotli::DecompressorWriter<Cursor<Box<[u8]>>>);
struct BrotliBufWriter(brotli::DecompressorWriter<BlockingCursor>);
impl Write for BrotliBufWriter {
fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
@ -678,11 +727,11 @@ impl Write for BrotliBufWriter {
}
impl BufWriter for BrotliBufWriter {
fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
fn get_mut(&mut self) -> Option<&mut BlockingCursor> {
Some(self.0.get_mut())
}
fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
fn finish(self: Box<Self>) -> std::io::Result<BlockingCursor> {
self.0
.into_inner()
.map_err(|_e| std::io::Error::new(std::io::ErrorKind::Other, "brotli"))
@ -714,7 +763,7 @@ impl InnerDecompressor {
fn writer(
encoding: HtpContentEncoding, options: &Options,
) -> std::io::Result<(Box<dyn BufWriter>, bool)> {
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
match encoding {
HtpContentEncoding::Gzip => Ok((Box::new(GzipBufWriter::new(buf)), false)),
@ -994,56 +1043,56 @@ impl Decompress for InnerDecompressor {
fn test_gz_header() {
// No flags or other bits
let input = b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x00";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Just CRC
let input = b"\x1f\x8b\x08\x02\x00\x00\x00\x00\x00\x00\x11\x22";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Just extra
let input = b"\x1f\x8b\x08\x04\x00\x00\x00\x00\x00\x00\x04\x00abcd";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Just filename
let input = b"\x1f\x8b\x08\x08\x00\x00\x00\x00\x00\x00variable\x00";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Just comment
let input = b"\x1f\x8b\x08\x10\x00\x00\x00\x00\x00\x00also variable\x00";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Extra and Filename
let input = b"\x1f\x8b\x08\x0c\x00\x00\x00\x00\x00\x00\x05\x00extrafilename\x00";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Extra and Comment and CRC
let input = b"\x1f\x8b\x08\x16\x00\x00\x00\x00\x00\x00\x05\x00extracomment\x00\x34\x12";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Filename and Comment
let input = b"\x1f\x8b\x08\x18\x00\x00\x00\x00\x00\x00filename\x00comment\x00";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
@ -1051,14 +1100,14 @@ fn test_gz_header() {
// Extra Filename and Comment and CRC
let input =
b"\x1f\x8b\x08\x1e\x00\x00\x00\x00\x00\x00\x05\x00extrafilename\x00comment\x00\x34\x12";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
// Too short
let input = b"\x1f\x8b\x08\x1e\x00\x00\x00\x00\x00\x00\x05\x00extrafilename\x00comment\x00\x34";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len() - 1);
assert_eq!(gzw.state, GzState::Crc);
@ -1067,7 +1116,7 @@ fn test_gz_header() {
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::AfterHeader);
let input = b"\x1f\x8b\x08\x01\x00\x00\x00\x00\x00";
let buf = Cursor::new(Box::new([0u8; ENCODING_CHUNK_SIZE]) as Box<[u8]>);
let buf = BlockingCursor::new();
let mut gzw = GzipBufWriter::new(buf);
assert_eq!(gzw.write(input).unwrap(), input.len());
assert_eq!(gzw.state, GzState::Start);

Loading…
Cancel
Save