Permalink
Browse files

thrift: sniffing thrift filter with request/response stats (#3622)

Glues together code from previous PRs into a ThriftFilter that counts requests (by type) and responses (by type and result), counts protocol errors for requests and responses, and records timings of request/response pairs.

Risk Level: Low
Testing: unit and manual testing
Docs Changes: trivial documentation of thrift config
Release Notes: introduced envoy.thrift filter
Relates to: #2247

Signed-off-by: Stephan Zuercher <stephan@turbinelabs.io>
  • Loading branch information...
zuercher committed Jun 20, 2018
1 parent b34adb5 commit 4521e89da33ce44ebdc61b2b1da07712341d743d
@@ -0,0 +1,8 @@
load("//bazel:api_build_system.bzl", "api_proto_library")

licenses(["notice"]) # Apache 2

api_proto_library(
name = "thrift_proxy",
srcs = ["thrift_proxy.proto"],
)
@@ -0,0 +1 @@
Protocol buffer definitions for the Thrift proxy.
@@ -0,0 +1,13 @@
syntax = "proto3";

package envoy.extensions.filters.network.thrift_proxy.v2alpha1;
option go_package = "v2";

import "validate/validate.proto";

// [#protodoc-title: Extensions Thrift Proxy]
// Thrift Proxy filter configuration.
message ThriftProxy {
// The human readable prefix to use when emitting statistics.
string stat_prefix = 1 [(validate.rules).string.min_bytes = 1];
}
@@ -67,8 +67,7 @@ EXTENSIONS = {
"envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config",
"envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config",
"envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config",
# TODO(zuercher): switch to config target once a filter exists
"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:transport_lib",
"envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config",

#
# Stat sinks
@@ -19,6 +19,20 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
":filter_lib",
"//include/envoy/registry",
"//source/common/config:filter_json_lib",
"//source/extensions/filters/network:well_known_names",
"//source/extensions/filters/network/common:factory_base_lib",
"@envoy_api//envoy/extensions/filters/network/thrift_proxy/v2alpha1:thrift_proxy_cc",
],
)

envoy_cc_library(
name = "decoder_lib",
srcs = ["decoder.cc"],
@@ -30,6 +44,24 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "filter_lib",
srcs = ["filter.cc"],
hdrs = ["filter.h"],
deps = [
":decoder_lib",
"//include/envoy/network:connection_interface",
"//include/envoy/network:filter_interface",
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
"//include/envoy/stats:timespan",
"//source/common/buffer:buffer_lib",
"//source/common/common:assert_lib",
"//source/common/common:logger_lib",
"//source/common/network:filter_lib",
],
)

