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
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
ee7cb0d
Add config for behavior on HTTP invalid message to HTTP1 options. Als…
rulex123 Jul 3, 2020
b04b98e
Correct typo
rulex123 Jul 3, 2020
46768be
Shadow API
rulex123 Jul 3, 2020
d1bf85d
Extend codec to return behavior on invalid HTTP messaging
rulex123 Jul 9, 2020
2d7e93d
Update API documentation
rulex123 Jul 9, 2020
86031f0
Add TODO
rulex123 Jul 9, 2020
397e369
Add utility function that determines behavior on invalid HTTP messagi…
rulex123 Jul 9, 2020
c139ff1
Format code
rulex123 Jul 9, 2020
2d6cac8
Fix format
rulex123 Jul 13, 2020
d30e854
Add const
rulex123 Jul 17, 2020
aa421fd
Plug logic into HCM
rulex123 Jul 17, 2020
9718246
Format
rulex123 Jul 17, 2020
cc33ccf
Add unit test for HCM logic
rulex123 Jul 19, 2020
cab6751
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Jul 19, 2020
00814d2
Add unit tests for HCM
rulex123 Jul 20, 2020
7f61bdf
Update API to remove hide-implementation annotation
rulex123 Jul 20, 2020
908bdfc
Remove changes from v2 API
rulex123 Jul 20, 2020
e5239e8
Code format headaches
rulex123 Jul 22, 2020
4b2f72a
Revisit API docs
rulex123 Jul 22, 2020
922bb6f
Add unit test for codec HTTP/1.1
rulex123 Jul 24, 2020
9dec35a
Add unit test for codec HTTP/2
rulex123 Jul 24, 2020
5fb9be3
Integration tests for HTTP/1.1
rulex123 Jul 27, 2020
83cbadf
Integration test
rulex123 Jul 27, 2020
c9c55bc
Formatting, again
rulex123 Jul 27, 2020
a31323f
HTTP/3 stub out method for response encoder
rulex123 Jul 28, 2020
354c08b
Update HCM API docs and release notes
rulex123 Jul 29, 2020
07c3ee5
Address code review feedback
rulex123 Jul 31, 2020
5e1b95e
Move code from utility class to anon namespace
rulex123 Jul 31, 2020
eec3bb2
Remove TODO
rulex123 Jul 31, 2020
f1787af
Fix typo
rulex123 Jul 31, 2020
2fe6fbc
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Jul 31, 2020
447b719
Fix CI
rulex123 Aug 2, 2020
c1d456a
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Aug 3, 2020
025dbc4
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Aug 12, 2020
30154d7
Address code review feedback
rulex123 Aug 12, 2020
f99724d
Fix formatting
rulex123 Aug 12, 2020
a35bada
More code review feedback
rulex123 Aug 17, 2020
b037f64
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Aug 24, 2020
16414a0
Have logic that determines stream error on codec instantiation
rulex123 Aug 26, 2020
4bb2a86
Unit test for init logic of stream_error
rulex123 Aug 26, 2020
1410972
Update doc
rulex123 Aug 26, 2020
5e63612
Address feedback and fix CI
rulex123 Aug 27, 2020
85e57c4
Address feedback
rulex123 Aug 28, 2020
3dd2c3d
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Aug 28, 2020
464a5c7
Fix unit test
rulex123 Aug 28, 2020
e663b45
Fix test
rulex123 Aug 28, 2020
d92640d
Merge branch 'master' into behavior-on-invalid-http-message
rulex123 Sep 1, 2020
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
7 changes: 6 additions & 1 deletion api/envoy/config/core/v3/protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ message HttpProtocolOptions {
HeadersWithUnderscoresAction headers_with_underscores_action = 5;
}

// [#next-free-field: 6]
// [#next-free-field: 7]
message Http1ProtocolOptions {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.core.Http1ProtocolOptions";
Expand Down Expand Up @@ -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.

// HTTP/1.1 connections upon receiving an invalid HTTP message. 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>`
google.protobuf.BoolValue override_stream_error_on_invalid_http_message = 6;
}

