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

http: stream error on invalid HTTP messaging for HTTP/1 #11884

Merged

Conversation

rulex123
Copy link
Contributor

@rulex123 rulex123 commented Jul 3, 2020

In this PR:

Risk Level: high (HCM changes)
Testing: new unit tests and integration tests
Docs Changes: n/a
Release Notes: updated
Runtime guard: envoy.reloadable_features.hcm_stream_error_on_invalid_message (already introduced by #11748)

@repokitteh-read-only
Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to (api/envoy[\w/]*/(v1alpha\d?|v1|v2alpha\d?|v2))|(api/envoy/type/(matcher/)?\w+.proto).
CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/envoy/.
CC @envoyproxy/api-watchers: FYI only for changes made to api/envoy/.

🐱

Caused by: #11884 was opened by rulex123.

see: more, trace.

@alyssawilk alyssawilk self-assigned this Jul 6, 2020
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.

Yep, good start! Next you'll want to make your new function available from the Http Connection Manager, since it only has the abstract APIs. You could also start on the codec tests paralleling mine for HTTP/2 for which value takes precedence. right now you can obviously only test yours being on and off, but once my PR lands we can do the full 4x combinations.

@@ -142,6 +142,10 @@ message Http1ProtocolOptions {
// - Not a response to a HEAD request.
// - The content length header is not present.
bool enable_trailers = 5;

// [#not-implemented-hide:]
// Overrides envoy's behavior on invalid HTTP messaging configured in the hcm
Copy link
Contributor

Choose a reason for hiding this comment

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

you'll want to ref link to the actual hcm field - you can crib that notation from my PR as well :-)

@@ -128,8 +131,13 @@ class ResponseEncoderImpl : public StreamEncoderImpl, public ResponseEncoder {
void encodeHeaders(const ResponseHeaderMap& headers, bool end_stream) override;
void encodeTrailers(const ResponseTrailerMap& trailers) override { encodeTrailersBase(trailers); }

absl::optional<bool> streamErrorOnInvalidHTTPMessage() override {
Copy link
Contributor

Choose a reason for hiding this comment

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

HTTP -> Http ?

rulex123 added 9 commits July 13, 2020 17:26
…o, extend codec interface so that hcm can pull this new option from the codec.

Signed-off-by: Erica Manno <[email protected]>
Signed-off-by: Erica Manno <[email protected]>
Signed-off-by: Erica Manno <[email protected]>
Signed-off-by: Erica Manno <[email protected]>
Signed-off-by: Erica Manno <[email protected]>
Signed-off-by: Erica Manno <[email protected]>
Signed-off-by: Erica Manno <[email protected]>
@rulex123 rulex123 force-pushed the behavior-on-invalid-http-message branch from 26b7a81 to 2d6cac8 Compare July 13, 2020 16:35
@rulex123 rulex123 changed the title http: config for behaviour on invalid HTTP message for HTTP/1.1 http: stream error on invalid HTTP messaging for HTTP/1 Jul 20, 2020
@rulex123 rulex123 marked this pull request as ready for review July 20, 2020 15:02
@rulex123 rulex123 requested a review from mattklein123 as a code owner July 20, 2020 15:02
@rulex123
Copy link
Contributor Author

@alyssawilk I think this is ready for another look.

One question though: do we want to update the old code path for sending errors from the codec as well (

void ServerConnectionImpl::sendProtocolErrorOld(absl::string_view details) {
) to terminate the connection or not based on the new config param?

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!

I think your PR needs a format check, and it'd be good to have an integration test Luckily I think Piotr wrote most of one for you
DownstreamProtocolIntegrationTest,.InvalidContentLengthAllowed
tests that for http/2 the config override results in stream reset, for HTTP/1 it results in disconnect.

You will need to change it to set the HTTP/1.1 override as well, and perhaps change the invalid message from one with invalid content length (which will trigger before the headers are complete) to some other messaging error - maybe remove the host?

@@ -157,6 +157,11 @@ message Http1ProtocolOptions {
// - Not a response to a HEAD request.
// - The content length header is not present.
bool enable_trailers = 5;

// Allows invalid HTTP messaging. When this option is disabled (default), then Envoy will terminate
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, when this option is disabled, Envoy will terminate the connection, but I think you should remove reference to default behavior, since the default behavior is actually to ignore this field and use the HCM value.

!connection_manager_.config_.streamErrorOnInvalidHttpMessaging() &&
code == Http::Code::BadRequest && connection_manager_.codec_->protocol() < Protocol::Http2) {
code == Http::Code::BadRequest && connection_manager_.codec_->protocol() < Protocol::Http2 &&
!Utility::streamErrorOnInvalidHttpMessage(
Copy link
Contributor

Choose a reason for hiding this comment

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

If this function is only used in one place, I'd be mildly inclined to make this utility in an unnamed namespace in the cc file, and unit test the HCM by setting explicit configs. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

ping?

absl::optional<bool> stream_error_on_invalid_http_message_;

absl::optional<bool> streamErrorOnInvalidHttpMessage() const override {
return stream_error_on_invalid_http_message_;
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 need to latch this, or can we just return parent_.stream_error_on_invalid_http_messaging_ here?

Also let's make sure this and the HTTP/1.1 function have unit tests, even if they're pretty trivial.

Signed-off-by: Erica Manno <[email protected]>
mattklein123
mattklein123 previously approved these changes Aug 17, 2020
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.

Thanks this LGTM modulo the comment about clarifying what is supported downstream and upstream. @alyssawilk is back from vacation so I will defer to her for final review. Thank you!

// Behaviour on invalid HTTP messaging:
// - if set and true, the HTTP/1.1 connection is left open (where possible)
// - if set and false, the HTTP/1.1 connection is terminated
// - if not set, fall back to the HCM behaviour on invalid HTTP messaging
Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think it would be good to call this out more in the comments and the proto documentation. The proto is a generic message that is used for both upstream and downstream, so I think it would be good to clarify that it only applies right now for downstream?

@mattklein123 mattklein123 removed their assignment Aug 17, 2020
@rulex123
Copy link
Contributor Author

@alyssawilk ping: any further feedback beside what Matt said?

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 ugh I'm so sorry, I thought I sent these comments out last week! thanks for the ping and sorry for the lag and late cross-codec-behavior comment

// open where possible.
// If set, this overrides any HCM :ref:`stream_error_on_invalid_http_messaging
// <envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_error_on_invalid_http_message>`;
// if not set, this is overridden by any HCM :ref:`stream_error_on_invalid_http_messaging
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 you can leave out the "if not set" because the sentence above makes it clear that otherwise the HCM value is used.

@rulex123
Copy link
Contributor Author

@alyssawilk no worries! I have reworked the code a bit to take into account your cross-codec-behavior comment. Please take another look when you get a chance :-)

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.

Awesome, thanks for your patience with the last minute thought! sending a few more ideas your way

@@ -164,7 +164,7 @@ message Http1ProtocolOptions {
// open where possible.
// If set, this overrides any HCM :ref:`stream_error_on_invalid_http_messaging
// <envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.stream_error_on_invalid_http_message>`;
// if not set, this is overridden by any HCM :ref:`stream_error_on_invalid_http_messaging
Copy link
Contributor

Choose a reason for hiding this comment

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

sorry, for any ocnfusion, I was suggesting taking out this line and following.

@@ -147,7 +147,7 @@ class ResponseEncoder : public virtual StreamEncoder {
* Indicates whether invalid HTTP messaging should be handled with a stream error or a connection
* error; if value is missing, then HCM drives behaviour on invalid HTTP messaging.
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 to remove "if value is missing, then HCM drives behaviour on invalid HTTP messaging."

Utility::parseHttp1Settings(const envoy::config::core::v3::Http1ProtocolOptions& config,
bool hcm_stream_error_set,
const Protobuf::BoolValue& hcm_stream_error) {
Http1Settings ret = parseHttp1Settings(config);
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 function just return the override_stream_error if set, and if not returns the HCM value no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, isn't it the case that when this function is called the HCM value hasn't been initialised to its default value yet (in case of no explicit configuration)? See

stream_error_on_invalid_http_messaging_(
PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, stream_error_on_invalid_http_message, false)),

I guess we could move that initialisation so that it happens earlier, and then simplify this function as per your comment. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

ah, I see what your concern is, but I believe (and you can unit test) that the default value for BoolValue is false, and the default for stream_error_on_invalid_http_messaging_ is false, so you should just be able to set the default to config.override_stream_error_on_invalid_http_message().value() even if it's not set.

Signed-off-by: Erica Manno <[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.

Looks great - just a few final comments :-)

Utility::parseHttp1Settings(const envoy::config::core::v3::Http1ProtocolOptions& config,
bool hcm_stream_error_set,
const Protobuf::BoolValue& hcm_stream_error) {
Http1Settings ret = parseHttp1Settings(config);
Copy link
Contributor

Choose a reason for hiding this comment

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

ah, I see what your concern is, but I believe (and you can unit test) that the default value for BoolValue is false, and the default for stream_error_on_invalid_http_messaging_ is false, so you should just be able to set the default to config.override_stream_error_on_invalid_http_message().value() even if it's not set.

@@ -2131,6 +2131,63 @@ TEST_F(HttpConnectionManagerImplTest, TestAccessLogWithInvalidRequest) {
conn_manager_->onData(fake_input, false);
}

class StreamErrorOnInvalidHttpMessageTest : public HttpConnectionManagerImplTest {
public:
void sendInvalidRequestAndVerifyConnectionState(bool codec_stream_error,
Copy link
Contributor

Choose a reason for hiding this comment

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

Since these are always opposite I think you can just have one boolean, stream_error_in_invalid_messaging and make sure that the connection is terminated if !stream_error

// HTTP message (missing :host header)
TEST_P(DownstreamProtocolIntegrationTest,
Http1ConnectionIsLeftOpenIfHCMStreamErrorIsFalseAndOverrideIsTrue) {
if (downstreamProtocol() == Http::CodecClient::Type::HTTP2) {
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 you can move these to
TEST_P(IntegrationTest,...)
and then it won't kick off a test run for downstream HTTP/2

@rulex123
Copy link
Contributor Author

rulex123 commented Sep 1, 2020

@alyssawilk ping: any further comments or feedback? Would love to wrap this up this week if possible :-)

@alyssawilk
Copy link
Contributor

over to Matt for API sign off.

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.

Thank you, especially for the lengthy review cycle. This stuff is tricky to get right!

@repokitteh-read-only repokitteh-read-only bot removed the api label Sep 1, 2020
@mattklein123 mattklein123 merged commit 52ec66f into envoyproxy:master Sep 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants