From 2a85539ef08dd57c4d794de2bbcf30572a45bf1c Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 7 Sep 2026 10:41:22 +0200 Subject: [PATCH] http1: use brotli with owned BrotliState wrapper Ticket: 9008 Will allow to use strict brotli, and forbid large window --- rust/htp/src/decompressors.rs | 75 +++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/rust/htp/src/decompressors.rs b/rust/htp/src/decompressors.rs index 9a395c6e58..8b4a5fedf9 100644 --- a/rust/htp/src/decompressors.rs +++ b/rust/htp/src/decompressors.rs @@ -1,4 +1,4 @@ -use brotli; +use brotli::{BrotliDecompressStream, BrotliResult, BrotliState, HeapAlloc, HuffmanCode}; use nom::Parser as _; use std::{ io::{Cursor, Write}, @@ -736,27 +736,76 @@ impl BufWriter for LzmaBufWriter { } /// Simple wrapper around an lzma implementation -struct BrotliBufWriter(brotli::DecompressorWriter); +struct BrotliBufWriter { + state: BrotliState, HeapAlloc, HeapAlloc>, + cursor: BlockingCursor, +} + +impl BrotliBufWriter { + pub fn new(cursor: BlockingCursor) -> BrotliBufWriter { + BrotliBufWriter { + state: BrotliState::new( + HeapAlloc::::new(0), + HeapAlloc::::new(0), + HeapAlloc::::new(HuffmanCode { bits: 0, value: 0 }), + ), + cursor, + } + } +} impl Write for BrotliBufWriter { - fn write(&mut self, data: &[u8]) -> std::io::Result { - self.0.write(data) + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let start_offset = 0; + let mut input_offset = 0; + let mut available_in = buf.len() - input_offset; + let mut output_offset = self.cursor.cursor.position() as usize; + let mut available_out = self.cursor.cursor.get_ref().len() - output_offset; + let mut written = 0; + match BrotliDecompressStream( + &mut available_in, + &mut input_offset, + buf, + &mut available_out, + &mut output_offset, + self.cursor.cursor.get_mut(), + &mut written, + &mut self.state, + ) { + BrotliResult::ResultSuccess => { + self.cursor.cursor.set_position(output_offset as u64); + Ok(input_offset - start_offset) + } + BrotliResult::NeedsMoreInput => { + self.cursor.cursor.set_position(output_offset as u64); + Ok(input_offset - start_offset) + } + BrotliResult::NeedsMoreOutput => { + self.cursor.cursor.set_position(output_offset as u64); + if input_offset == start_offset { + return Err(std::io::ErrorKind::WriteZero.into()); + } + Ok(input_offset - start_offset) + } + BrotliResult::ResultFailure => { + Err(std::io::Error::other("Brotli decompression failed")) + } + } } fn flush(&mut self) -> std::io::Result<()> { - self.0.flush() + self.write(&[])?; + Ok(()) } } impl BufWriter for BrotliBufWriter { fn get_mut(&mut self) -> Option<&mut BlockingCursor> { - Some(self.0.get_mut()) + Some(&mut self.cursor) } fn finish(self: Box) -> std::io::Result { - self.0 - .into_inner() - .map_err(|_e| std::io::Error::other("brotli")) + Ok(self.cursor) } fn try_finish(&mut self) -> std::io::Result<()> { @@ -797,13 +846,7 @@ impl InnerDecompressor { Box::new(ZlibBufWriter(flate2::write::ZlibDecoder::new(buf))), false, )), - HtpContentEncoding::Brotli => Ok(( - Box::new(BrotliBufWriter(brotli::DecompressorWriter::new( - buf, - ENCODING_CHUNK_SIZE, - ))), - false, - )), + HtpContentEncoding::Brotli => Ok((Box::new(BrotliBufWriter::new(buf)), false)), HtpContentEncoding::Lzma => { if let Some(options) = options.lzma { Ok((