Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
de5288a
Initial implementation of DynamicMetadata & StringAccessor
Jul 19, 2018
3b1250e
Integrated (I hope) with RequestInfo.
Jul 19, 2018
ee73e1f
Initial dynamic metadata test passes.
Jul 20, 2018
c8066df
All DM tests.
Jul 20, 2018
653fdd7
Trivial request_info test.
Jul 20, 2018
36a3594
Format fixed.
Jul 20, 2018
1bfe99c
Changes forced by asan testing.
Jul 20, 2018
603c588
Got test to build.
Jul 20, 2018
199a5c8
Fixed format
Jul 20, 2018
229414d
Incorporated most comments.
Jul 24, 2018
48f2246
Made type id thread safe.
Jul 24, 2018
ed497c8
Removed insertion into RequestInfo
Jul 25, 2018
dbbaec5
fit fix format
Jul 25, 2018
cedbac4
Cleanup
Jul 25, 2018
4207f27
Merge branch 'master' into DynamicMetadata
Jul 25, 2018
6864aa4
Added doxygen comments and finer grained probe function
Jul 26, 2018
18b3b57
Added missing const markers
Jul 26, 2018
0df9cfa
dock fix
Jul 26, 2018
510368d
Incoporated comments.
Jul 27, 2018
88fade4
dock fix
Jul 27, 2018
66d640e
Removed StringAccessor.
Jul 27, 2018
5dd51be
Merge remote-tracking branch 'origin/DynamicMetadata_xfer' into Dynam…
Jul 27, 2018
c5f3775
Shifted source associated with template MP to source/common
Jul 31, 2018
4a89207
Switched implementation over to dynamic_cast<> with all that represents.
Aug 1, 2018
d76a5fe
... and finished tests.
Aug 1, 2018
e59f4be
Incorporated rest of comments.
Aug 1, 2018
c6ba24c
Fix format
Aug 1, 2018
5205592
Merge remote-tracking branch 'origin/DynamicMetadata_xfer' into Dynam…
Aug 1, 2018
9a5ba4f
Remove unneeded header.
Aug 1, 2018
9cb16cc
Changed DynamicMetadataObject to Object
Aug 2, 2018
65d8e16
Incorporated comments
Aug 2, 2018
e62097f
fix format
Aug 2, 2018
d221181
Incorporated comments.
Aug 3, 2018
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
7 changes: 7 additions & 0 deletions include/envoy/request_info/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ envoy_cc_library(
hdrs = ["request_info.h"],
external_deps = ["abseil_optional"],
deps = [
":dynamic_metadata_interface",
"//include/envoy/common:time_interface",
"//include/envoy/http:protocol_interface",
"//include/envoy/upstream:upstream_interface",
],
)

envoy_cc_library(
name = "dynamic_metadata_interface",
hdrs = ["dynamic_metadata.h"],
external_deps = ["abseil_optional"],
)
70 changes: 70 additions & 0 deletions include/envoy/request_info/dynamic_metadata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#pragma once

#include <memory>

#include "envoy/common/exception.h"
#include "envoy/common/pure.h"

#include "common/common/fmt.h"

#include "absl/strings/string_view.h"

namespace Envoy {
namespace RequestInfo {

class DynamicMetadata {
public:
class Object {
public:
virtual ~Object(){};
};

virtual ~DynamicMetadata(){};

/**
* @param data_name the name of the data being set.
* @param data an owning pointer to the data to be stored.
* Note that it is an error to call setData() twice with the same data_name; this is to
* enforce a single authoritative source for each piece of data stored in DynamicMetadata.
*/
virtual void setData(absl::string_view data_name, std::unique_ptr<Object>&& data) PURE;

/**
* @param data_name the name of the data being set.
* @return a reference to the stored data.
* Note that it is an error to access data that has not previously been set.
* This function will fail if the data stored under |data_name| cannot be
* dynamically cast to the type specified.
*/
template <typename T> const T& getData(absl::string_view data_name) const {
const T* result = dynamic_cast<const T*>(getDataGeneric(data_name));
if (!result) {
throw EnvoyException(
fmt::format("Data stored under {} cannot be coerced to specified type", data_name));
}
return *result;
}

/**
* @param data_name the name of the data being probed.
* @return Whether data of the type and name specified exists in the
* data store.
*/
template <typename T> bool hasData(absl::string_view data_name) const {
return (hasDataWithName(data_name) &&
(dynamic_cast<const T*>(getDataGeneric(data_name)) != nullptr));
}

/**
* @param data_name the name of the data being probed.
* @return Whether data of any type and the name specified exists in the
* data store.
*/
virtual bool hasDataWithName(absl::string_view data_name) const PURE;

protected:
virtual const Object* getDataGeneric(absl::string_view data_name) const PURE;
};

} // namespace RequestInfo
} // namespace Envoy
1 change: 1 addition & 0 deletions include/envoy/request_info/request_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "envoy/common/pure.h"
#include "envoy/common/time.h"
#include "envoy/http/protocol.h"
#include "envoy/request_info/dynamic_metadata.h"
#include "envoy/upstream/upstream.h"

