Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions include/envoy/router/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ envoy_cc_library(
hdrs = ["router.h"],
deps = [
"//include/envoy/common:optional",
"//include/envoy/http:access_log_interface",
"//include/envoy/http:codec_interface",
"//include/envoy/http:header_map_interface",
"//include/envoy/tracing:http_tracer_interface",
"//include/envoy/upstream:resource_manager_interface",
"//source/common/protobuf",
"//source/common/protobuf:utility_lib",
],
)

Expand Down
39 changes: 39 additions & 0 deletions include/envoy/router/router.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#include "envoy/tracing/http_tracer.h"
#include "envoy/upstream/resource_manager.h"

#include "common/protobuf/protobuf.h"
#include "common/protobuf/utility.h"

namespace Envoy {
namespace Router {

Expand Down Expand Up @@ -226,6 +229,36 @@ class HashPolicy {
const Http::HeaderMap& headers) const PURE;
};

class MetadataMatchCriterion {
public:
virtual ~MetadataMatchCriterion() {}

/*
* @return const std::string& the name of the metadata key
*/
virtual const std::string& name() const PURE;

/*
* @return const Envoy::HashedValue& the value for the metadata key
*/
virtual const HashedValue& value() const PURE;
};

typedef std::shared_ptr<const MetadataMatchCriterion> MetadataMatchCriterionConstSharedPtr;
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.

Does this need to be shared_ptr ?

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.

If a Route's RouteAction has metadata_match criteria and has multiple WeightedClusters with their own metadata_match criteria, the RouteAction's MetadataMatchCriterion ends being referenced by multiple MetadataMatchCriteria implementations.

That said, they could be copied instead of using a shared_ptr.

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 fine, just wanted to check.


class MetadataMatchCriteria {
public:
virtual ~MetadataMatchCriteria() {}

/*
* @return std::vector<MetadataMatchCriterionConstSharedPtr>& a vector of
* metadata to be matched against upstream endpoints when load
* balancing, sorted lexically by name.
*/
virtual const std::vector<MetadataMatchCriterionConstSharedPtr>&
metadataMatchCriteria() const PURE;
};

/**
* An individual resolved route entry.
*/
Expand Down Expand Up @@ -307,6 +340,12 @@ class RouteEntry {
*/
virtual bool useWebSocket() const PURE;

/**
* @return MetadataMatchCriteria* the metadata that a subset load balancer should match when
* selecting an upstream host
*/
virtual const MetadataMatchCriteria* metadataMatchCriteria() const PURE;

/**
* @return const std::multimap<std::string, std::string> the opaque configuration associated
* with the route
Expand Down
1 change: 1 addition & 0 deletions source/common/http/async_client_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class AsyncStreamImpl : public AsyncClient::Stream,
void finalizeRequestHeaders(Http::HeaderMap&,
const Http::AccessLog::RequestInfo&) const override {}
const Router::HashPolicy* hashPolicy() const override { return nullptr; }
const Router::MetadataMatchCriteria* metadataMatchCriteria() const override { return nullptr; }
Upstream::ResourcePriority priority() const override {
return Upstream::ResourcePriority::Default;
}
Expand Down
57 changes: 57 additions & 0 deletions source/common/protobuf/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,61 @@ void MessageUtil::jsonConvert(const Protobuf::Message& source, Protobuf::Message
MessageUtil::loadFromJson(json, dest);
}

bool ValueUtil::equal(const ProtobufWkt::Value& v1, const ProtobufWkt::Value& v2) {
ProtobufWkt::Value::KindCase kind = v1.kind_case();
if (kind != v2.kind_case()) {
return false;
}

switch (kind) {
case ProtobufWkt::Value::kNullValue:
return true;

case ProtobufWkt::Value::kNumberValue:
return v1.number_value() == v2.number_value();

case ProtobufWkt::Value::kStringValue:
return v1.string_value() == v2.string_value();

case ProtobufWkt::Value::kBoolValue:
return v1.bool_value() == v2.bool_value();

case ProtobufWkt::Value::kStructValue: {
const ProtobufWkt::Struct& s1 = v1.struct_value();
const ProtobufWkt::Struct& s2 = v2.struct_value();
if (s1.fields_size() != s2.fields_size()) {
return false;
}
for (const auto& it1 : s1.fields()) {
const auto& it2 = s2.fields().find(it1.first);
if (it2 == s2.fields().end()) {
return false;
}

if (!equal(it1.second, it2->second)) {
return false;
}
}
return true;
}

case ProtobufWkt::Value::kListValue: {
const ProtobufWkt::ListValue& l1 = v1.list_value();
const ProtobufWkt::ListValue& l2 = v2.list_value();
if (l1.values_size() != l2.values_size()) {
return false;
}
for (int i = 0; i < l1.values_size(); i++) {
if (!equal(l1.values(i), l2.values(i))) {
return false;
}
}
return true;
}

default:
NOT_REACHED;
}
}

} // namespace Envoy
44 changes: 44 additions & 0 deletions source/common/protobuf/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,48 @@ class MessageUtil {
}
};

class ValueUtil {
public:
static std::size_t hash(const ProtobufWkt::Value& value) { return MessageUtil::hash(value); }

/**
* Compare two ProtobufWkt::Values for equality.
* @param v1 message of type type.googleapis.com/google.protobuf.Value
* @param v2 message of type type.googleapis.com/google.protobuf.Value
* @return true if v1 and v2 are identical
*/
static bool equal(const ProtobufWkt::Value& v1, const ProtobufWkt::Value& v2);
};

/**
* HashedValue is a wrapper around ProtobufWkt::Value that computes
* and stores a hash code for the Value at construction.
*/
class HashedValue {
public:
HashedValue(const ProtobufWkt::Value& value) : value_(value), hash_(ValueUtil::hash(value)){};
HashedValue(const HashedValue& v) : value_(v.value_), hash_(v.hash_){};

const ProtobufWkt::Value& value() const { return value_; }
std::size_t hash() const { return hash_; }

bool operator==(const HashedValue& rhs) const {
return hash_ == rhs.hash_ && ValueUtil::equal(value_, rhs.value_);
}

bool operator!=(const HashedValue& rhs) const { return !(*this == rhs); }

private:
const ProtobufWkt::Value value_;
const std::size_t hash_;
};

} // namespace Envoy

