-
Notifications
You must be signed in to change notification settings - Fork 5.3k
response decoder: add new interfaces for life time tracking of response decoder #41048
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 all commits
ca29616
742ce65
7a6605b
a57534a
7d6c5e9
5a704e8
918841f
563f3e7
fdd17cf
417e117
ba8f0d3
97ef957
5f4575a
50708eb
74680c2
be6d7cb
850c93e
1a7b13b
242b503
6575c1b
4627f86
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 |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ | |
| #include "envoy/upstream/resource_manager.h" | ||
| #include "envoy/upstream/retry.h" | ||
|
|
||
| #include "source/common/http/response_decoder_impl_base.h" | ||
| #include "source/common/protobuf/protobuf.h" | ||
| #include "source/common/protobuf/utility.h" | ||
|
|
||
|
|
@@ -1490,7 +1491,7 @@ class GenericConnPool { | |
| * An API for the interactions the upstream stream needs to have with the downstream stream | ||
| * and/or router components | ||
| */ | ||
| class UpstreamToDownstream : public Http::ResponseDecoder, public Http::StreamCallbacks { | ||
| class UpstreamToDownstream : public Http::ResponseDecoderImplBase, public Http::StreamCallbacks { | ||
|
Contributor
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. why do we need to change the inheritance to ResponseDecoderImplBase?
Contributor
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. I think this is so we get an implementation of the new
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. ResponseDecoderImplBase implements the new interface getResponseDecoderHandle()
Contributor
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. sorry this was a comment i had left in before i read the full PR but forgot to remove :/ sorry about that! |
||
| public: | ||
| /** | ||
| * @return return the route for the downstream stream. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,24 +2,35 @@ | |
|
|
||
| #include "envoy/http/codec.h" | ||
|
|
||
| #include "source/common/http/response_decoder_impl_base.h" | ||
| #include "source/common/runtime/runtime_features.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Http { | ||
|
|
||
| /** | ||
| * Wrapper for ResponseDecoder that just forwards to an "inner" decoder. | ||
| */ | ||
| class ResponseDecoderWrapper : public ResponseDecoder { | ||
| class ResponseDecoderWrapper : public ResponseDecoderImplBase { | ||
| public: | ||
| // ResponseDecoder | ||
| void decode1xxHeaders(ResponseHeaderMapPtr&& headers) override { | ||
| inner_.decode1xxHeaders(std::move(headers)); | ||
| if (Http::ResponseDecoder* inner = getInnerDecoder()) { | ||
|
Contributor
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. nit: I leave it up to you if you think it helps readability, but you could refactor this into a function: then all of these functions would look like:
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. stack trace with lambda function is hard to read. I'd prefer inlining the if else block. |
||
| inner->decode1xxHeaders(std::move(headers)); | ||
| } else { | ||
| onInnerDecoderDead(); | ||
| } | ||
| } | ||
|
|
||
| void decodeHeaders(ResponseHeaderMapPtr&& headers, bool end_stream) override { | ||
| if (end_stream) { | ||
| onPreDecodeComplete(); | ||
| } | ||
| inner_.decodeHeaders(std::move(headers), end_stream); | ||
| if (Http::ResponseDecoder* inner = getInnerDecoder()) { | ||
| inner->decodeHeaders(std::move(headers), end_stream); | ||
| } else { | ||
| onInnerDecoderDead(); | ||
| } | ||
| if (end_stream) { | ||
| onDecodeComplete(); | ||
| } | ||
|
|
@@ -29,28 +40,45 @@ class ResponseDecoderWrapper : public ResponseDecoder { | |
| if (end_stream) { | ||
| onPreDecodeComplete(); | ||
| } | ||
| inner_.decodeData(data, end_stream); | ||
| if (Http::ResponseDecoder* inner = getInnerDecoder()) { | ||
| inner->decodeData(data, end_stream); | ||
| } else { | ||
| onInnerDecoderDead(); | ||
| } | ||
| if (end_stream) { | ||
| onDecodeComplete(); | ||
| } | ||
| } | ||
|
|
||
| void decodeTrailers(ResponseTrailerMapPtr&& trailers) override { | ||
| onPreDecodeComplete(); | ||
| inner_.decodeTrailers(std::move(trailers)); | ||
| if (Http::ResponseDecoder* inner = getInnerDecoder()) { | ||
| inner->decodeTrailers(std::move(trailers)); | ||
| } else { | ||
| onInnerDecoderDead(); | ||
| } | ||
| onDecodeComplete(); | ||
| } | ||
|
|
||
| void decodeMetadata(MetadataMapPtr&& metadata_map) override { | ||
| inner_.decodeMetadata(std::move(metadata_map)); | ||
| if (Http::ResponseDecoder* inner = getInnerDecoder()) { | ||
| inner->decodeMetadata(std::move(metadata_map)); | ||
| } else { | ||
| onInnerDecoderDead(); | ||
| } | ||
| } | ||
|
|
||
| void dumpState(std::ostream& os, int indent_level) const override { | ||
| inner_.dumpState(os, indent_level); | ||
| if (Http::ResponseDecoder* inner = getInnerDecoder()) { | ||
| inner->dumpState(os, indent_level); | ||
| } else { | ||
| onInnerDecoderDead(); | ||
| } | ||
| } | ||
|
|
||
| protected: | ||
| ResponseDecoderWrapper(ResponseDecoder& inner) : inner_(inner) {} | ||
| ResponseDecoderWrapper(ResponseDecoder& inner) | ||
| : inner_handle_(inner.createResponseDecoderHandle()), inner_(&inner) {} | ||
|
|
||
| /** | ||
| * Consumers of the wrapper generally want to know when a decode is complete. This is called | ||
|
|
@@ -59,7 +87,29 @@ class ResponseDecoderWrapper : public ResponseDecoder { | |
| virtual void onPreDecodeComplete() PURE; | ||
| virtual void onDecodeComplete() PURE; | ||
|
|
||
| ResponseDecoder& inner_; | ||
| ResponseDecoderHandlePtr inner_handle_; | ||
| Http::ResponseDecoder* inner_ = nullptr; | ||
|
|
||
| private: | ||
| Http::ResponseDecoder* getInnerDecoder() const { | ||
| if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.use_response_decoder_handle")) { | ||
| return inner_; | ||
| } | ||
| if (inner_handle_) { | ||
| if (OptRef<ResponseDecoder> inner = inner_handle_->get(); inner.has_value()) { | ||
| return &inner.value().get(); | ||
| } | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| void onInnerDecoderDead() const { | ||
| const std::string error_msg = "Wrapped decoder use after free detected."; | ||
| IS_ENVOY_BUG(error_msg); | ||
| RELEASE_ASSERT(!Runtime::runtimeFeatureEnabled( | ||
| "envoy.reloadable_features.abort_when_accessing_dead_decoder"), | ||
| error_msg); | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #pragma once | ||
|
|
||
| #include <memory> | ||
|
|
||
| #include "envoy/http/codec.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Http { | ||
|
|
||
| class ResponseDecoderHandleImpl : public ResponseDecoderHandle { | ||
| public: | ||
| ResponseDecoderHandleImpl(std::weak_ptr<bool> live_trackable, ResponseDecoder& decoder) | ||
| : live_trackable_(live_trackable), decoder_(decoder) {} | ||
|
|
||
| OptRef<ResponseDecoder> get() override { | ||
| if (live_trackable_.lock()) { | ||
| return decoder_; | ||
| } | ||
| return {}; | ||
| } | ||
|
|
||
| private: | ||
| std::weak_ptr<bool> live_trackable_; | ||
| ResponseDecoder& decoder_; | ||
| }; | ||
|
|
||
| class ResponseDecoderImplBase : public ResponseDecoder { | ||
| public: | ||
| ResponseDecoderImplBase() : live_trackable_(std::make_shared<bool>(true)) {} | ||
|
|
||
| ResponseDecoderHandlePtr createResponseDecoderHandle() override { | ||
| return std::make_unique<ResponseDecoderHandleImpl>(live_trackable_, *this); | ||
| } | ||
|
|
||
| private: | ||
| std::shared_ptr<bool> live_trackable_; | ||
|
Contributor
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. I see. So effectively, this Handle is basically a weak pointer to the decoder, but manually implemented? Looking at the CL in more detail, I wonder if we could do something a bit simpler. What if we added a new capability to the response decoder, which is a callback to run when the decoder is destroyed. We could then implement this decoder something like: Then we don't need explicit ref-counting in the base class, and we don't need the Decoder to create Handles (though maybe we would still want to?). WDYT?
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. ResponseDecoderHandleImpl also needs to notify the decoder_ about its own lifetime then. There would be mutual references and detaching upon destruction between the handle and the decoder. It's possible, but I feel shared_ptr is less error-prone. Happy to chat more offline about the approach. |
||
| }; | ||
|
|
||
| } // namespace Http | ||
| } // namespace Envoy | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a brief comment explaining the purpose of this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done