// [#next-free-field: 15]
Expand Down
7 changes: 6 additions & 1 deletion api/envoy/config/core/v4alpha/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion generated_api_shadow/envoy/config/core/v3/protocol.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions include/envoy/http/codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ class ResponseEncoder : public virtual StreamEncoder {
* @param trailers supplies the trailers to encode.
*/
virtual void encodeTrailers(const ResponseTrailerMap& trailers) PURE;

/**
* Indicates whether invalid HTTP messaging should be handled with a stream error or a connection
* error.
Comment on lines +147 to +148
Copy link
Member

Choose a reason for hiding this comment

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

What does nullopt mean in this context?

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 would mean that the new HTTP/1.1 config stream_error_on_invalid_http_message hasn't been set, indicating that we need to fall back to the HCM behaviour.

Copy link
Member

Choose a reason for hiding this comment

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

OK makes sense can you clarify the comment? Also what does this mean in the case of upstream since there is no HCM?

*/
virtual absl::optional<bool> streamErrorOnInvalidHttpMessage() const PURE;
};

/**
Expand Down Expand Up @@ -388,6 +394,8 @@ struct Http1Settings {

// How header keys should be formatted when serializing HTTP/1.1 headers.
HeaderKeyFormat header_key_format_{HeaderKeyFormat::Default};

absl::optional<bool> 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.

comment, please!

Copy link
Member

Choose a reason for hiding this comment

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

nit: {} not needed

};

/**
Expand Down
9 changes: 5 additions & 4 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#include "common/http/conn_manager_impl.h"

#include <cstdint>
#include <functional>
#include <list>
Expand Down Expand Up @@ -27,6 +25,7 @@
#include "common/common/scope_tracker.h"
#include "common/common/utility.h"
#include "common/http/codes.h"
#include "common/http/conn_manager_impl.h"
#include "common/http/conn_manager_utility.h"
#include "common/http/exception.h"
#include "common/http/header_map_impl.h"
Expand Down Expand Up @@ -1524,8 +1523,10 @@ void ConnectionManagerImpl::ActiveStream::sendLocalReply(
// The BadRequest error code indicates there has been a messaging error.
if (Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.hcm_stream_error_on_invalid_message") &&
!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?

connection_manager_.config_.streamErrorOnInvalidHttpMessaging(),
response_encoder_->streamErrorOnInvalidHttpMessage())) {
state_.saw_connection_close_ = true;
}

Expand Down
17 changes: 13 additions & 4 deletions source/common/http/http1/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ class StreamEncoderImpl : public virtual StreamEncoder,
*/
class ResponseEncoderImpl : public StreamEncoderImpl, public ResponseEncoder {
public:
ResponseEncoderImpl(ConnectionImpl& connection, HeaderKeyFormatter* header_key_formatter)
: StreamEncoderImpl(connection, header_key_formatter) {}
ResponseEncoderImpl(ConnectionImpl& connection, HeaderKeyFormatter* header_key_formatter,
absl::optional<bool>& stream_error_on_invalid_http_message)
: StreamEncoderImpl(connection, header_key_formatter) {
stream_error_on_invalid_http_message_ = stream_error_on_invalid_http_message;
Copy link
Member

Choose a reason for hiding this comment

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

nit: initialize in initializer list, make constant member. Same below.

}

bool startedResponse() { return started_response_; }

Expand All @@ -133,8 +136,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() const override {
return stream_error_on_invalid_http_message_;
}

private:
bool started_response_{};
absl::optional<bool> stream_error_on_invalid_http_message_;
};

/**
Expand Down Expand Up @@ -432,8 +440,9 @@ class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
* An active HTTP/1.1 request.
*/
struct ActiveRequest {
ActiveRequest(ConnectionImpl& connection, HeaderKeyFormatter* header_key_formatter)
: response_encoder_(connection, header_key_formatter) {}
ActiveRequest(ServerConnectionImpl& connection, HeaderKeyFormatter* header_key_formatter)
: response_encoder_(connection, header_key_formatter,
connection.codec_settings_.stream_error_on_invalid_http_message_) {}

HeaderString request_url_;
RequestDecoder* request_decoder_{};
Expand Down
5 changes: 5 additions & 0 deletions source/common/http/http1/codec_impl_legacy.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,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() const override {
return stream_error_on_invalid_http_message_;
}

private:
bool started_response_{};
absl::optional<bool> stream_error_on_invalid_http_message_;
};

