#include "extensions/compression/gzip/decompressor/zlib_decompressor_impl.h" #include #include #include "envoy/common/exception.h" #include "common/common/assert.h" #include "absl/container/fixed_array.h" namespace Envoy { namespace Extensions { namespace Compression { namespace Gzip { namespace Decompressor { ZlibDecompressorImpl::ZlibDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix) : ZlibDecompressorImpl(scope, stats_prefix, 4096) {} ZlibDecompressorImpl::ZlibDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix, uint64_t chunk_size) : Zlib::Base(chunk_size, [](z_stream* z) { inflateEnd(z); delete z; }), stats_(generateStats(stats_prefix, scope)) { zstream_ptr_->zalloc = Z_NULL; zstream_ptr_->zfree = Z_NULL; zstream_ptr_->opaque = Z_NULL; zstream_ptr_->avail_out = chunk_size_; zstream_ptr_->next_out = chunk_char_ptr_.get(); } void ZlibDecompressorImpl::init(int64_t window_bits) { ASSERT(initialized_ == false); const int result = inflateInit2(zstream_ptr_.get(), window_bits); RELEASE_ASSERT(result >= 0, ""); initialized_ = true; } void ZlibDecompressorImpl::decompress(const Buffer::Instance& input_buffer, Buffer::Instance& output_buffer) { for (const Buffer::RawSlice& input_slice : input_buffer.getRawSlices()) { zstream_ptr_->avail_in = input_slice.len_; zstream_ptr_->next_in = static_cast(input_slice.mem_); while (inflateNext()) { if (zstream_ptr_->avail_out == 0) { updateOutput(output_buffer); } } } // Flush z_stream and reset its buffer. Otherwise the stale content of the buffer // will pollute output upon the next call to decompress(). updateOutput(output_buffer); } bool ZlibDecompressorImpl::inflateNext() { const int result = inflate(zstream_ptr_.get(), Z_NO_FLUSH); if (result == Z_STREAM_END) { // Z_FINISH informs inflate to not maintain a sliding window if the stream completes, which // reduces inflate's memory footprint. Ref: https://www.zlib.net/manual.html. inflate(zstream_ptr_.get(), Z_FINISH); return false; } if (result == Z_BUF_ERROR && zstream_ptr_->avail_in == 0) { return false; // This means that zlib needs more input, so stop here. } if (result < 0) { decompression_error_ = result; ENVOY_LOG(trace, "zlib decompression error: {}, msg: {}. Error codes are defined in " "https://www.zlib.net/manual.html", result, zstream_ptr_->msg); chargeErrorStats(result); return false; } return true; } void ZlibDecompressorImpl::chargeErrorStats(const int result) { switch (result) { case Z_ERRNO: stats_.zlib_errno_.inc(); break; case Z_STREAM_ERROR: stats_.zlib_stream_error_.inc(); break; case Z_DATA_ERROR: stats_.zlib_data_error_.inc(); break; case Z_MEM_ERROR: stats_.zlib_mem_error_.inc(); break; case Z_BUF_ERROR: stats_.zlib_buf_error_.inc(); break; case Z_VERSION_ERROR: stats_.zlib_version_error_.inc(); break; } } } // namespace Decompressor } // namespace Gzip } // namespace Compression } // namespace Extensions } // namespace Envoy