Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,6 @@ with the following:
not_decompressed, Counter, Number of request/responses not compressed.
total_uncompressed_bytes, Counter, The total uncompressed bytes of all the request/responses that were marked for decompression.
total_compressed_bytes, Counter, The total compressed bytes of all the request/responses that were marked for decompression.

Additional stats for the decompressor library are rooted at
<stat_prefix>.decompressor.<decompressor_library.name>.<decompressor_library_stat_prefix>.decompressor_library.
2 changes: 1 addition & 1 deletion include/envoy/compression/decompressor/factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class DecompressorFactory {
public:
virtual ~DecompressorFactory() = default;

virtual DecompressorPtr createDecompressor() PURE;
virtual DecompressorPtr createDecompressor(const std::string& stats_prefix) PURE;
virtual const std::string& statsPrefix() const PURE;
// TODO(junr03): this method assumes that decompressors are used on http messages.
// A more generic method might be `hint()` which gives the user of the decompressor a hint about
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class DecompressorLibraryFactoryBase
Server::Configuration::FactoryContext& context) override {
return createDecompressorFactoryFromProtoTyped(
MessageUtil::downcastAndValidate<const ConfigProto&>(proto_config,
context.messageValidationVisitor()));
context.messageValidationVisitor()),
context);
}

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
Expand All @@ -31,7 +32,8 @@ class DecompressorLibraryFactoryBase

private:
virtual Envoy::Compression::Decompressor::DecompressorFactoryPtr
createDecompressorFactoryFromProtoTyped(const ConfigProto&) PURE;
createDecompressorFactoryFromProtoTyped(const ConfigProto& proto_config,
Server::Configuration::FactoryContext& context) PURE;

const std::string name_;
};
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/compression/gzip/decompressor/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ envoy_cc_library(
external_deps = ["zlib"],
deps = [
"//include/envoy/compression/decompressor:decompressor_interface",
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
"//source/common/buffer:buffer_lib",
"//source/common/common:assert_lib",
"//source/common/common:minimal_logger_lib",
Expand Down
15 changes: 9 additions & 6 deletions source/extensions/compression/gzip/decompressor/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,24 @@ const uint32_t GzipHeaderValue = 16;
} // namespace

GzipDecompressorFactory::GzipDecompressorFactory(
const envoy::extensions::compression::gzip::decompressor::v3::Gzip& gzip)
: window_bits_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(gzip, window_bits, DefaultWindowBits) |
const envoy::extensions::compression::gzip::decompressor::v3::Gzip& gzip, Stats::Scope& scope)
: scope_(scope),
window_bits_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(gzip, window_bits, DefaultWindowBits) |
GzipHeaderValue),
chunk_size_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(gzip, chunk_size, DefaultChunkSize)) {}