#include "absl/types/optional.h"
Expand Down
9 changes: 9 additions & 0 deletions source/common/request_info/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "dynamic_metadata_lib",
srcs = ["dynamic_metadata_impl.cc"],
hdrs = ["dynamic_metadata_impl.h"],
deps = [
"//include/envoy/request_info:dynamic_metadata_interface",
],
)

envoy_cc_library(
name = "utility_lib",
srcs = ["utility.cc"],
Expand Down
32 changes: 32 additions & 0 deletions source/common/request_info/dynamic_metadata_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "common/request_info/dynamic_metadata_impl.h"

#include "envoy/common/exception.h"

namespace Envoy {
namespace RequestInfo {

void DynamicMetadataImpl::setData(absl::string_view data_name, std::unique_ptr<Object>&& data) {
if (data_storage_.find(data_name) != data_storage_.end()) {
throw EnvoyException("DynamicMetadata::setData<T> called twice with same name.");
}
// absl::string_view will not convert to std::string without an explicit case; see
// https://github.com/abseil/abseil-cpp/blob/master/absl/strings/string_view.h#L328
data_storage_[static_cast<std::string>(data_name)] = std::move(data);
}

bool DynamicMetadataImpl::hasDataWithName(absl::string_view data_name) const {
return data_storage_.count(data_name) > 0;
}

const DynamicMetadata::Object*
DynamicMetadataImpl::getDataGeneric(absl::string_view data_name) const {
const auto& it = data_storage_.find(data_name);

if (it == data_storage_.end()) {
throw EnvoyException("DynamicMetadata::getData<T> called for unknown data name.");
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.

Nit: getDataGeneric

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I intentionally made it getData<> as the receiver of the throw will be above the public API of the DynamicMetadata class, and hence will be more interested in what they did wrong in targeting that public API. Given that, do you still want it spelled getDataGeneric?

}
return it->second.get();
}

} // namespace RequestInfo
} // namespace Envoy
27 changes: 27 additions & 0 deletions source/common/request_info/dynamic_metadata_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <map>

#include "envoy/request_info/dynamic_metadata.h"

#include "absl/strings/string_view.h"

namespace Envoy {
namespace RequestInfo {

class DynamicMetadataImpl : public DynamicMetadata {
public:
// DynamicMetadata
void setData(absl::string_view data_name, std::unique_ptr<Object>&& data) override;
bool hasDataWithName(absl::string_view) const override;
const Object* getDataGeneric(absl::string_view data_name) const override;

private:
// The explicit non-type-specific comparator is necessary to allow use of find() method
// with absl::string_view. See
// https://stackoverflow.com/questions/20317413/what-are-transparent-comparators.
std::map<std::string, std::unique_ptr<Object>, std::less<>> data_storage_;
};

} // namespace RequestInfo
} // namespace Envoy
9 changes: 9 additions & 0 deletions test/common/request_info/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ load(

envoy_package()

envoy_cc_test(
name = "dynamic_metadata_impl_test",
srcs = ["dynamic_metadata_impl_test.cc"],
deps = [
"//source/common/request_info:dynamic_metadata_lib",
"//test/test_common:utility_lib",
],
)

envoy_cc_test(
name = "request_info_impl_test",
srcs = ["request_info_impl_test.cc"],
Expand Down
171 changes: 171 additions & 0 deletions test/common/request_info/dynamic_metadata_impl_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include "envoy/common/exception.h"

#include "common/request_info/dynamic_metadata_impl.h"

#include "test/test_common/utility.h"

#include "gtest/gtest.h"

namespace Envoy {
namespace RequestInfo {
namespace {

class TestStoredTypeTracking : public DynamicMetadata::Object {
public:
TestStoredTypeTracking(int value, size_t* access_count, size_t* destruction_count)
: value_(value), access_count_(access_count), destruction_count_(destruction_count) {}
~TestStoredTypeTracking() {
if (destruction_count_) {
++*destruction_count_;
}
}

int access() const {
if (access_count_) {
++*access_count_;
}
return value_;
}

private:
int value_;
size_t* access_count_;
size_t* destruction_count_;
};

class SimpleType : public DynamicMetadata::Object {
public:
SimpleType(int value) : value_(value) {}

int access() const { return value_; }

private:
int value_;
};

class DynamicMetadataImplTest : public testing::Test {
public:
DynamicMetadataImplTest() { resetDynamicMetadata(); }

void resetDynamicMetadata() { dynamic_metadata_ = std::make_unique<DynamicMetadataImpl>(); }
DynamicMetadata& dynamic_metadata() { return *dynamic_metadata_; }

private:
std::unique_ptr<DynamicMetadataImpl> dynamic_metadata_;
};

} // namespace

TEST_F(DynamicMetadataImplTest, Simple) {
size_t access_count = 0u;
size_t destruction_count = 0u;
dynamic_metadata().setData(
"test_name", std::make_unique<TestStoredTypeTracking>(5, &access_count, &destruction_count));
EXPECT_EQ(0u, access_count);
EXPECT_EQ(0u, destruction_count);

EXPECT_EQ(5, dynamic_metadata().getData<TestStoredTypeTracking>("test_name").access());
EXPECT_EQ(1u, access_count);
EXPECT_EQ(0u, destruction_count);

resetDynamicMetadata();
EXPECT_EQ(1u, access_count);
EXPECT_EQ(1u, destruction_count);
}

TEST_F(DynamicMetadataImplTest, SameTypes) {
size_t access_count_1 = 0u;
size_t access_count_2 = 0u;
size_t destruction_count = 0u;
static const int ValueOne = 5;
static const int ValueTwo = 6;

dynamic_metadata().setData("test_1", std::make_unique<TestStoredTypeTracking>(
ValueOne, &access_count_1, &destruction_count));
dynamic_metadata().setData("test_2", std::make_unique<TestStoredTypeTracking>(
ValueTwo, &access_count_2, &destruction_count));
EXPECT_EQ(0u, access_count_1);
EXPECT_EQ(0u, access_count_2);
EXPECT_EQ(0u, destruction_count);

EXPECT_EQ(ValueOne, dynamic_metadata().getData<TestStoredTypeTracking>("test_1").access());
EXPECT_EQ(1u, access_count_1);
EXPECT_EQ(0u, access_count_2);
EXPECT_EQ(ValueTwo, dynamic_metadata().getData<TestStoredTypeTracking>("test_2").access());
EXPECT_EQ(1u, access_count_1);
EXPECT_EQ(1u, access_count_2);
resetDynamicMetadata();
EXPECT_EQ(2u, destruction_count);
}

TEST_F(DynamicMetadataImplTest, SimpleType) {
dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(1));
dynamic_metadata().setData("test_2", std::make_unique<SimpleType>(2));

EXPECT_EQ(1, dynamic_metadata().getData<SimpleType>("test_1").access());
EXPECT_EQ(2, dynamic_metadata().getData<SimpleType>("test_2").access());
}

TEST_F(DynamicMetadataImplTest, NameConflict) {
dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(1));
EXPECT_THROW_WITH_MESSAGE(dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(2)),
EnvoyException,
"DynamicMetadata::setData<T> called twice with same name.");
EXPECT_EQ(1, dynamic_metadata().getData<SimpleType>("test_1").access());
}

TEST_F(DynamicMetadataImplTest, NameConflictDifferentTypes) {
dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(1));
EXPECT_THROW_WITH_MESSAGE(
dynamic_metadata().setData("test_1",
std::make_unique<TestStoredTypeTracking>(2, nullptr, nullptr)),
EnvoyException, "DynamicMetadata::setData<T> called twice with same name.");
}

TEST_F(DynamicMetadataImplTest, UnknownName) {
EXPECT_THROW_WITH_MESSAGE(dynamic_metadata().getData<SimpleType>("test_1"), EnvoyException,
"DynamicMetadata::getData<T> called for unknown data name.");
}

TEST_F(DynamicMetadataImplTest, WrongTypeGet) {
dynamic_metadata().setData("test_name",
std::make_unique<TestStoredTypeTracking>(5, nullptr, nullptr));
EXPECT_EQ(5, dynamic_metadata().getData<TestStoredTypeTracking>("test_name").access());
EXPECT_THROW_WITH_MESSAGE(dynamic_metadata().getData<SimpleType>("test_name"), EnvoyException,
"Data stored under test_name cannot be coerced to specified type");
}

namespace {

class A : public DynamicMetadata::Object {};

class B : public A {};

class C : public B {};

} // namespace

TEST_F(DynamicMetadataImplTest, FungibleInheritance) {
dynamic_metadata().setData("testB", std::make_unique<B>());
EXPECT_TRUE(dynamic_metadata().hasData<B>("testB"));
EXPECT_TRUE(dynamic_metadata().hasData<A>("testB"));
EXPECT_FALSE(dynamic_metadata().hasData<C>("testB"));

dynamic_metadata().setData("testC", std::make_unique<C>());
EXPECT_TRUE(dynamic_metadata().hasData<B>("testC"));
EXPECT_TRUE(dynamic_metadata().hasData<A>("testC"));
EXPECT_TRUE(dynamic_metadata().hasData<C>("testC"));
}

TEST_F(DynamicMetadataImplTest, HasData) {
dynamic_metadata().setData("test_1", std::make_unique<SimpleType>(1));
EXPECT_TRUE(dynamic_metadata().hasData<SimpleType>("test_1"));
EXPECT_FALSE(dynamic_metadata().hasData<SimpleType>("test_2"));
EXPECT_FALSE(dynamic_metadata().hasData<TestStoredTypeTracking>("test_1"));
EXPECT_FALSE(dynamic_metadata().hasData<TestStoredTypeTracking>("test_2"));
EXPECT_TRUE(dynamic_metadata().hasDataWithName("test_1"));
EXPECT_FALSE(dynamic_metadata().hasDataWithName("test_2"));
}

} // namespace RequestInfo
} // namespace Envoy