Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decompressor library: add stats to zlib library #11782

Merged
merged 23 commits into from
Jul 12, 2020
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::CustomHeaders::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
@@ -1,5 +1,7 @@
#include "extensions/compression/gzip/decompressor/zlib_decompressor_impl.h"

#include <zlib.h>

#include <memory>

#include "envoy/common/exception.h"
Expand All @@ -14,13 +16,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,16 +73,40 @@ 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);
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
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,32 @@ namespace Compression {
namespace Gzip {
namespace Decompressor {

/**
* All zlib decompressor stats. @see stats_macros.h
*/
#define ALL_ZLIB_DECOMPRESSOR_STATS(COUNTER) \
COUNTER(zlib_errno) \
COUNTER(zlib_stream_error) \
COUNTER(zlib_data_error) \
COUNTER(zlib_mem_error) \
COUNTER(zlib_buf_error) \
COUNTER(zlib_version_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 +51,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 +69,19 @@ class ZlibDecompressorImpl : public Zlib::Base,
int decompression_error_{0};

private:
// TODO: clean up friend class. This is here to allow coverage of chargeErrorStats as it isn't
Copy link
Member

Choose a reason for hiding this comment

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

Nit: you may want to have TODO(junr03) in this line.

Copy link
Member Author

Choose a reason for hiding this comment

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

I like leaving todos I don't intend to do right away without a username that way it doesn't dissuade others to fix todos they find.

// completely straightforward
// to cause zlib's inflate function to return all the error codes necessary to hit all the cases
// in the switch statement.
friend class ZlibDecompressorStatsTest;
static ZlibDecompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) {
return ZlibDecompressorStats{ALL_ZLIB_DECOMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))};
}

bool inflateNext();
void chargeErrorStats(const int result);

const ZlibDecompressorStats stats_;
};

} // namespace Decompressor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ DecompressorFilterConfig::DecompressorFilterConfig(
: stats_prefix_(fmt::format("{}decompressor.{}.{}", stats_prefix,
proto_config.decompressor_library().name(),
decompressor_factory->statsPrefix())),
decompressor_stats_prefix_(stats_prefix_ + "decompressor_library"),
decompressor_factory_(std::move(decompressor_factory)),
request_direction_config_(proto_config.request_direction_config(), stats_prefix_, scope,
runtime),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,15 @@ class DecompressorFilterConfig {
Compression::Decompressor::DecompressorFactoryPtr decompressor_factory);

Compression::Decompressor::DecompressorPtr makeDecompressor() {
return decompressor_factory_->createDecompressor();
return decompressor_factory_->createDecompressor(decompressor_stats_prefix_);
}
const std::string& contentEncoding() { return decompressor_factory_->contentEncoding(); }
const RequestDirectionConfig& requestDirectionConfig() { return request_direction_config_; }
const ResponseDirectionConfig& responseDirectionConfig() { return response_direction_config_; }

private:
const std::string stats_prefix_;
const std::string decompressor_stats_prefix_;
const Compression::Decompressor::DecompressorFactoryPtr decompressor_factory_;
const RequestDirectionConfig request_direction_config_;
const ResponseDirectionConfig response_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
Loading