Envoy::Compression::Decompressor::DecompressorPtr GzipDecompressorFactory::createDecompressor() {
auto decompressor = std::make_unique<ZlibDecompressorImpl>(chunk_size_);
Envoy::Compression::Decompressor::DecompressorPtr
GzipDecompressorFactory::createDecompressor(const std::string& stats_prefix) {
auto decompressor = std::make_unique<ZlibDecompressorImpl>(scope_, stats_prefix, chunk_size_);
decompressor->init(window_bits_);
return decompressor;
}

Envoy::Compression::Decompressor::DecompressorFactoryPtr
GzipDecompressorLibraryFactory::createDecompressorFactoryFromProtoTyped(
const envoy::extensions::compression::gzip::decompressor::v3::Gzip& proto_config) {
return std::make_unique<GzipDecompressorFactory>(proto_config);
const envoy::extensions::compression::gzip::decompressor::v3::Gzip& proto_config,
Server::Configuration::FactoryContext& context) {
return std::make_unique<GzipDecompressorFactory>(proto_config, context.scope());
}

/**
Expand Down
10 changes: 7 additions & 3 deletions source/extensions/compression/gzip/decompressor/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,19 @@ const std::string& gzipExtensionName() {

class GzipDecompressorFactory : public Envoy::Compression::Decompressor::DecompressorFactory {
public:
GzipDecompressorFactory(const envoy::extensions::compression::gzip::decompressor::v3::Gzip& gzip);
GzipDecompressorFactory(const envoy::extensions::compression::gzip::decompressor::v3::Gzip& gzip,
Stats::Scope& scope);

// Envoy::Compression::Decompressor::DecompressorFactory
Envoy::Compression::Decompressor::DecompressorPtr createDecompressor() override;
Envoy::Compression::Decompressor::DecompressorPtr
createDecompressor(const std::string& stats_prefix) override;
const std::string& statsPrefix() const override { return gzipStatsPrefix(); }
const std::string& contentEncoding() const override {
return Http::Headers::get().ContentEncodingValues.Gzip;
}

private:
Stats::Scope& scope_;
const int32_t window_bits_;
const uint32_t chunk_size_;
};
Expand All @@ -47,7 +50,8 @@ class GzipDecompressorLibraryFactory

private:
Envoy::Compression::Decompressor::DecompressorFactoryPtr createDecompressorFactoryFromProtoTyped(
const envoy::extensions::compression::gzip::decompressor::v3::Gzip& config) override;
const envoy::extensions::compression::gzip::decompressor::v3::Gzip& proto_config,
Server::Configuration::FactoryContext& context) override;
};

DECLARE_FACTORY(GzipDecompressorLibraryFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ namespace Compression {
namespace Gzip {
namespace Decompressor {

ZlibDecompressorImpl::ZlibDecompressorImpl() : ZlibDecompressorImpl(4096) {}
ZlibDecompressorImpl::ZlibDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix)
: ZlibDecompressorImpl(scope, stats_prefix, 4096) {}

ZlibDecompressorImpl::ZlibDecompressorImpl(uint64_t chunk_size)
: Zlib::Base(chunk_size, [](z_stream* z) {
inflateEnd(z);
delete z;
}) {
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;
Expand Down Expand Up @@ -67,10 +71,11 @@ bool ZlibDecompressorImpl::inflateNext() {

if (result < 0) {
decompression_error_ = result;
ENVOY_LOG(
trace,
"zlib decompression error: {}. Error codes are defined in https://www.zlib.net/manual.html",
result);
ENVOY_LOG(trace,
"zlib decompression error: {}, msg: {}. Error codes are defined in "
"https://www.zlib.net/manual.html",
result, zstream_ptr_->msg);
stats_.decompression_error_.inc();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it enough for your use case not to differentiate errors by their type?

I'm thinking that it might be useful to check the stats and see what's wrong immediately (e.g. is it Z_DATA_ERROR or Z_MEM_ERROR?) instead of increasing log verbosity to trace.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that even a Z_DATA_ERROR can have dozens of different reasons for occurring, so you would still need to look at logs to get at the heart of it. However, I do think your idea of differentiating based on returned error code is good. I'll make the change.

return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "envoy/compression/decompressor/decompressor.h"
#include "envoy/stats/scope.h"
#include "envoy/stats/stats_macros.h"

#include "common/common/logger.h"

Expand All @@ -14,14 +16,26 @@ namespace Compression {
namespace Gzip {
namespace Decompressor {

/**
* All zlib decompressor stats. @see stats_macros.h
*/
#define ALL_ZLIB_DECOMPRESSOR_STATS(COUNTER) COUNTER(decompression_error)

/**
* Struct definition for zlib decompressor stats. @see stats_macros.h
*/
struct ZlibDecompressorStats {
ALL_ZLIB_DECOMPRESSOR_STATS(GENERATE_COUNTER_STRUCT)
};

/**
* Implementation of decompressor's interface.
*/
class ZlibDecompressorImpl : public Zlib::Base,
public Envoy::Compression::Decompressor::Decompressor,
public Logger::Loggable<Logger::Id::decompression> {
public:
ZlibDecompressorImpl();
ZlibDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix);

/**
* Constructor that allows setting the size of decompressor's output buffer. It
Expand All @@ -31,7 +45,7 @@ class ZlibDecompressorImpl : public Zlib::Base,
* 256K bytes. @see http://zlib.net/zlib_how.html
* @param chunk_size amount of memory reserved for the decompressor output.
*/
ZlibDecompressorImpl(uint64_t chunk_size);
ZlibDecompressorImpl(Stats::Scope& scope, const std::string& stats_prefix, uint64_t chunk_size);

/**
* Init must be called in order to initialize the decompressor. Once decompressor is initialized,
Expand All @@ -49,7 +63,13 @@ class ZlibDecompressorImpl : public Zlib::Base,
int decompression_error_{0};

private:
static ZlibDecompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) {
return ZlibDecompressorStats{ALL_ZLIB_DECOMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))};
}

bool inflateNext();

const ZlibDecompressorStats stats_;
};

} // namespace Decompressor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class DecompressorFilterConfig {
Compression::Decompressor::DecompressorFactoryPtr decompressor_factory);

Compression::Decompressor::DecompressorPtr makeDecompressor() {
return decompressor_factory_->createDecompressor();
return decompressor_factory_->createDecompressor(stats_prefix_ + "decompressor_library");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this stat prefix be a part of decompressor_factory_'s state? So that this string concatenation would happen only once at Envoy's start rather than for every stream creation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I debated this back and forth because I am not sure if we would have a place where we would want each decompressor created by the same factory to have different strings.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just do it the way you suggest. Having the string concatenation in the hot path seems like a bad price to pay for the niche ability to configure stats trees differently for individual decompressors.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm all of the above. I now do understand what you mean. Let me know if what I have pushed is not it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's definitely better. Though I was thinking about changing the meaning of DecompressorFactory::statsPrefix(). Instead of returning .gzip it could return the full root stats prefix since we create a factory per DecompressionFilterConfig anyway.

Then DecompressorFilterConfig::stats_prefix_ wouldn't be needed and DecompressorFactory::createDecompressor() would retain its current signature.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn’t want to do that because I don’t want the filter stats to have the decompressor_library prefix. To me the public statsPrefix function is for a consumer to use. And the argument in createDeconoressor is what the consumer wants to ultimately root the decompressor at.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, fair enough.

}
const std::string& contentEncoding() { return decompressor_factory_->contentEncoding(); }
const RequestDirectionConfig& requestDirectionConfig() { return request_direction_config_; }
Expand Down
5 changes: 4 additions & 1 deletion test/extensions/compression/gzip/compressor_fuzz_test.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "common/buffer/buffer_impl.h"
#include "common/common/assert.h"
#include "common/stats/isolated_store_impl.h"

#include "extensions/compression/gzip/compressor/zlib_compressor_impl.h"
#include "extensions/compression/gzip/decompressor/zlib_decompressor_impl.h"
Expand All @@ -19,9 +20,11 @@ namespace Fuzz {
// trip compress-decompress pair; the decompressor itself is not fuzzed beyond
// whatever the compressor emits, as it exists only as a test utility today.
DEFINE_FUZZER(const uint8_t* buf, size_t len) {

FuzzedDataProvider provider(buf, len);
ZlibCompressorImpl compressor;
Decompressor::ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store;
Decompressor::ZlibDecompressorImpl decompressor{stats_store, "test"};

// Select target compression level. We can't use ConsumeEnum() since the range
// is non-contiguous.
Expand Down
1 change: 1 addition & 0 deletions test/extensions/compression/gzip/decompressor/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ envoy_extension_cc_test(
deps = [
"//source/common/common:assert_lib",
"//source/common/common:hex_lib",
"//source/common/stats:isolated_store_lib",
"//source/extensions/compression/gzip/compressor:compressor_lib",
"//source/extensions/compression/gzip/decompressor:zlib_decompressor_impl_lib",
"//test/test_common:utility_lib",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "common/buffer/buffer_impl.h"
#include "common/common/hex.h"
#include "common/stats/isolated_store_impl.h"

#include "extensions/compression/gzip/compressor/zlib_compressor_impl.h"
#include "extensions/compression/gzip/decompressor/zlib_decompressor_impl.h"
Expand Down Expand Up @@ -46,7 +47,8 @@ class ZlibDecompressorImplTest : public testing::Test {
drainBuffer(buffer);
ASSERT_EQ(0, buffer.length());

ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
decompressor.init(window_bits);

decompressor.decompress(accumulation_buffer, buffer);
Expand All @@ -66,17 +68,20 @@ class ZlibDecompressorImplTest : public testing::Test {
class ZlibDecompressorImplFailureTest : public ZlibDecompressorImplTest {
protected:
static void decompressorBadInitTestHelper(int64_t window_bits) {
ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
decompressor.init(window_bits);
}

static void uninitializedDecompressorTestHelper() {
Buffer::OwnedImpl input_buffer;
Buffer::OwnedImpl output_buffer;
ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
TestUtility::feedBufferWithRandomCharacters(input_buffer, 100);
decompressor.decompress(input_buffer, output_buffer);
ASSERT_TRUE(decompressor.decompression_error_ < 0);
ASSERT_EQ(stats_store.counterFromString("test.decompression_error").value(), 1);
}
};

Expand Down Expand Up @@ -105,7 +110,8 @@ TEST_F(ZlibDecompressorImplTest, CallingChecksum) {
compressor.compress(compressor_buffer, Envoy::Compression::Compressor::State::Flush);
ASSERT_TRUE(compressor.checksum() > 0);

ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
decompressor.init(gzip_window_bits);
EXPECT_EQ(0, decompressor.checksum());

Expand Down Expand Up @@ -150,7 +156,8 @@ TEST_F(ZlibDecompressorImplTest, CompressAndDecompress) {
drainBuffer(buffer);
ASSERT_EQ(0, buffer.length());

ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
decompressor.init(gzip_window_bits);

decompressor.decompress(accumulation_buffer, buffer);
Expand Down Expand Up @@ -180,12 +187,14 @@ TEST_F(ZlibDecompressorImplTest, FailedDecompression) {
accumulation_buffer.add(buffer);
drainBuffer(buffer);
}
ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
decompressor.init(gzip_window_bits);

decompressor.decompress(accumulation_buffer, buffer);

ASSERT_TRUE(decompressor.decompression_error_ < 0);
ASSERT_EQ(stats_store.counterFromString("test.decompression_error").value(), 17);
}

// Exercises decompression with a very small output buffer.
Expand Down Expand Up @@ -218,7 +227,8 @@ TEST_F(ZlibDecompressorImplTest, DecompressWithSmallOutputBuffer) {
drainBuffer(buffer);
ASSERT_EQ(0, buffer.length());

ZlibDecompressorImpl decompressor(16);
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test.", 16};
decompressor.init(gzip_window_bits);

decompressor.decompress(accumulation_buffer, buffer);
Expand Down Expand Up @@ -284,7 +294,8 @@ TEST_F(ZlibDecompressorImplTest, CompressDecompressOfMultipleSlices) {
compressor.compress(buffer, Envoy::Compression::Compressor::State::Flush);
accumulation_buffer.add(buffer);

ZlibDecompressorImpl decompressor;
Stats::IsolatedStoreImpl stats_store{};
ZlibDecompressorImpl decompressor{stats_store, "test."};
decompressor.init(gzip_window_bits);

drainBuffer(buffer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ class CompressorIntegrationTest : public testing::TestWithParam<Network::Address

const uint64_t window_bits{15 | 16};

Extensions::Compression::Gzip::Decompressor::ZlibDecompressorImpl decompressor_{};
Stats::IsolatedStoreImpl stats_store_;
Extensions::Compression::Gzip::Decompressor::ZlibDecompressorImpl decompressor_{stats_store_,
"test"};
};

INSTANTIATE_TEST_SUITE_P(IpVersions, CompressorIntegrationTest,
Expand Down
Loading