envoy_cc_library(
name = "protocol_lib",
srcs = [
@@ -10,6 +10,52 @@ namespace Extensions {
namespace NetworkFilters {
namespace ThriftProxy {

/**
* BufferWrapper provides a partial implementation of Buffer::Instance that is sufficient for
* BufferHelper to read Thrift protocol data without draining the buffer's contents.
*/
class BufferWrapper : public Buffer::Instance {
public:
BufferWrapper(Buffer::Instance& underlying) : underlying_(underlying) {}

uint64_t position() { return position_; }

// Buffer::Instance
void copyOut(size_t start, uint64_t size, void* data) const override {
ASSERT(position_ + start + size <= underlying_.length());
underlying_.copyOut(start + position_, size, data);
}
void drain(uint64_t size) override {
ASSERT(position_ + size <= underlying_.length());
position_ += size;
}
uint64_t length() const override {
ASSERT(underlying_.length() >= position_);
return underlying_.length() - position_;
}
void* linearize(uint32_t size) override {
ASSERT(position_ + size <= underlying_.length());
uint8_t* p = static_cast<uint8_t*>(underlying_.linearize(position_ + size));
return p + position_;
}
void add(const void*, uint64_t) override { NOT_IMPLEMENTED; }
void addBufferFragment(Buffer::BufferFragment&) override { NOT_IMPLEMENTED; }
void add(const std::string&) override { NOT_IMPLEMENTED; }
void add(const Buffer::Instance&) override { NOT_IMPLEMENTED; }
void commit(Buffer::RawSlice*, uint64_t) override { NOT_IMPLEMENTED; }
uint64_t getRawSlices(Buffer::RawSlice*, uint64_t) const override { NOT_IMPLEMENTED; }
void move(Buffer::Instance&) override { NOT_IMPLEMENTED; }
void move(Buffer::Instance&, uint64_t) override { NOT_IMPLEMENTED; }
int read(int, uint64_t) override { NOT_IMPLEMENTED; }
uint64_t reserve(uint64_t, Buffer::RawSlice*, uint64_t) override { NOT_IMPLEMENTED; }
ssize_t search(const void*, uint64_t, size_t) const override { NOT_IMPLEMENTED; }
int write(int) override { NOT_IMPLEMENTED; }

private:
Buffer::Instance& underlying_;
uint64_t position_{0};
};

/**
* BufferHelper provides buffer operations for reading bytes and numbers in the various encodings
* used by Thrift protocols.
@@ -0,0 +1,37 @@
#include "extensions/filters/network/thrift_proxy/config.h"

#include <string>

#include "envoy/network/connection.h"
#include "envoy/registry/registry.h"

#include "extensions/filters/network/thrift_proxy/filter.h"

namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace ThriftProxy {

Network::FilterFactoryCb ThriftProxyFilterConfigFactory::createFilterFactoryFromProtoTyped(
const envoy::extensions::filters::network::thrift_proxy::v2alpha1::ThriftProxy& proto_config,
Server::Configuration::FactoryContext& context) {
ASSERT(!proto_config.stat_prefix().empty());

const std::string stat_prefix = fmt::format("thrift.{}.", proto_config.stat_prefix());

return [stat_prefix, &context](Network::FilterManager& filter_manager) -> void {
filter_manager.addFilter(std::make_shared<Filter>(stat_prefix, context.scope()));
};
}

/**
* Static registration for the thrift filter. @see RegisterFactory.
*/
static Registry::RegisterFactory<ThriftProxyFilterConfigFactory,
Server::Configuration::NamedNetworkFilterConfigFactory>
registered_;

} // namespace ThriftProxy
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
@@ -0,0 +1,34 @@
#pragma once

#include <string>

#include "envoy/extensions/filters/network/thrift_proxy/v2alpha1/thrift_proxy.pb.h"
#include "envoy/extensions/filters/network/thrift_proxy/v2alpha1/thrift_proxy.pb.validate.h"

#include "extensions/filters/network/common/factory_base.h"
#include "extensions/filters/network/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace ThriftProxy {

/**
* Config registration for the thrift proxy filter. @see NamedNetworkFilterConfigFactory.
*/
class ThriftProxyFilterConfigFactory
: public Common::FactoryBase<
envoy::extensions::filters::network::thrift_proxy::v2alpha1::ThriftProxy> {
public:
ThriftProxyFilterConfigFactory() : FactoryBase(NetworkFilterNames::get().THRIFT_PROXY) {}

private:
Network::FilterFactoryCb createFilterFactoryFromProtoTyped(
const envoy::extensions::filters::network::thrift_proxy::v2alpha1::ThriftProxy& proto_config,
Server::Configuration::FactoryContext& context) override;
};

} // namespace ThriftProxy
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
@@ -77,8 +77,9 @@ ProtocolState DecoderStateMachine::fieldBegin(Buffer::Instance& buffer) {
// FieldValue -> FieldEnd (via stack return state)
ProtocolState DecoderStateMachine::fieldValue(Buffer::Instance& buffer) {
ASSERT(!stack_.empty());
FieldType field_type = stack_.back().elem_type_;
return handleValue(buffer, field_type, popReturnState());

Frame& frame = stack_.back();
return handleValue(buffer, frame.elem_type_, frame.return_state_);
}

// FieldEnd -> FieldBegin
@@ -87,6 +88,8 @@ ProtocolState DecoderStateMachine::fieldEnd(Buffer::Instance& buffer) {
return ProtocolState::WaitForData;
}

popReturnState();

return ProtocolState::FieldBegin;
}

Oops, something went wrong.

0 comments on commit 4521e89

Please sign in to comment.