namespace std {
// Inject an implementation of std::hash for Envoy::HashedValue into the std namespace.
template <> struct hash<Envoy::HashedValue> {
std::size_t operator()(Envoy::HashedValue const& v) const { return v.hash(); }
};

} // namespace std
2 changes: 2 additions & 0 deletions source/common/router/config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ class RouteEntryImplBase : public RouteEntry,
void finalizeRequestHeaders(Http::HeaderMap& headers,
const Http::AccessLog::RequestInfo& request_info) const override;
const HashPolicy* hashPolicy() const override { return hash_policy_.get(); }
const MetadataMatchCriteria* metadataMatchCriteria() const override { return nullptr; }
Upstream::ResourcePriority priority() const override { return priority_; }
const RateLimitPolicy& rateLimitPolicy() const override { return rate_limit_policy_; }
const RetryPolicy& retryPolicy() const override { return retry_policy_; }
Expand Down Expand Up @@ -315,6 +316,7 @@ class RouteEntryImplBase : public RouteEntry,
const RetryPolicy& retryPolicy() const override { return parent_->retryPolicy(); }
const ShadowPolicy& shadowPolicy() const override { return parent_->shadowPolicy(); }
std::chrono::milliseconds timeout() const override { return parent_->timeout(); }
const MetadataMatchCriteria* metadataMatchCriteria() const override { return nullptr; }

const VirtualCluster* virtualCluster(const Http::HeaderMap& headers) const override {
return parent_->virtualCluster(headers);
Expand Down
126 changes: 126 additions & 0 deletions test/common/protobuf/utility_test.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <unordered_set>

#include "common/protobuf/protobuf.h"
#include "common/protobuf/utility.h"

Expand Down Expand Up @@ -51,4 +53,128 @@ TEST(UtilityTest, LoadTextProtoFromFile_Failure) {
"\" as a text protobuf (type envoy.api.v2.Bootstrap)");
}

TEST(UtilityTest, ValueUtilEqual_NullValues) {
ProtobufWkt::Value v1, v2;
v1.set_null_value(ProtobufWkt::NULL_VALUE);
v2.set_null_value(ProtobufWkt::NULL_VALUE);

ProtobufWkt::Value other;
other.set_string_value("s");

EXPECT_TRUE(ValueUtil::equal(v1, v2));
EXPECT_FALSE(ValueUtil::equal(v1, other));
}

TEST(UtilityTest, ValueUtilEqual_StringValues) {
ProtobufWkt::Value v1, v2, v3;
v1.set_string_value("s");
v2.set_string_value("s");
v3.set_string_value("not_s");

EXPECT_TRUE(ValueUtil::equal(v1, v2));
EXPECT_FALSE(ValueUtil::equal(v1, v3));
}

