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
3 changes: 1 addition & 2 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ ConnectionManagerImpl::ConnectionManagerImpl(ConnectionManagerConfig& config,

const HeaderMapImpl& ConnectionManagerImpl::continueHeader() {
CONSTRUCT_ON_FIRST_USE(HeaderMapImpl,
Http::HeaderMapImpl{{Http::Headers::get().Status,
std::to_string(enumToInt(Code::Continue))}});
{Http::Headers::get().Status, std::to_string(enumToInt(Code::Continue))});
}

void ConnectionManagerImpl::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) {
Expand Down
30 changes: 15 additions & 15 deletions source/common/http/header_map_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,22 @@ void HeaderMapImpl::appendToHeader(HeaderString& header, absl::string_view data)

HeaderMapImpl::HeaderMapImpl() { memset(&inline_headers_, 0, sizeof(inline_headers_)); }

HeaderMapImpl::HeaderMapImpl(const HeaderMap& rhs) : HeaderMapImpl() {
rhs.iterate(
HeaderMapImpl::HeaderMapImpl(
const std::initializer_list<std::pair<LowerCaseString, std::string>>& values)
: HeaderMapImpl() {
for (auto& value : values) {
HeaderString key_string;
key_string.setCopy(value.first.get().c_str(), value.first.get().size());
HeaderString value_string;
value_string.setCopy(value.second.c_str(), value.second.size());
addViaMove(std::move(key_string), std::move(value_string));
}
}

void HeaderMapImpl::copyFrom(const HeaderMap& header_map) {
header_map.iterate(
[](const HeaderEntry& header, void* context) -> HeaderMap::Iterate {
// TODO(mattklein123) PERF: Avoid copying here is not necessary.
// TODO(mattklein123) PERF: Avoid copying here if not necessary.
HeaderString key_string;
key_string.setCopy(header.key().c_str(), header.key().size());
HeaderString value_string;
Expand All @@ -292,18 +304,6 @@ HeaderMapImpl::HeaderMapImpl(const HeaderMap& rhs) : HeaderMapImpl() {
this);
}

HeaderMapImpl::HeaderMapImpl(
const std::initializer_list<std::pair<LowerCaseString, std::string>>& values)
: HeaderMapImpl() {
for (auto& value : values) {
HeaderString key_string;
key_string.setCopy(value.first.get().c_str(), value.first.get().size());
HeaderString value_string;
value_string.setCopy(value.second.c_str(), value.second.size());
addViaMove(std::move(key_string), std::move(value_string));
}
}

bool HeaderMapImpl::operator==(const HeaderMapImpl& rhs) const {
if (size() != rhs.size()) {
return false;
Expand Down
20 changes: 16 additions & 4 deletions source/common/http/header_map_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public:
* paths use O(1) direct access. In general, we try to copy as little as possible and allocate as
* little as possible in any of the paths.
*/
class HeaderMapImpl : public HeaderMap {
class HeaderMapImpl : public HeaderMap, NonCopyable {
public:
/**
* Appends data to header. If header already has a value, the string ',' is added between the
Expand All @@ -46,8 +46,9 @@ class HeaderMapImpl : public HeaderMap {
static void appendToHeader(HeaderString& header, absl::string_view data);

HeaderMapImpl();
HeaderMapImpl(const std::initializer_list<std::pair<LowerCaseString, std::string>>& values);
HeaderMapImpl(const HeaderMap& rhs);
explicit HeaderMapImpl(
const std::initializer_list<std::pair<LowerCaseString, std::string>>& values);
explicit HeaderMapImpl(const HeaderMap& rhs) : HeaderMapImpl() { copyFrom(rhs); }

/**
* Add a header via full move. This is the expected high performance paths for codecs populating
Expand Down Expand Up @@ -80,6 +81,10 @@ class HeaderMapImpl : public HeaderMap {
size_t size() const override { return headers_.size(); }

protected:
// For tests only, unoptimized, they aren't intended for regular HeaderMapImpl users.
void copyFrom(const HeaderMap& rhs);
void clear() { removePrefix(LowerCaseString("")); }

struct HeaderEntryImpl : public HeaderEntry, NonCopyable {
HeaderEntryImpl(const LowerCaseString& key);
HeaderEntryImpl(const LowerCaseString& key, HeaderString&& value);
Expand Down Expand Up @@ -130,8 +135,15 @@ class HeaderMapImpl : public HeaderMap {
/**
* List of HeaderEntryImpl that keeps the pseudo headers (key starting with ':') in the front
* of the list (as required by nghttp2) and otherwise maintains insertion order.
*
* Note: the internal iterators held in fields make this unsafe to copy and move, since the
* reference to end() is not preserved across a move (see Notes in
* https://en.cppreference.com/w/cpp/container/list/list). The NonCopyable will supress both copy
* and move constructors/assignment.
* TODO(htuch): Maybe we want this to movable one day; for now, our header map moves happen on
* HeaderMapPtr, so the performance impact should not be evident.
*/
class HeaderList {
class HeaderList : NonCopyable {
Copy link
Member

Choose a reason for hiding this comment

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

I think it's fine to block move, but why not just block the implicit move constructor inside NonCopyable? I realize it's called NonCopyable but I'm pretty sure in all cases where we use that we also don't want move?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think this change is needed anymore, but fine to keep.

public:
HeaderList() : pseudo_headers_end_(headers_.end()) {}

Expand Down
2 changes: 1 addition & 1 deletion test/common/grpc/grpc_client_integration_test_harness.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class HelloworldStream : public MockAsyncStreamCallbacks<helloworld::HelloReply>
reply_headers->addReference(value.first, value.second);
}
expectInitialMetadata(metadata);
fake_stream_->encodeHeaders(*reply_headers, false);
fake_stream_->encodeHeaders(Http::HeaderMapImpl(*reply_headers), false);
}

void sendReply() {
Expand Down
15 changes: 15 additions & 0 deletions test/common/http/header_map_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -853,5 +853,20 @@ TEST(HeaderMapImplTest, PseudoHeaderOrder) {
}
}

// Validate that TestHeaderMapImpl copy construction and assignment works. This is a
// regression for where we were missing a valid copy constructor and had the
// default (dangerous) move semantics takeover.
TEST(HeaderMapImplTest, TestHeaderMapImplyCopy) {
TestHeaderMapImpl foo;
foo.addCopy(LowerCaseString("foo"), "bar");
auto headers = std::make_unique<TestHeaderMapImpl>(foo);
EXPECT_STREQ("bar", headers->get(LowerCaseString("foo"))->value().c_str());
TestHeaderMapImpl baz{{"foo", "baz"}};
baz = *headers;
EXPECT_STREQ("bar", baz.get(LowerCaseString("foo"))->value().c_str());
baz = baz;
EXPECT_STREQ("bar", baz.get(LowerCaseString("foo"))->value().c_str());
}

} // namespace Http
} // namespace Envoy
14 changes: 14 additions & 0 deletions test/test_common/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,20 @@ TestHeaderMapImpl::TestHeaderMapImpl(

TestHeaderMapImpl::TestHeaderMapImpl(const HeaderMap& rhs) : HeaderMapImpl(rhs) {}

TestHeaderMapImpl::TestHeaderMapImpl(const TestHeaderMapImpl& rhs)
: TestHeaderMapImpl(static_cast<const HeaderMap&>(rhs)) {}

TestHeaderMapImpl& TestHeaderMapImpl::operator=(const TestHeaderMapImpl& rhs) {
if (&rhs == this) {
return *this;
}

clear();
copyFrom(rhs);

return *this;
}

void TestHeaderMapImpl::addCopy(const std::string& key, const std::string& value) {
addCopy(LowerCaseString(key), value);
}
Expand Down
6 changes: 6 additions & 0 deletions test/test_common/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,12 @@ class TestHeaderMapImpl : public HeaderMapImpl {
TestHeaderMapImpl(const std::initializer_list<std::pair<std::string, std::string>>& values);
TestHeaderMapImpl(const HeaderMap& rhs);

// The above constructor for TestHeaderMap is not an actual copy constructor.
TestHeaderMapImpl(const TestHeaderMapImpl& rhs);
TestHeaderMapImpl& operator=(const TestHeaderMapImpl& rhs);

bool operator==(const TestHeaderMapImpl& rhs) const { return HeaderMapImpl::operator==(rhs); }

friend std::ostream& operator<<(std::ostream& os, const TestHeaderMapImpl& p) {
p.iterate(
[](const HeaderEntry& header, void* context) -> HeaderMap::Iterate {
Expand Down