/**
Expand Down
9 changes: 8 additions & 1 deletion source/common/http/http2/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,9 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
*/
struct ServerStreamImpl : public StreamImpl, public ResponseEncoder {
ServerStreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
: StreamImpl(parent, buffer_limit), headers_or_trailers_(RequestHeaderMapImpl::create()) {}
: StreamImpl(parent, buffer_limit), headers_or_trailers_(RequestHeaderMapImpl::create()) {
stream_error_on_invalid_http_message_ = parent.stream_error_on_invalid_http_messaging_;
}

// StreamImpl
void submitHeaders(const std::vector<nghttp2_nv>& final_headers,
Expand Down Expand Up @@ -381,6 +383,11 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log

RequestDecoder* request_decoder_{};
absl::variant<RequestHeaderMapPtr, RequestTrailerMapPtr> headers_or_trailers_;
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.

}
};

using ServerStreamImplPtr = std::unique_ptr<ServerStreamImpl>;
Expand Down
9 changes: 8 additions & 1 deletion source/common/http/http2/codec_impl_legacy.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,9 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
*/
struct ServerStreamImpl : public StreamImpl, public ResponseEncoder {
ServerStreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
: StreamImpl(parent, buffer_limit), headers_or_trailers_(RequestHeaderMapImpl::create()) {}
: StreamImpl(parent, buffer_limit), headers_or_trailers_(RequestHeaderMapImpl::create()) {
stream_error_on_invalid_http_message_ = parent.stream_error_on_invalid_http_messaging_;
}

// StreamImpl
void submitHeaders(const std::vector<nghttp2_nv>& final_headers,
Expand Down Expand Up @@ -382,6 +384,11 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log

RequestDecoder* request_decoder_{};
absl::variant<RequestHeaderMapPtr, RequestTrailerMapPtr> headers_or_trailers_;
absl::optional<bool> stream_error_on_invalid_http_message_;

absl::optional<bool> streamErrorOnInvalidHttpMessage() const override {
return stream_error_on_invalid_http_message_;
}
};

using ServerStreamImplPtr = std::unique_ptr<ServerStreamImpl>;
Expand Down
18 changes: 16 additions & 2 deletions source/common/http/utility.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#include "common/http/utility.h"

#include <cstdint>
#include <string>
#include <vector>
Expand All @@ -19,6 +17,7 @@
#include "common/http/header_map_impl.h"
#include "common/http/headers.h"
#include "common/http/message_impl.h"
#include "common/http/utility.h"
#include "common/network/utility.h"
#include "common/protobuf/utility.h"
#include "common/runtime/runtime_features.h"
Expand Down Expand Up @@ -410,9 +409,24 @@ Utility::parseHttp1Settings(const envoy::config::core::v3::Http1ProtocolOptions&
ret.header_key_format_ = Http1Settings::HeaderKeyFormat::Default;
}

if (config.has_override_stream_error_on_invalid_http_message()) {
ret.stream_error_on_invalid_http_message_ =
config.override_stream_error_on_invalid_http_message().value();
}

return ret;
}

bool Utility::streamErrorOnInvalidHttpMessage(
bool hcm_stream_error_on_invalid_http_message,
const absl::optional<bool>& override_stream_error_on_invalid_http_message) {
if (override_stream_error_on_invalid_http_message.has_value()) {
return override_stream_error_on_invalid_http_message.value();
} else {
return hcm_stream_error_on_invalid_http_message;
}
}

void Utility::sendLocalReply(const bool& is_reset, StreamDecoderFilterCallbacks& callbacks,
const LocalReplyData& local_reply_data) {
sendLocalReply(
Expand Down
4 changes: 4 additions & 0 deletions source/common/http/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ bool isWebSocketUpgradeRequest(const RequestHeaderMap& headers);
*/
Http1Settings parseHttp1Settings(const envoy::config::core::v3::Http1ProtocolOptions& config);

bool streamErrorOnInvalidHttpMessage(
const bool hcm_stream_error_on_invalid_http_message,
const absl::optional<bool>& override_stream_error_on_invalid_http_message);

struct EncodeFunctions {
// Function to rewrite locally generated response.
std::function<void(ResponseHeaderMap& response_headers, Code& code, std::string& body,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#include "extensions/filters/network/http_connection_manager/config.h"

#include <chrono>
#include <memory>
#include <string>
Expand Down Expand Up @@ -35,6 +33,8 @@
#include "common/tracing/http_tracer_config_impl.h"
#include "common/tracing/http_tracer_manager_impl.h"

#include "extensions/filters/network/http_connection_manager/config.h"

namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
Expand Down
Loading