TEST(UtilityTest, ValueUtilEqual_NumberValues) {
ProtobufWkt::Value v1, v2, v3;
v1.set_number_value(1.0);
v2.set_number_value(1.0);
v3.set_number_value(100.0);

EXPECT_TRUE(ValueUtil::equal(v1, v2));
EXPECT_FALSE(ValueUtil::equal(v1, v3));
}

TEST(UtilityTest, ValueUtilEqual_BoolValues) {
ProtobufWkt::Value v1, v2, v3;
v1.set_bool_value(true);
v2.set_bool_value(true);
v3.set_bool_value(false);

EXPECT_TRUE(ValueUtil::equal(v1, v2));
EXPECT_FALSE(ValueUtil::equal(v1, v3));
}

TEST(UtilityTest, ValueUtilEqual_StructValues) {
ProtobufWkt::Value string_val1, string_val2, bool_val;

string_val1.set_string_value("s1");
string_val2.set_string_value("s2");
bool_val.set_bool_value(true);

ProtobufWkt::Value v1, v2, v3, v4;
v1.mutable_struct_value()->mutable_fields()->insert({"f1", string_val1});
v1.mutable_struct_value()->mutable_fields()->insert({"f2", bool_val});

v2.mutable_struct_value()->mutable_fields()->insert({"f1", string_val1});
v2.mutable_struct_value()->mutable_fields()->insert({"f2", bool_val});

v3.mutable_struct_value()->mutable_fields()->insert({"f1", string_val2});
v3.mutable_struct_value()->mutable_fields()->insert({"f2", bool_val});

v4.mutable_struct_value()->mutable_fields()->insert({"f1", string_val1});

EXPECT_TRUE(ValueUtil::equal(v1, v2));
EXPECT_FALSE(ValueUtil::equal(v1, v3));
EXPECT_FALSE(ValueUtil::equal(v1, v4));
}

TEST(UtilityTest, ValueUtilEqual_ListValues) {
ProtobufWkt::Value v1, v2, v3, v4;
v1.mutable_list_value()->add_values()->set_string_value("s");
v1.mutable_list_value()->add_values()->set_bool_value(true);

v2.mutable_list_value()->add_values()->set_string_value("s");
v2.mutable_list_value()->add_values()->set_bool_value(true);

v3.mutable_list_value()->add_values()->set_bool_value(true);
v3.mutable_list_value()->add_values()->set_string_value("s");

v4.mutable_list_value()->add_values()->set_string_value("s");

EXPECT_TRUE(ValueUtil::equal(v1, v2));
EXPECT_FALSE(ValueUtil::equal(v1, v3));
EXPECT_FALSE(ValueUtil::equal(v1, v4));
}

TEST(UtilityTest, ValueUtilHash) {
ProtobufWkt::Value v;
v.set_string_value("s1");

EXPECT_NE(ValueUtil::hash(v), 0);
}

TEST(UtilityTest, HashedValue) {
ProtobufWkt::Value v1, v2, v3;
v1.set_string_value("s");
v2.set_string_value("s");
v3.set_string_value("not_s");

HashedValue hv1(v1), hv2(v2), hv3(v3);

EXPECT_EQ(hv1, hv2);
EXPECT_NE(hv1, hv3);

HashedValue copy(hv1);
EXPECT_EQ(hv1, copy);
}

TEST(UtilityTest, HashedValueStdHash) {
ProtobufWkt::Value v1, v2, v3;
v1.set_string_value("s");
v2.set_string_value("s");
v3.set_string_value("not_s");

HashedValue hv1(v1), hv2(v2), hv3(v3);

std::unordered_set<HashedValue> set;
set.emplace(hv1);
set.emplace(hv2);
set.emplace(hv3);

EXPECT_EQ(set.size(), 2); // hv1 == hv2
EXPECT_NE(set.find(hv1), set.end());
EXPECT_NE(set.find(hv3), set.end());
}

} // namespace Envoy
1 change: 1 addition & 0 deletions test/mocks/router/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class MockRouteEntry : public RouteEntry {
void(Http::HeaderMap& headers,
const Http::AccessLog::RequestInfo& request_info));
MOCK_CONST_METHOD0(hashPolicy, const HashPolicy*());
MOCK_CONST_METHOD0(metadataMatchCriteria, const Router::MetadataMatchCriteria*());
MOCK_CONST_METHOD0(priority, Upstream::ResourcePriority());
MOCK_CONST_METHOD0(rateLimitPolicy, const RateLimitPolicy&());
MOCK_CONST_METHOD0(retryPolicy, const RetryPolicy&());
Expand Down