-
Notifications
You must be signed in to change notification settings - Fork 5.5k
grpc: utilities for inter-converting grpc::ByteBuffer and Buffer::Instance. #6732
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
Changes from 3 commits
42d4dcb
6014cd4
88ef0fc
253b584
5e0763a
c54b9da
2860281
542ea54
887f759
5f941b4
84e9709
deb27a3
8f398ef
406bd50
377de24
8a9b7f4
70ecea2
5d716ae
1c87b72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| #include <arpa/inet.h> | ||
|
|
||
| #include <atomic> | ||
| #include <cstdint> | ||
| #include <cstring> | ||
| #include <string> | ||
|
|
@@ -12,6 +13,7 @@ | |
| #include "common/common/enum_to_int.h" | ||
| #include "common/common/fmt.h" | ||
| #include "common/common/macros.h" | ||
| #include "common/common/stack_array.h" | ||
| #include "common/common/utility.h" | ||
| #include "common/http/headers.h" | ||
| #include "common/http/message_impl.h" | ||
|
|
@@ -111,7 +113,7 @@ bool Common::resolveServiceAndMethod(const Http::HeaderEntry* path, std::string* | |
| return true; | ||
| } | ||
|
|
||
| Buffer::InstancePtr Common::serializeBody(const Protobuf::Message& message) { | ||
| Buffer::InstancePtr Common::serializeToGrpcFrame(const Protobuf::Message& message) { | ||
|
jplevyak marked this conversation as resolved.
|
||
| // http://www.grpc.io/docs/guides/wire.html | ||
| // Reserve enough space for the entire message and the 5 byte header. | ||
| Buffer::InstancePtr body(new Buffer::OwnedImpl()); | ||
|
|
@@ -133,6 +135,21 @@ Buffer::InstancePtr Common::serializeBody(const Protobuf::Message& message) { | |
| return body; | ||
| } | ||
|
|
||
| Buffer::InstancePtr Common::serializeMessage(const Protobuf::Message& message) { | ||
| Buffer::InstancePtr body(new Buffer::OwnedImpl()); | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| const uint32_t size = message.ByteSize(); | ||
| Buffer::RawSlice iovec; | ||
| body->reserve(size, &iovec, 1); | ||
| ASSERT(iovec.len_ >= size); | ||
| iovec.len_ = size; | ||
| uint8_t* current = reinterpret_cast<uint8_t*>(iovec.mem_); | ||
| Protobuf::io::ArrayOutputStream stream(current, size, -1); | ||
| Protobuf::io::CodedOutputStream codec_stream(&stream); | ||
| message.SerializeWithCachedSizes(&codec_stream); | ||
| body->commit(&iovec, 1); | ||
| return body; | ||
| } | ||
|
|
||
| std::chrono::milliseconds Common::getGrpcTimeout(Http::HeaderMap& request_headers) { | ||
| std::chrono::milliseconds timeout(0); | ||
| Http::HeaderEntry* header_grpc_timeout_entry = request_headers.GrpcTimeout(); | ||
|
|
@@ -269,5 +286,98 @@ std::string Common::typeUrl(const std::string& qualified_name) { | |
| return typeUrlPrefix() + "/" + qualified_name; | ||
| } | ||
|
|
||
| struct BufferInstanceContainer { | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| BufferInstanceContainer(int ref_count, Buffer::InstancePtr buffer) | ||
| : ref_count_(ref_count), buffer_(std::move(buffer)) {} | ||
| std::atomic<int> ref_count_; | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| Buffer::InstancePtr buffer_; | ||
| }; | ||
|
|
||
| static void derefBufferInstanceContainer(void* container_ptr) { | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| auto container = reinterpret_cast<BufferInstanceContainer*>(container_ptr); | ||
| container->ref_count_--; | ||
| if (container->ref_count_ <= 0) { | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| delete container; | ||
| } | ||
| } | ||
|
|
||
| grpc::ByteBuffer Common::makeByteBuffer(Buffer::InstancePtr bufferInstance) { | ||
|
jplevyak marked this conversation as resolved.
Outdated
jplevyak marked this conversation as resolved.
Outdated
|
||
| if (!bufferInstance) { | ||
| return {}; | ||
| } | ||
| Buffer::RawSlice oneRawSlice; | ||
| // NB: we need to pass in >= 1 in order to get the real "n" (see Buffer::Instance for details). | ||
| int nSlices = bufferInstance->getRawSlices(&oneRawSlice, 1); | ||
| if (nSlices <= 0) { | ||
| return {}; | ||
| } | ||
| auto container = new BufferInstanceContainer{nSlices, std::move(bufferInstance)}; | ||
| if (nSlices == 1) { | ||
| grpc::Slice oneSlice(oneRawSlice.mem_, oneRawSlice.len_, &derefBufferInstanceContainer, | ||
| container); | ||
| return {&oneSlice, 1}; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this copying the |
||
| } | ||
| STACK_ARRAY(manyRawSlices, Buffer::RawSlice, nSlices); | ||
| container->buffer_->getRawSlices(manyRawSlices.begin(), nSlices); | ||
| std::vector<grpc::Slice> slices; | ||
| slices.reserve(nSlices); | ||
| for (int i = 0; i < nSlices; i++) { | ||
| slices.emplace_back(manyRawSlices[i].mem_, manyRawSlices[i].len_, &derefBufferInstanceContainer, | ||
| container); | ||
| } | ||
| return {&slices[0], slices.size()}; | ||
| } | ||
|
|
||
| struct ByteBufferContainer { | ||
| ByteBufferContainer(int ref_count) : ref_count_(ref_count) {} | ||
| ~ByteBufferContainer() { ::free(fragments); } | ||
| std::atomic<int> ref_count_; | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| Buffer::BufferFragmentImpl* fragments = nullptr; | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| std::vector<grpc::Slice> slices_; | ||
| }; | ||
|
|
||
| Buffer::InstancePtr Common::makeBufferInstance(const grpc::ByteBuffer& byteBuffer) { | ||
| auto buffer = std::make_unique<Buffer::OwnedImpl>(); | ||
| if (byteBuffer.Length() == 0) { | ||
| return buffer; | ||
| } | ||
| // NB: ByteBuffer::Dump moves the data out of the ByteBuffer so we need to ensure that the | ||
| // lifetime of the Slice(s) exceeds our Buffer::Instance. | ||
| std::vector<grpc::Slice> slices; | ||
| byteBuffer.Dump(&slices); | ||
| if (slices.size() == 0) { | ||
| return buffer; | ||
| } | ||
| auto container = new ByteBufferContainer(static_cast<int>(slices.size())); | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| std::function<void(const void*, size_t, const Buffer::BufferFragmentImpl*)> releaser = | ||
| [container](const void*, size_t, const Buffer::BufferFragmentImpl*) { | ||
| container->ref_count_--; | ||
| if (container->ref_count_ <= 0) { | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| delete container; | ||
| } | ||
| }; | ||
| // NB: addBufferFragment takes a pointer alias to the BufferFragmentImpl which is passed in so we | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just make
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I make it container->fragments_ = std::make_uniqueBuffer::BufferFragmentImpl[](slices.size()); unique_ptr.h:831:34: error: no matching constructor for initialization of 'remove_extent_t<Envoy::Buffer::BufferFragmentImpl []> []' buffer_impl.h:430:7: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided Could you suggest an alternative?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could, but it was explicitly made NonCopyable because it is aliased by OwnedImpl, and therefore copying it could break the pointer from OwnedImpl, so I understand why it was made NonCopyable and I believe it needs to stay that way. Any other ideas?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In response to: "Can you elaborate?": The BufferFragmentImpl is not owned by the OwnedImpl, instead it takes a reference to it, so allowing the BufferFragmentImpl to move could result in hard to find bugs if someone erroneously thought that implied that it was a value type as opposed to what it is: something with strong address-based identity. |
||
| // need to ensure that the lifetime of those objects exceeds that of the Buffer::Instance. | ||
| container->fragments = static_cast<Buffer::BufferFragmentImpl*>( | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| ::malloc(sizeof(Buffer::BufferFragmentImpl) * slices.size())); | ||
| for (size_t i = 0; i < slices.size(); i++) { | ||
| new (&container->fragments[i]) | ||
| Buffer::BufferFragmentImpl(slices[i].begin(), slices[i].size(), releaser); | ||
| } | ||
| for (size_t i = 0; i < slices.size(); i++) { | ||
| buffer->addBufferFragment(container->fragments[i]); | ||
| } | ||
| container->slices_ = std::move(slices); | ||
| return buffer; | ||
| } | ||
|
|
||
| void Common::PrependGrpcFrameHeader(Buffer::Instance* buffer) { | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| char header[5]; | ||
| header[0] = 0; // flags | ||
| const uint32_t nsize = htonl(buffer->length()); | ||
| std::memcpy(&header[1], reinterpret_cast<const void*>(&nsize), sizeof(uint32_t)); | ||
| buffer->prepend(absl::string_view(header, 5)); | ||
| } | ||
|
|
||
| } // namespace Grpc | ||
| } // namespace Envoy | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| #include <arpa/inet.h> | ||
|
jplevyak marked this conversation as resolved.
|
||
|
|
||
| #include "common/grpc/common.h" | ||
| #include "common/http/headers.h" | ||
| #include "common/http/message_impl.h" | ||
|
|
@@ -349,5 +351,78 @@ TEST(GrpcCommonTest, ValidateResponse) { | |
| } | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, MakeBufferInstanceEmpty) { | ||
| grpc::ByteBuffer byteBuffer; | ||
| Common::makeBufferInstance(byteBuffer); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, MakeByteBufferEmpty) { | ||
| auto buffer = std::make_unique<Buffer::OwnedImpl>(); | ||
| Common::makeByteBuffer(std::move(buffer)); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, MakeBufferInstance1) { | ||
| grpc::Slice slice("test"); | ||
| grpc::ByteBuffer byteBuffer(&slice, 1); | ||
| auto bufferInstance = Common::makeBufferInstance(byteBuffer); | ||
| EXPECT_EQ(bufferInstance->toString(), "test"); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, MakeBufferInstance3) { | ||
| grpc::Slice slices[3] = {{"test"}, {" "}, {"this"}}; | ||
| grpc::ByteBuffer byteBuffer(slices, 3); | ||
| auto bufferInstance = Common::makeBufferInstance(byteBuffer); | ||
| EXPECT_EQ(bufferInstance->toString(), "test this"); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, MakeByteBuffer1) { | ||
| auto buffer = std::make_unique<Buffer::OwnedImpl>(); | ||
| buffer->add("test", 4); | ||
| auto byteBuffer = Common::makeByteBuffer(std::move(buffer)); | ||
| std::vector<grpc::Slice> slices; | ||
| byteBuffer.Dump(&slices); | ||
| std::string str; | ||
| for (auto& s : slices) { | ||
| str.append(std::string(reinterpret_cast<const char*>(s.begin()), s.size())); | ||
| } | ||
| EXPECT_EQ(str, "test"); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, MakeByteBuffer3) { | ||
|
jplevyak marked this conversation as resolved.
Outdated
|
||
| auto buffer = std::make_unique<Buffer::OwnedImpl>(); | ||
| buffer->add("test", 4); | ||
| buffer->add(" ", 1); | ||
| buffer->add("this", 4); | ||
| auto byteBuffer = Common::makeByteBuffer(std::move(buffer)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add in some ASSERTs here and also in this |
||
| std::vector<grpc::Slice> slices; | ||
| byteBuffer.Dump(&slices); | ||
| std::string str; | ||
| for (auto& s : slices) { | ||
| str.append(std::string(reinterpret_cast<const char*>(s.begin()), s.size())); | ||
| } | ||
| EXPECT_EQ(str, "test this"); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, ByteBufferInstanceRoundTrip) { | ||
| grpc::Slice slices[3] = {{"test"}, {" "}, {"this"}}; | ||
| grpc::ByteBuffer byteBuffer1(slices, 3); | ||
| auto bufferInstance1 = Common::makeBufferInstance(byteBuffer1); | ||
| auto byteBuffer2 = Common::makeByteBuffer(std::move(bufferInstance1)); | ||
| auto bufferInstance2 = Common::makeBufferInstance(byteBuffer2); | ||
| EXPECT_EQ(bufferInstance2->toString(), "test this"); | ||
| } | ||
|
|
||
| TEST(GrpcCommonTest, PreependGrpcFrameHeader) { | ||
| auto buffer = std::make_unique<Buffer::OwnedImpl>(); | ||
| buffer->add("test", 4); | ||
| char expected_header[5]; | ||
| expected_header[0] = 0; // flags | ||
| const uint32_t nsize = htonl(4); | ||
| std::memcpy(&expected_header[1], reinterpret_cast<const void*>(&nsize), sizeof(uint32_t)); | ||
| std::string header_string(expected_header, 5); | ||
| Common::PrependGrpcFrameHeader(buffer.get()); | ||
| EXPECT_EQ(buffer->toString(), header_string + "test"); | ||
| } | ||
|
|
||
| } // namespace Grpc | ||
| } // namespace Envoy | ||
Uh oh!
There was an error while loading. Please reload this page.