Skip to content
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

Add Metadata to codec #4796

Merged
merged 22 commits into from
Nov 8, 2018
Merged

Conversation

soya3129
Copy link
Contributor

Signed-off-by: Yang Song [email protected]

Description: Add MetadataEncoder and MetadataDecoder to codec. I also corrected some of the design decisions in MetadataEnocder and MetadataDecoder so that they are more suitable for codec. Those corrections includes: 1. let StreamImpl owns the decoded metadata, not the decoder. 2. Remove stream_id field in decoder and encoder since stream id is stored in StreamImpl. 3. Remove set/getMaxMetadataSize(), because nghttp2 uses default max frame size.

This is the second step towards supporting metadata in envoy. The next step would be add filters to add/delete/modify metadata.

Risk Level: Low. Not used.
Testing: Unit test.
Docs Changes:
Release Notes:
#2394

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Looking good!

* Encode METADATA.
* @param metadata_map is the METADATA to encode.
*/
virtual void encodeMetadata(const MetadataMap& metadata_map) PURE;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a good PR to start landing docs with, even if we have them tagged as work in progress.
Let's talk about when you can encodeMetadata, how often you can encode it, when it will be passed up to filters (we can edit the docs if they change when that lands) etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Still working on the doc.

@@ -303,6 +303,7 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
void decodeHeaders(HeaderMapPtr&& headers, bool end_stream) override;
void decodeData(Buffer::Instance& data, bool end_stream) override;
void decodeTrailers(HeaderMapPtr&& trailers) override;
void decodeMetadata(MetadataMap&) override { NOT_REACHED_GCOVR_EXCL_LINE; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we think there should be anything done if an HTTP/1.1 codec is asked to encode metadata? If nothing else maybe error reporting stats so folks know their metadata is being swallowed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call! I added an error log in Http1::StreamEncoderImpl::encodeHeader().

@@ -48,8 +48,8 @@ template <typename T> static T* remove_const(const void* object) {
}

ConnectionImpl::StreamImpl::StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
: parent_(parent), headers_(new HeaderMapImpl()), local_end_stream_sent_(false),
remote_end_stream_(false), data_deferred_(false),
: parent_(parent), headers_(new HeaderMapImpl()), metadata_map_(new MetadataMap()),
Copy link
Contributor

Choose a reason for hiding this comment

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

What do we think about doing lazy creation here? I think it's worth the complexity to not create a map for the vast majority of users who don't need this functionality.

Also if there's one metadata_map_ (at a time) per stream and one decoder per stream I'm not sure I follow why you moved ownership out of the decoder.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right! Moved metadata_map_ back to decoder. I thought it would be consistent to let StreamImpl hold metadata since it holds headers. But it is definitely not necessary.

encoder.releasePayload(size_to_copy);

// Keep submitting extension frames if there is payload left in the encoder.
if (encoder.hasNextFrame()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

so when the caller calls encodeMetadata with a large frame, encodeMetadata calls submitMetadata, submitMetadata results in nghttp2 calling packMetadata, which may result in another submitMetadata?

Any chance we can remove the recursion by moving where we check for hasNextFrame and calling submitMetadata multiple times instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right. The old code let packMetadata call submitMetadata until there is no metadata left. I can see it is very hard to understand. Changed according to the suggestion.

@@ -288,6 +288,7 @@ class Filter : Logger::Loggable<Logger::Id::router>,
void decodeHeaders(Http::HeaderMapPtr&& headers, bool end_stream) override;
void decodeData(Buffer::Instance& data, bool end_stream) override;
void decodeTrailers(Http::HeaderMapPtr&& trailers) override;
void decodeMetadata(Http::MetadataMap&) override { NOT_REACHED_GCOVR_EXCL_LINE; }
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we're going to need to implement something here. Maybe a TODO instead of marking it as not reached?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -280,6 +281,7 @@ class GrpcHealthCheckerImpl : public HealthCheckerImplBase {
void decodeHeaders(Http::HeaderMapPtr&& headers, bool end_stream) override;
void decodeData(Buffer::Instance&, bool end_stream) override;
void decodeTrailers(Http::HeaderMapPtr&&) override;
void decodeMetadata(Http::MetadataMap&) override {}
Copy link
Contributor

Choose a reason for hiding this comment

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

These ones we're probably never going to really use, but we could unit test the functions to avoid a coverage decrease

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea! Test added.

@@ -22,10 +21,13 @@ namespace Http2 {
const uint8_t METADATA_FRAME_TYPE = 0x4d;
const uint8_t END_METADATA_FLAG = 0x4;

// NGHTTP2_MAX_PAYLOADLEN in nghttp2.
const uint64_t METADATA_MAX_PAYLOAD_SIZE = 16384;
Copy link
Contributor

Choose a reason for hiding this comment

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

As a sanity check, if nghttp2 changed their NGHTTP2_MAX_PAYLOADLEN to be lower, they'd reject large data chunks and your large metadata frame test would fail when we imported the change, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great question! I increase METADATA_MAX_PAYLOAD_SIZE to 20000, and the test fails at:
source/common/http/http2/codec_impl.cc:623
ASSERT(len >= size_to_copy);

Added a comment to check NGHTTP2_MAX_PAYLOADLEN in case of the assert failure.

Copy link
Member

Choose a reason for hiding this comment

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

Make this as constexpr (perhaps for all constants), and do static_assert against NGHTTP2_MAX_PAYLOADLEN? It will fail at compile time then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a good idea! But I don't think nghttp2 exposes NGHTTP2_MAX_PAYLOADLEN. I changed const to constexpr. Thanks!

@soya3129 soya3129 changed the title Metadata codec new Add Metadata to codec Oct 23, 2018
@soya3129
Copy link
Contributor Author

Thanks for the review! I am still working on the doc part. Will update the PR when the doc is ready.

I changed two if conditions to assert since the if condition should not happen, and it increases the test coverage. I am having trouble making tests hit the if condition in onMetadataReceived():
if (!stream) {
return 0
}
It can't be changed to ASSERT in case envoy receives metadata with invalid stream id. I also notice the existing function saveHeader() function has the same problem. I will leave it for now if there is no objection.

Yang Song added 4 commits October 24, 2018 15:29
Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Oh argh, I had draft comments and failed to actually send them out. So sorry for review lag!

int result = submitMetadata();
ASSERT(result == 0);
parent_.sendPendingFrames();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have an ASSERT that hasNextFrame is false, or if failure is possible should there be error handling?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea! Done.

// Keep submitting extension frames if there is payload left in the encoder.
while (metadata_encoder_->hasNextFrame() && count++ <= frame_count) {
int result = submitMetadata();
ASSERT(result == 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at nghttp2_submit_extension the failure modes are specifying a non-registered type (I agree shouldn't happen if metadata is on), insufficient memory (I agree shouldn't happen) and the pack_extension callback not being set (shouldn't happen if metadata is on).

But then it occurred to me (which I should have caught on first pass - sorry!) we're adding H2 Metadata support by default. I think we should probably make this an opt-in feature and add it to the Http2ProtocolOptions. I'd hope you could crib where we added allow_connect support for e2e config examples when you get there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also it does strike me as a bit weird we have a return which we assert is always 0. I'd be inclined to keep the assert lower down and keep the function void, or if we think it might legit fail, do error handling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made metadata as an opt-in feature (will add to Http2ProtocolOptions later. TODO added). Made submitMetadata a void function, and added assert outside the loop. Thanks for the suggestions!

@@ -37,6 +37,9 @@ class StreamEncoderImpl : public StreamEncoder,
void encodeHeaders(const HeaderMap& headers, bool end_stream) override;
void encodeData(Buffer::Instance& data, bool end_stream) override;
void encodeTrailers(const HeaderMap& trailers) override;
void encodeMetadata(const MetadataMap&) override {
ENVOY_LOG(error, "HTTP/1.1 doesn't support METADATA. METADATA is ignored.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Unit test, just for coverage? :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, right! Done.

Copy link
Member

Choose a reason for hiding this comment

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

Personally I would just do NOT_IMPLEMENTED and have this crash but I don't feel strongly about it.

response_encoder_->encodeMetadata(metadata_map);
}

TEST_P(Http2CodecImplTest, LargeMetadataTest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we have examples of this, but could we maybe have a test of error conditions / bad data? I think you could tweak setupDefaultConnectionMocks to corrupt the data to result in some of the calls failing and make sure nghtttp2 ends up killing the connection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add a test with corrupted metadata. Verified exception is thrown.

Signed-off-by: Yang Song <[email protected]>
Signed-off-by: Yang Song <[email protected]>
Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Docs are a great addition - thanks for adding them!

information is represented by a string key-value pair. For example, users can
pass along RTT information associated with a stream using a key of "rtt info", and a value of
"100ms". In Envoy, we call this type of information metadata.
A stream can be associated with multiple metadatas, and the multiple metadatas
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the plural of metdata is metadata, so s/metadatas/metadata/ here and below?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see! Thanks for correcting my English!


### Limitation

For the ease of implementation and compatibility purpose, metadata will only be
Copy link
Contributor

Choose a reason for hiding this comment

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

For the ease of implementation and compatibility purpose -> For ease of implementation and compatibility purposes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Thanks!

errors or be ignored.

Envoy can consume or add metadata. But for one request (or response), we expect at most one of the
two operations to be performed.
Copy link
Contributor

Choose a reason for hiding this comment

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

You think? I'm thinking we had a 3 layer Envoy set up, the middle Envoy could consume metadata from upstream and send metadata downstream.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I didn't think the limitation through. I would like to avoid the case where metadata added by one filter is consumed by another filter on the same stream. But now I think how to use the filters is up to the users. Removed this limitation.


Envoy provides the functionality to proxy, process and add metadatas.

## Proxy metadata
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's have the headers consistent
Proxying meadata
Consuming metadata
Inserting metadata

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are better headers! Fixed.


(To be implemented)

If Envoy needs to take actions when a metadata is received, users should
Copy link
Contributor

Choose a reason for hiding this comment

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

s/a metadata/metadata/
or "a metadata frame"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Thanks!

it untouched.

If Envoy needs to parse a metadata sent on a response from upstream to downstream, a
StreamEncodeFilter should be created. The interface to override is
Copy link
Contributor

Choose a reason for hiding this comment

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

Encoder

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Fixed.

The METADATA frame payload is not subject to HTTP/2 flow control, but the size
of the payload is bounded by the maximum frame size negotiated in SETTINGS.
There are no restrictions on the set of octets that may be used in keys or values.
TODO(soya3129): decide if we allow METADATA frame to terminate a stream.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd say no for now. Folks can send 0 length data frame.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

+1!

If not specified, all the metadata received by Envoy is proxied to the next hop
unmodified. Note that, we do not guarantee the same frame order will be preserved from
hop by hop. That is, metadata from upstream at the beginning of a stream can be
received by the downstream at the end of the stream.
Copy link
Contributor

Choose a reason for hiding this comment

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

So I agree that we don't guarantee anything with respect to body order (and doccing that up is an excellent idea!) but I think we can and probably should make sure we don't mess with end stream. For example if we forward end-stream ahead of metadata in a response headed downstream that stream may be torn down before the metadata is received. I think we have to make sure that if we've read metadata we defer end stream until metadata is flushed through the filter chain. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I totally agree! I will follow the suggestion in the implementation.

Signed-off-by: Yang Song <[email protected]>
@soya3129
Copy link
Contributor Author

Please hold the review until I fix the check failure. Sorry about the noisy notification! I will address the check failure and the documentation comments together.

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Cool, getting there!

I think I'm pretty good on the code/test front (may do one more pass on the docs) so may be worth @mattklein123 doing a pass if he has time.

@@ -863,8 +870,10 @@ ConnectionImpl::Http2Options::Http2Options(const Http2Settings& http2_settings)
if (http2_settings.hpack_table_size_ != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
nghttp2_option_set_max_deflate_dynamic_table_size(options_, http2_settings.hpack_table_size_);
}

nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE);
// TODO(soya3129): change to ENABLE_METADATA when it's available.
Copy link
Contributor

Choose a reason for hiding this comment

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

AFIK nghttp2 won't take this until we make it an rfc, which is probably 1-2 years out given we haven't started anything IETF-side and it takes a while. If it were me I wouldn't TODO this (I did it for CONNECT as it was about 3 months from landing) but it's totally your call :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it! Removed.

class Http2CodecImplTestEnableMetadata : public Http2CodecImplTest {
public:
Http2CodecImplTestEnableMetadata() : Http2CodecImplTest(true) {}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

super nitty - I'd rather defer client_http2settings_ initialization to initialize() than subclass to set a boolean, but I'll leave it up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see! Done. It's definitely cleaner.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

In general LGTM, a few small comments. Thank you!

* Called with decoded METADATA.
* @param decoded METADATA.
*/
virtual void decodeMetadata(MetadataMap& metadata_map) PURE;
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to pass this as a unique_ptr with move semantics so that the callee can take ownership if they desire? I suspect this is what you want, much like the other header decode APIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed as suggested. Thanks!

@@ -37,6 +37,9 @@ class StreamEncoderImpl : public StreamEncoder,
void encodeHeaders(const HeaderMap& headers, bool end_stream) override;
void encodeData(Buffer::Instance& data, bool end_stream) override;
void encodeTrailers(const HeaderMap& trailers) override;
void encodeMetadata(const MetadataMap&) override {
ENVOY_LOG(error, "HTTP/1.1 doesn't support METADATA. METADATA is ignored.");
Copy link
Member

Choose a reason for hiding this comment

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

Personally I would just do NOT_IMPLEMENTED and have this crash but I don't feel strongly about it.


void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMap& metadata_map) {
if (!parent_.allow_metadata_) {
ENVOY_CONN_LOG(error, "Connection doesn't allow metadata", parent_.connection_);
Copy link
Member

Choose a reason for hiding this comment

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

When do we envision this error happening? I'm mainly wondering if this should be a stat? Or should it just crash? Or just be a debug statement? I'm leery of error logs being useful to people.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! I thought it could stop encoding metadata in case metadata is not allowed. Now I realize the check should be done in ActiveStream or UpstreamRequest. Changed to ASSERT.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct my comments above: we don't need to check allow_metadata_ not because of the reasons I stated. If allow_metadata_ is false, nghttp2 will not pass metadata to codec. We don't need to check it in envoy code anywhere. Sorry about the wrong information!

@@ -194,6 +215,15 @@ void ConnectionImpl::StreamImpl::submitTrailers(const HeaderMap& trailers) {
ASSERT(rc == 0);
}

void ConnectionImpl::StreamImpl::submitMetadata() {
ASSERT(stream_id_ > -1);
Copy link
Member

Choose a reason for hiding this comment

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

We should never encode metadata on stream 0/connection either, right? I think this can be > 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, we don't have connection usage case for now. Changed to > 0.

MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
if (metadata_decoder_ == nullptr) {
auto cb =
std::bind(&ConnectionImpl::StreamImpl::onMetadataDecoded, this, std::placeholders::_1);
Copy link
Member

Choose a reason for hiding this comment

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

nit: I find bind hard to read, can you just pass an inline lambda / std::function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to inline lambda.

// NGHTTP2_MAX_PAYLOADLEN is consistent with METADATA_MAX_PAYLOAD_SIZE.
ASSERT(len >= size_to_copy);

Buffer::OwnedImpl& p = reinterpret_cast<Buffer::OwnedImpl&>(encoder.payload());
Copy link
Member

Choose a reason for hiding this comment

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

Doing this type of cast is a bit of an anti-pattern. Why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Fixed.

@@ -210,6 +221,8 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
[this]() -> void { this->pendingSendBufferLowWatermark(); },
[this]() -> void { this->pendingSendBufferHighWatermark(); }};
HeaderMapPtr pending_trailers_;
std::unique_ptr<MetadataDecoder> metadata_decoder_{nullptr};
Copy link
Member

Choose a reason for hiding this comment

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

nit: {nullptr} not needed. Default init will do this. Same next line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@mattklein123
Copy link
Member

Marking as waiting.

/wait

Signed-off-by: Yang Song <[email protected]>
@soya3129
Copy link
Contributor Author

soya3129 commented Nov 4, 2018

Very sorry about delay!

Yang Song added 3 commits November 4, 2018 15:23
Signed-off-by: Yang Song <[email protected]>
Signed-off-by: Yang Song <[email protected]>
Signed-off-by: Yang Song <[email protected]>
Signed-off-by: Yang Song <[email protected]>
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Generally LGTM. Will defer to @alyssawilk for the rest of the review. Thank you!

* Called with decoded METADATA.
* @param decoded METADATA.
*/
virtual void decodeMetadata(std::unique_ptr<MetadataMap> metadata_map) PURE;
Copy link
Member

Choose a reason for hiding this comment

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

I would pass as std::unique_ptr<MetadataMap>&& (see style doc)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@soya3129
Copy link
Contributor Author

soya3129 commented Nov 7, 2018

Thanks for the review!

Signed-off-by: Yang Song <[email protected]>
If Envoy needs to parse a metadata sent on a request from downstream to upstream, a
StreamDecodeFilter should be created. The interface to override is

FilterMetadataStatus StreamDecoderFilter::decodeMetadata(std::unique\_ptr\<MetadataMap\> metadata);
Copy link
Member

Choose a reason for hiding this comment

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

MetadataMapPtr&&

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Fixed.

@@ -58,18 +59,15 @@ class MetadataDecoder : Logger::Loggable<Logger::Id::http2> {
*/
bool decodeMetadataPayloadUsingNghttp2(bool end_metadata);

// Metadata that is currently under decoding.
std::unique_ptr<MetadataMap> metadata_map_;
Copy link
Member

Choose a reason for hiding this comment

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

nit: MetadataMapPtr

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, plus "is currently under decoding." -> "is currently being decoded."?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed the code and English. Thanks!

@@ -22,10 +21,13 @@ namespace Http2 {
const uint8_t METADATA_FRAME_TYPE = 0x4d;
const uint8_t END_METADATA_FLAG = 0x4;

// NGHTTP2_MAX_PAYLOADLEN in nghttp2.
const uint64_t METADATA_MAX_PAYLOAD_SIZE = 16384;
Copy link
Member

Choose a reason for hiding this comment

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

Make this as constexpr (perhaps for all constants), and do static_assert against NGHTTP2_MAX_PAYLOADLEN? It will fail at compile time then.

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Looking great! just a few more nits and you're good to go.

@@ -19,7 +20,10 @@ class MetadataEncoderDecoderTest_VerifyEncoderDecoderOnMultipleMetadataMaps_Test
// map of string key value pairs.
class MetadataDecoder : Logger::Loggable<Logger::Id::http2> {
public:
MetadataDecoder(uint64_t stream_id, MetadataCallback cb);
/**
* @param metadata_map is where to save the decoded metadata.
Copy link
Contributor

Choose a reason for hiding this comment

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

update comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Fixed.

@@ -58,18 +59,15 @@ class MetadataDecoder : Logger::Loggable<Logger::Id::http2> {
*/
bool decodeMetadataPayloadUsingNghttp2(bool end_metadata);

// Metadata that is currently under decoding.
std::unique_ptr<MetadataMap> metadata_map_;
Copy link
Contributor

Choose a reason for hiding this comment

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

+1, plus "is currently under decoding." -> "is currently being decoded."?

If not specified, all the metadata received by Envoy is proxied to the next hop
unmodified. Note that, we do not guarantee the same frame order will be preserved from
hop by hop. That is, metadata from upstream at the beginning of a stream can be
received by the downstream at the end of the stream.
Copy link
Contributor

Choose a reason for hiding this comment

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

Worth commenting somewhere that empty maps will not be serialized out, i.e. if you clear the data it will functionally result in metadata being removed for the next hop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea! Done.

Signed-off-by: Yang Song <[email protected]>
@soya3129
Copy link
Contributor Author

soya3129 commented Nov 8, 2018

Thanks for the reviews!

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

@lizan any final comments?

@alyssawilk alyssawilk merged commit 7b96648 into envoyproxy:master Nov 8, 2018
@soya3129
Copy link
Contributor Author

Cc Biren the metadata change.
@birenroy

fredlas pushed a commit to fredlas/envoy that referenced this pull request Mar 5, 2019
Description: Add MetadataEncoder and MetadataDecoder to codec. I also corrected some of the design decisions in MetadataEnocder and MetadataDecoder so that they are more suitable for codec. Those corrections includes: 1. let StreamImpl owns the decoded metadata, not the decoder. 2. Remove stream_id field in decoder and encoder since stream id is stored in StreamImpl. 3. Remove set/getMaxMetadataSize(), because nghttp2 uses default max frame size.

This is the second step towards supporting metadata in envoy. The next step would be add filters to add/delete/modify metadata.

Risk Level: Low. Not used.
Testing: Unit test.
Docs Changes:
Release Notes:
envoyproxy#2394

Signed-off-by: Yang Song <[email protected]>
Signed-off-by: Fred Douglas <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants