From dcf575aa40ce281398a15e175c41a66c2ade3a7e Mon Sep 17 00:00:00 2001 From: Snow Pettersen Date: Tue, 12 Jan 2021 09:19:21 -0500 Subject: [PATCH 1/4] http: support creating filters with match tree (#14430) Adds support for wrapping a HTTP filter with an ExtensionWithMatcher proto to create the filters with an associated match tree. Under the hood this makes use of a wrapper filter factory that manages creating the match tree and adding it to the FM alongside the associated filter. Also includes some code to register factories for input/actions, allowing them to be referenced in the proto configuration. Signed-off-by: Snow Pettersen --- api/envoy/type/matcher/v3/http_inputs.proto | 35 ++++ .../type/matcher/v4alpha/http_inputs.proto | 40 +++++ docs/root/api-v3/types/types.rst | 1 + .../envoy/type/matcher/v3/http_inputs.proto | 35 ++++ .../type/matcher/v4alpha/http_inputs.proto | 40 +++++ include/envoy/http/filter.h | 2 + include/envoy/matcher/BUILD | 1 + include/envoy/matcher/matcher.h | 5 +- source/common/http/BUILD | 3 + source/common/http/filter_manager.cc | 2 + source/common/http/filter_manager.h | 52 ++++++ source/common/http/match_wrapper/BUILD | 23 +++ source/common/http/match_wrapper/config.cc | 90 ++++++++++ source/common/http/match_wrapper/config.h | 28 ++++ source/common/matcher/matcher.h | 2 +- test/common/http/match_wrapper/BUILD | 19 +++ test/common/http/match_wrapper/config_test.cc | 91 ++++++++++ test/common/matcher/test_utility.h | 4 +- test/integration/BUILD | 3 + .../extension_discovery_integration_test.cc | 158 +++++++++++++++++- test/integration/integration_test.cc | 52 ++++++ 21 files changed, 677 insertions(+), 9 deletions(-) create mode 100644 api/envoy/type/matcher/v3/http_inputs.proto create mode 100644 api/envoy/type/matcher/v4alpha/http_inputs.proto create mode 100644 generated_api_shadow/envoy/type/matcher/v3/http_inputs.proto create mode 100644 generated_api_shadow/envoy/type/matcher/v4alpha/http_inputs.proto create mode 100644 source/common/http/match_wrapper/BUILD create mode 100644 source/common/http/match_wrapper/config.cc create mode 100644 source/common/http/match_wrapper/config.h create mode 100644 test/common/http/match_wrapper/BUILD create mode 100644 test/common/http/match_wrapper/config_test.cc diff --git a/api/envoy/type/matcher/v3/http_inputs.proto b/api/envoy/type/matcher/v3/http_inputs.proto new file mode 100644 index 000000000000..48eb4c43154a --- /dev/null +++ b/api/envoy/type/matcher/v3/http_inputs.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package envoy.type.matcher.v3; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher.v3"; +option java_outer_classname = "HttpInputsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Common HTTP Inputs] + +// Match input indicates that matching should be done on a specific request header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the request contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpRequestHeaderMatchInput { + // The request header to match on. + string header_name = 1; +} + +// Match input indicating that matching should be done on a specific response header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the response contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpResponseHeaderMatchInput { + // The response header to match on. + string header_name = 1; +} diff --git a/api/envoy/type/matcher/v4alpha/http_inputs.proto b/api/envoy/type/matcher/v4alpha/http_inputs.proto new file mode 100644 index 000000000000..f15e9ceb70e2 --- /dev/null +++ b/api/envoy/type/matcher/v4alpha/http_inputs.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package envoy.type.matcher.v4alpha; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher.v4alpha"; +option java_outer_classname = "HttpInputsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Common HTTP Inputs] + +// Match input indicates that matching should be done on a specific request header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the request contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpRequestHeaderMatchInput { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.v3.HttpRequestHeaderMatchInput"; + + // The request header to match on. + string header_name = 1; +} + +// Match input indicating that matching should be done on a specific response header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the response contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpResponseHeaderMatchInput { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.v3.HttpResponseHeaderMatchInput"; + + // The response header to match on. + string header_name = 1; +} diff --git a/docs/root/api-v3/types/types.rst b/docs/root/api-v3/types/types.rst index 3e6af53865bd..4321708bdb82 100644 --- a/docs/root/api-v3/types/types.rst +++ b/docs/root/api-v3/types/types.rst @@ -21,5 +21,6 @@ Types ../type/matcher/v3/string.proto ../type/matcher/v3/struct.proto ../type/matcher/v3/value.proto + ../type/matcher/v3/http_inputs.proto ../type/metadata/v3/metadata.proto ../type/tracing/v3/custom_tag.proto diff --git a/generated_api_shadow/envoy/type/matcher/v3/http_inputs.proto b/generated_api_shadow/envoy/type/matcher/v3/http_inputs.proto new file mode 100644 index 000000000000..48eb4c43154a --- /dev/null +++ b/generated_api_shadow/envoy/type/matcher/v3/http_inputs.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package envoy.type.matcher.v3; + +import "udpa/annotations/migrate.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher.v3"; +option java_outer_classname = "HttpInputsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Common HTTP Inputs] + +// Match input indicates that matching should be done on a specific request header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the request contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpRequestHeaderMatchInput { + // The request header to match on. + string header_name = 1; +} + +// Match input indicating that matching should be done on a specific response header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the response contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpResponseHeaderMatchInput { + // The response header to match on. + string header_name = 1; +} diff --git a/generated_api_shadow/envoy/type/matcher/v4alpha/http_inputs.proto b/generated_api_shadow/envoy/type/matcher/v4alpha/http_inputs.proto new file mode 100644 index 000000000000..f15e9ceb70e2 --- /dev/null +++ b/generated_api_shadow/envoy/type/matcher/v4alpha/http_inputs.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +package envoy.type.matcher.v4alpha; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.type.matcher.v4alpha"; +option java_outer_classname = "HttpInputsProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: Common HTTP Inputs] + +// Match input indicates that matching should be done on a specific request header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the request contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpRequestHeaderMatchInput { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.v3.HttpRequestHeaderMatchInput"; + + // The request header to match on. + string header_name = 1; +} + +// Match input indicating that matching should be done on a specific response header. +// The resulting input string will be all headers for the given key joined by a comma, +// e.g. if the response contains two 'foo' headers with value 'bar' and 'baz', the input +// string will be 'bar,baz'. +// [#comment:TODO(snowp): Link to unified matching docs.] +message HttpResponseHeaderMatchInput { + option (udpa.annotations.versioning).previous_message_type = + "envoy.type.matcher.v3.HttpResponseHeaderMatchInput"; + + // The response header to match on. + string header_name = 1; +} diff --git a/include/envoy/http/filter.h b/include/envoy/http/filter.h index 3fb1210bb110..ecb39f22dcb0 100644 --- a/include/envoy/http/filter.h +++ b/include/envoy/http/filter.h @@ -885,6 +885,8 @@ using StreamFilterSharedPtr = std::shared_ptr; class HttpMatchingData { public: + static absl::string_view name() { return "http"; } + virtual ~HttpMatchingData() = default; virtual RequestHeaderMapOptConstRef requestHeaders() const PURE; diff --git a/include/envoy/matcher/BUILD b/include/envoy/matcher/BUILD index 838e7fd5e0c1..fadfc109afb0 100644 --- a/include/envoy/matcher/BUILD +++ b/include/envoy/matcher/BUILD @@ -17,6 +17,7 @@ envoy_cc_library( ], deps = [ "//include/envoy/config:typed_config_interface", + "//include/envoy/protobuf:message_validator_interface", "@envoy_api//envoy/config/common/matcher/v3:pkg_cc_proto", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], diff --git a/include/envoy/matcher/matcher.h b/include/envoy/matcher/matcher.h index 613274ce389a..678eed64023b 100644 --- a/include/envoy/matcher/matcher.h +++ b/include/envoy/matcher/matcher.h @@ -7,6 +7,7 @@ #include "envoy/config/common/matcher/v3/matcher.pb.h" #include "envoy/config/core/v3/extension.pb.h" #include "envoy/config/typed_config.h" +#include "envoy/protobuf/message_validator.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" @@ -209,7 +210,9 @@ template class DataInputFactory : public Config::TypedFactory { /** * Creates a DataInput from the provided config. */ - virtual DataInputPtr createDataInput(const Protobuf::Message& config) PURE; + virtual DataInputPtr + createDataInput(const Protobuf::Message& config, + ProtobufMessage::ValidationVisitor& validation_visitor) PURE; /** * The category of this factory depends on the DataType, so we require a name() function to exist diff --git a/source/common/http/BUILD b/source/common/http/BUILD index e19d26c058f4..a1917b42809c 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -184,6 +184,8 @@ envoy_cc_library( "//source/common/local_reply:local_reply_lib", "//source/common/matcher:matcher_lib", "@envoy_api//envoy/extensions/filters/common/matcher/action/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", + "@envoy_api//envoy/type/matcher/v3:pkg_cc_proto", ], ) @@ -247,6 +249,7 @@ envoy_cc_library( "//source/common/http/http2:codec_lib", "//source/common/http/http3:quic_codec_factory_lib", "//source/common/http/http3:well_known_names", + "//source/common/http/match_wrapper:config", "//source/common/network:utility_lib", "//source/common/router:config_lib", "//source/common/router:scoped_rds_lib", diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc index 4a4e355da3f6..bde33e6d4520 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc @@ -15,6 +15,8 @@ namespace Envoy { namespace Http { namespace { +REGISTER_FACTORY(HttpRequestHeadersDataInputFactory, Matcher::DataInputFactory); +REGISTER_FACTORY(SkipActionFactory, Matcher::ActionFactory); template using FilterList = std::list>; diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h index 65061c9113c2..06b9e4ee65ce 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h @@ -4,10 +4,14 @@ #include "envoy/common/optref.h" #include "envoy/extensions/filters/common/matcher/action/v3/skip_action.pb.h" +#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" +#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.validate.h" #include "envoy/http/filter.h" #include "envoy/http/header_map.h" #include "envoy/matcher/matcher.h" #include "envoy/network/socket.h" +#include "envoy/protobuf/message_validator.h" +#include "envoy/type/matcher/v3/http_inputs.pb.validate.h" #include "common/buffer/watermark_buffer.h" #include "common/common/dump_state_utils.h" @@ -18,6 +22,7 @@ #include "common/http/headers.h" #include "common/local_reply/local_reply.h" #include "common/matcher/matcher.h" +#include "common/protobuf/utility.h" namespace Envoy { namespace Http { @@ -97,6 +102,36 @@ class HttpRequestHeadersDataInput : public HttpHeadersDataInputBase +class HttpHeadersDataInputFactoryBase : public Matcher::DataInputFactory { +public: + explicit HttpHeadersDataInputFactoryBase(const std::string& name) : name_(name) {} + + std::string name() const override { return name_; } + + Matcher::DataInputPtr + createDataInput(const Protobuf::Message& config, + ProtobufMessage::ValidationVisitor& validation_visitor) override { + const auto& typed_config = + MessageUtil::downcastAndValidate(config, validation_visitor); + + return std::make_unique(typed_config.header_name()); + }; + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } + +private: + const std::string name_; +}; + +class HttpRequestHeadersDataInputFactory + : public HttpHeadersDataInputFactoryBase< + HttpRequestHeadersDataInput, envoy::type::matcher::v3::HttpRequestHeaderMatchInput> { +public: + HttpRequestHeadersDataInputFactory() : HttpHeadersDataInputFactoryBase("request-headers") {} +}; + class HttpResponseHeadersDataInput : public HttpHeadersDataInputBase { public: explicit HttpResponseHeadersDataInput(const std::string& name) : HttpHeadersDataInputBase(name) {} @@ -107,9 +142,26 @@ class HttpResponseHeadersDataInput : public HttpHeadersDataInputBase { +public: + HttpResponseHeadersDataInputFactory() : HttpHeadersDataInputFactoryBase("response-headers") {} +}; + class SkipAction : public Matcher::ActionBase< envoy::extensions::filters::common::matcher::action::v3::SkipFilter> {}; +class SkipActionFactory : public Matcher::ActionFactory { +public: + std::string name() const override { return "skip"; } + Matcher::ActionFactoryCb createActionFactoryCb(const Protobuf::Message&) override { + return []() { return std::make_unique(); }; + } + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } +}; /** * Base class wrapper for both stream encoder and decoder filters. * diff --git a/source/common/http/match_wrapper/BUILD b/source/common/http/match_wrapper/BUILD new file mode 100644 index 000000000000..1350d3db39e0 --- /dev/null +++ b/source/common/http/match_wrapper/BUILD @@ -0,0 +1,23 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + "//include/envoy/registry", + "//include/envoy/server:filter_config_interface", + "//source/common/matcher:matcher_lib", + "//source/extensions/filters/http:well_known_names", + "//source/extensions/filters/http/common:factory_base_lib", + "@envoy_api//envoy/extensions/common/matching/v3:pkg_cc_proto", + ], +) diff --git a/source/common/http/match_wrapper/config.cc b/source/common/http/match_wrapper/config.cc new file mode 100644 index 000000000000..7d5b75f9f47a --- /dev/null +++ b/source/common/http/match_wrapper/config.cc @@ -0,0 +1,90 @@ +#include "common/http/match_wrapper/config.h" + +#include "envoy/http/filter.h" +#include "envoy/matcher/matcher.h" +#include "envoy/registry/registry.h" + +#include "common/config/utility.h" +#include "common/matcher/matcher.h" + +namespace Envoy { +namespace Common { +namespace Http { +namespace MatchWrapper { + +namespace { +struct DelegatingFactoryCallbacks : public Envoy::Http::FilterChainFactoryCallbacks { + DelegatingFactoryCallbacks(Envoy::Http::FilterChainFactoryCallbacks& delegated_callbacks, + Matcher::MatchTreeSharedPtr match_tree) + : delegated_callbacks_(delegated_callbacks), match_tree_(std::move(match_tree)) {} + + void addStreamDecoderFilter(Envoy::Http::StreamDecoderFilterSharedPtr filter) override { + delegated_callbacks_.addStreamDecoderFilter(std::move(filter), match_tree_); + } + void addStreamDecoderFilter( + Envoy::Http::StreamDecoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) override { + delegated_callbacks_.addStreamDecoderFilter(std::move(filter), std::move(match_tree)); + } + void addStreamEncoderFilter(Envoy::Http::StreamEncoderFilterSharedPtr filter) override { + delegated_callbacks_.addStreamEncoderFilter(std::move(filter), match_tree_); + } + void addStreamEncoderFilter( + Envoy::Http::StreamEncoderFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) override { + delegated_callbacks_.addStreamEncoderFilter(std::move(filter), std::move(match_tree)); + } + void addStreamFilter(Envoy::Http::StreamFilterSharedPtr filter) override { + delegated_callbacks_.addStreamFilter(std::move(filter), match_tree_); + } + void + addStreamFilter(Envoy::Http::StreamFilterSharedPtr filter, + Matcher::MatchTreeSharedPtr match_tree) override { + delegated_callbacks_.addStreamFilter(std::move(filter), std::move(match_tree)); + } + void addAccessLogHandler(AccessLog::InstanceSharedPtr handler) override { + delegated_callbacks_.addAccessLogHandler(std::move(handler)); + } + + Envoy::Http::FilterChainFactoryCallbacks& delegated_callbacks_; + Matcher::MatchTreeSharedPtr match_tree_; +}; +} // namespace + +Envoy::Http::FilterFactoryCb MatchWrapperConfig::createFilterFactoryFromProtoTyped( + const envoy::extensions::common::matching::v3::ExtensionWithMatcher& proto_config, + const std::string& prefix, Server::Configuration::FactoryContext& context) { + + ASSERT(proto_config.has_extension_config()); + auto& factory = + Config::Utility::getAndCheckFactory( + proto_config.extension_config()); + + auto message = Config::Utility::translateAnyToFactoryConfig( + proto_config.extension_config().typed_config(), context.messageValidationVisitor(), factory); + auto filter_factory = factory.createFilterFactoryFromProto(*message, prefix, context); + + auto match_tree = + Matcher::MatchTreeFactory(context.messageValidationVisitor()) + .create(proto_config.matcher()); + + return [filter_factory, match_tree](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void { + DelegatingFactoryCallbacks delegated_callbacks(callbacks, match_tree); + + return filter_factory(delegated_callbacks); + }; +} + +/** + * Static registration for the match wrapper filter. @see RegisterFactory. + * Note that we register this as a filter in order to serve as a drop in wrapper for other HTTP + * filters. While not a real filter, by being registered as one all the code paths that look up HTTP + * filters will look up this filter factory instead, which does the work to create and associate a + * match tree with the underlying filter. + */ +REGISTER_FACTORY(MatchWrapperConfig, Server::Configuration::NamedHttpFilterConfigFactory); + +} // namespace MatchWrapper +} // namespace Http +} // namespace Common +} // namespace Envoy diff --git a/source/common/http/match_wrapper/config.h b/source/common/http/match_wrapper/config.h new file mode 100644 index 000000000000..c1417aaa6f24 --- /dev/null +++ b/source/common/http/match_wrapper/config.h @@ -0,0 +1,28 @@ +#pragma once + +#include "envoy/extensions/common/matching/v3/extension_matcher.pb.validate.h" +#include "envoy/server/filter_config.h" + +#include "extensions/filters/http/common/factory_base.h" +#include "extensions/filters/http/well_known_names.h" + +namespace Envoy { +namespace Common { +namespace Http { +namespace MatchWrapper { + +class MatchWrapperConfig : public Extensions::HttpFilters::Common::FactoryBase< + envoy::extensions::common::matching::v3::ExtensionWithMatcher> { +public: + MatchWrapperConfig() : FactoryBase("match-wrapper") {} + +private: + Envoy::Http::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::extensions::common::matching::v3::ExtensionWithMatcher& proto_config, + const std::string&, Server::Configuration::FactoryContext& context) override; +}; + +} // namespace MatchWrapper +} // namespace Http +} // namespace Common +} // namespace Envoy diff --git a/source/common/matcher/matcher.h b/source/common/matcher/matcher.h index 4e21e17ea463..44ae0a26e787 100644 --- a/source/common/matcher/matcher.h +++ b/source/common/matcher/matcher.h @@ -160,7 +160,7 @@ template class MatchTreeFactory { auto& factory = Config::Utility::getAndCheckFactory>(config); ProtobufTypes::MessagePtr message = Config::Utility::translateAnyToFactoryConfig( config.typed_config(), validation_visitor_, factory); - return factory.createDataInput(*message); + return factory.createDataInput(*message, validation_visitor_); } InputMatcherPtr createInputMatcher( diff --git a/test/common/http/match_wrapper/BUILD b/test/common/http/match_wrapper/BUILD new file mode 100644 index 000000000000..52425ab72baa --- /dev/null +++ b/test/common/http/match_wrapper/BUILD @@ -0,0 +1,19 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + deps = [ + "//source/common/http/match_wrapper:config", + "//test/mocks/server:factory_context_mocks", + "//test/test_common:registry_lib", + ], +) diff --git a/test/common/http/match_wrapper/config_test.cc b/test/common/http/match_wrapper/config_test.cc new file mode 100644 index 000000000000..d29207ff3c6e --- /dev/null +++ b/test/common/http/match_wrapper/config_test.cc @@ -0,0 +1,91 @@ +#include "envoy/http/filter.h" +#include "envoy/server/factory_context.h" +#include "envoy/server/filter_config.h" + +#include "common/http/match_wrapper/config.h" + +#include "test/mocks/server/factory_context.h" +#include "test/test_common/registry.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Common { +namespace Http { +namespace MatchWrapper { +namespace { + +struct TestFactory : public Envoy::Server::Configuration::NamedHttpFilterConfigFactory { + std::string name() const override { return "test"; } + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } + Envoy::Http::FilterFactoryCb + createFilterFactoryFromProto(const Protobuf::Message&, const std::string&, + Server::Configuration::FactoryContext&) override { + return [](auto& callbacks) { + callbacks.addStreamDecoderFilter(nullptr); + callbacks.addStreamEncoderFilter(nullptr); + callbacks.addStreamFilter(nullptr); + + callbacks.addStreamDecoderFilter(nullptr, nullptr); + callbacks.addStreamEncoderFilter(nullptr, nullptr); + callbacks.addStreamFilter(nullptr, nullptr); + + callbacks.addAccessLogHandler(nullptr); + }; + } +}; + +TEST(MatchWrapper, WithMatcher) { + TestFactory test_factory; + Envoy::Registry::InjectFactory + inject_factory(test_factory); + + NiceMock factory_context; + + const auto config = + TestUtility::parseYaml(R"EOF( +extension_config: + name: test + typed_config: + "@type": type.googleapis.com/google.protobuf.StringValue +matcher: + matcher_tree: + input: + name: request-headers + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: default-matcher-header + exact_match_map: + map: + match: + action: + name: skip + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter +)EOF"); + + MatchWrapperConfig match_wrapper_config; + auto cb = match_wrapper_config.createFilterFactoryFromProto(config, "", factory_context); + + Envoy::Http::MockFilterChainFactoryCallbacks factory_callbacks; + testing::InSequence s; + + // This matches the sequence of calls in the filter factory above: the ones that call the overload + // without a match tree has a match tree added, the other one does not. + EXPECT_CALL(factory_callbacks, addStreamDecoderFilter(_, testing::NotNull())); + EXPECT_CALL(factory_callbacks, addStreamEncoderFilter(_, testing::NotNull())); + EXPECT_CALL(factory_callbacks, addStreamFilter(_, testing::NotNull())); + EXPECT_CALL(factory_callbacks, addStreamDecoderFilter(_, testing::IsNull())); + EXPECT_CALL(factory_callbacks, addStreamEncoderFilter(_, testing::IsNull())); + EXPECT_CALL(factory_callbacks, addStreamFilter(_, testing::IsNull())); + EXPECT_CALL(factory_callbacks, addAccessLogHandler(_)); + cb(factory_callbacks); +} + +} // namespace +} // namespace MatchWrapper +} // namespace Http +} // namespace Common +} // namespace Envoy \ No newline at end of file diff --git a/test/common/matcher/test_utility.h b/test/common/matcher/test_utility.h index 623d8158c55f..1348d9cd80d4 100644 --- a/test/common/matcher/test_utility.h +++ b/test/common/matcher/test_utility.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/matcher/matcher.h" +#include "envoy/protobuf/message_validator.h" #include "common/matcher/matcher.h" @@ -29,7 +30,8 @@ class TestDataInputFactory : public DataInputFactory { TestDataInputFactory(absl::string_view factory_name, absl::string_view data) : factory_name_(std::string(factory_name)), value_(std::string(data)), injection_(*this) {} - DataInputPtr createDataInput(const Protobuf::Message&) override { + DataInputPtr createDataInput(const Protobuf::Message&, + ProtobufMessage::ValidationVisitor&) override { return std::make_unique( DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, value_}); } diff --git a/test/integration/BUILD b/test/integration/BUILD index dc921e3a137e..605d63e4f212 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -877,6 +877,8 @@ envoy_cc_test( "//test/integration/filters:encoder_decoder_buffer_filter_lib", "//test/integration/filters:invalid_header_filter_lib", "//test/integration/filters:process_context_lib", + "//test/integration/filters:set_response_code_filter_config_proto_cc_proto", + "//test/integration/filters:set_response_code_filter_lib", "//test/integration/filters:stop_iteration_and_continue", "//test/mocks/http:http_mocks", "//test/test_common:utility_lib", @@ -1076,6 +1078,7 @@ envoy_cc_test( "//test/integration/filters:set_response_code_filter_config_proto_cc_proto", "//test/integration/filters:set_response_code_filter_lib", "//test/test_common:utility_lib", + "@envoy_api//envoy/extensions/common/matching/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", "@envoy_api//envoy/service/extension/v3:pkg_cc_proto", ], diff --git a/test/integration/extension_discovery_integration_test.cc b/test/integration/extension_discovery_integration_test.cc index b671e6f94bd9..28f49421dacd 100644 --- a/test/integration/extension_discovery_integration_test.cc +++ b/test/integration/extension_discovery_integration_test.cc @@ -1,3 +1,4 @@ +#include "envoy/extensions/common/matching/v3/extension_matcher.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" #include "envoy/service/extension/v3/config_discovery.pb.h" @@ -18,6 +19,32 @@ std::string denyPrivateConfig() { )EOF"; } +std::string denyPrivateConfigWithMatcher() { + return R"EOF( + "@type": type.googleapis.com/envoy.extensions.common.matching.v3.ExtensionWithMatcher + extension_config: + name: response-filter-config + typed_config: + "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig + prefix: "/private" + code: 403 + matcher: + matcher_tree: + input: + name: request-headers + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: some-header + exact_match_map: + map: + match: + action: + name: skip + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter + )EOF"; +} + std::string allowAllConfig() { return "code: 200"; } std::string invalidConfig() { return "code: 90"; } @@ -29,9 +56,10 @@ class ExtensionDiscoveryIntegrationTest : public Grpc::GrpcClientIntegrationPara : HttpIntegrationTest(Http::CodecClient::Type::HTTP1, ipVersion()) {} void addDynamicFilter(const std::string& name, bool apply_without_warming, - bool set_default_config = true, bool rate_limit = false) { + bool set_default_config = true, bool rate_limit = false, + bool use_default_matcher = false) { config_helper_.addConfigModifier( - [this, name, apply_without_warming, set_default_config, rate_limit]( + [this, name, apply_without_warming, set_default_config, rate_limit, use_default_matcher]( envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& http_connection_manager) { auto* filter = http_connection_manager.mutable_http_filters()->Add(); @@ -39,11 +67,41 @@ class ExtensionDiscoveryIntegrationTest : public Grpc::GrpcClientIntegrationPara auto* discovery = filter->mutable_config_discovery(); discovery->add_type_urls( "type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig"); + discovery->add_type_urls( + "type.googleapis.com/envoy.extensions.common.matching.v3.ExtensionWithMatcher"); if (set_default_config) { - const auto default_configuration = - TestUtility::parseYaml( - "code: 403"); - discovery->mutable_default_config()->PackFrom(default_configuration); + if (use_default_matcher) { + const auto default_configuration = TestUtility::parseYaml< + envoy::extensions::common::matching::v3::ExtensionWithMatcher>( + R"EOF( + extension_config: + name: set-response-code + typed_config: + "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig + code: 403 + matcher: + matcher_tree: + input: + name: request-headers + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: default-matcher-header + exact_match_map: + map: + match: + action: + name: skip + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter + )EOF"); + + discovery->mutable_default_config()->PackFrom(default_configuration); + } else { + const auto default_configuration = + TestUtility::parseYaml( + "code: 403"); + discovery->mutable_default_config()->PackFrom(default_configuration); + } } discovery->set_apply_default_config_without_warming(apply_without_warming); discovery->mutable_config_source()->set_resource_api_version( @@ -125,6 +183,19 @@ class ExtensionDiscoveryIntegrationTest : public Grpc::GrpcClientIntegrationPara ecds_stream_->sendGrpcMessage(response); } + void sendXdsResponseWithFullYaml(const std::string& name, const std::string& version, + const std::string& full_yaml) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version); + response.set_type_url("type.googleapis.com/envoy.config.core.v3.TypedExtensionConfig"); + const auto configuration = TestUtility::parseYaml(full_yaml); + envoy::config::core::v3::TypedExtensionConfig typed_config; + typed_config.set_name(name); + typed_config.mutable_typed_config()->MergeFrom(configuration); + response.add_resources()->PackFrom(typed_config); + ecds_stream_->sendGrpcMessage(response); + } + FakeUpstream& getEcdsFakeUpstream() const { return *fake_upstreams_[1]; } FakeHttpConnectionPtr ecds_connection_{nullptr}; @@ -176,6 +247,81 @@ TEST_P(ExtensionDiscoveryIntegrationTest, BasicSuccess) { } } +TEST_P(ExtensionDiscoveryIntegrationTest, BasicSuccessWithMatcher) { + on_server_init_function_ = [&]() { waitXdsStream(); }; + addDynamicFilter("foo", false); + initialize(); + test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initializing); + registerTestServerPorts({"http"}); + sendXdsResponseWithFullYaml("foo", "1", denyPrivateConfigWithMatcher()); + test_server_->waitForCounterGe("http.config_test.extension_config_discovery.foo.config_reload", + 1); + test_server_->waitUntilListenersReady(); + test_server_->waitForGaugeGe("listener_manager.workers_started", 1); + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initialized); + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + { + Http::TestRequestHeaderMapImpl request_headers{ + {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "host"}}; + auto response = codec_client_->makeHeaderOnlyRequest(request_headers); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + } + Http::TestRequestHeaderMapImpl banned_request_headers{ + {":method", "GET"}, {":path", "/private/key"}, {":scheme", "http"}, {":authority", "host"}}; + { + auto response = codec_client_->makeHeaderOnlyRequest(banned_request_headers); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("403", response->headers().getStatusValue()); + } + Http::TestRequestHeaderMapImpl banned_request_headers_skipped{{":method", "GET"}, + {":path", "/private/key"}, + {"some-header", "match"}, + {":scheme", "http"}, + {":authority", "host"}}; + { + auto response = codec_client_->makeHeaderOnlyRequest(banned_request_headers_skipped); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); + } +} + +TEST_P(ExtensionDiscoveryIntegrationTest, BasicDefaultMatcher) { + on_server_init_function_ = [&]() { waitXdsStream(); }; + addDynamicFilter("foo", false, true, false, true); + initialize(); + test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initializing); + registerTestServerPorts({"http"}); + sendXdsResponse("foo", "1", invalidConfig()); + test_server_->waitForCounterGe("http.config_test.extension_config_discovery.foo.config_fail", 1); + test_server_->waitUntilListenersReady(); + test_server_->waitForGaugeGe("listener_manager.workers_started", 1); + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initialized); + codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http")))); + { + Http::TestRequestHeaderMapImpl request_headers{ + {":method", "GET"}, {":path", "/"}, {":scheme", "http"}, {":authority", "host"}}; + auto response = codec_client_->makeHeaderOnlyRequest(request_headers); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("403", response->headers().getStatusValue()); + } + Http::TestRequestHeaderMapImpl request_headers{{":method", "GET"}, + {"default-matcher-header", "match"}, + {":path", "/"}, + {":scheme", "http"}, + {":authority", "host"}}; + auto response = codec_client_->makeHeaderOnlyRequest(request_headers); + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("200", response->headers().getStatusValue()); +} + TEST_P(ExtensionDiscoveryIntegrationTest, BasicFailWithDefault) { on_server_init_function_ = [&]() { waitXdsStream(); }; addDynamicFilter("foo", false); diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index f662744274f9..dc6e3f0b214e 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -364,6 +364,58 @@ TEST_P(IntegrationTest, EnvoyProxying100ContinueWithDecodeDataPause) { testEnvoyProxying1xx(true); } +// Verifies that we can construct a match tree with a filter, and that we are able to skip +// filter invocation through the match tree. +TEST_P(IntegrationTest, MatchingHttpFilterConstruction) { + config_helper_.addFilter(R"EOF( +name: matcher +typed_config: + "@type": type.googleapis.com/envoy.extensions.common.matching.v3.ExtensionWithMatcher + extension_config: + name: set-response-code + typed_config: + "@type": type.googleapis.com/test.integration.filters.SetResponseCodeFilterConfig + code: 403 + matcher: + matcher_tree: + input: + name: request-headers + typed_config: + "@type": type.googleapis.com/envoy.type.matcher.v3.HttpRequestHeaderMatchInput + header_name: match-header + exact_match_map: + map: + match: + action: + name: skip + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.common.matcher.action.v3.SkipFilter +)EOF"); + + initialize(); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + { + auto response = codec_client_->makeRequestWithBody(default_request_headers_, 1024); + response->waitForEndStream(); + EXPECT_THAT(response->headers(), HttpStatusIs("403")); + } + + codec_client_ = makeHttpConnection(lookupPort("http")); + Http::TestRequestHeaderMapImpl request_headers{ + {":method", "POST"}, {":path", "/test/long/url"}, {":scheme", "http"}, + {":authority", "host"}, {"match-header", "match"}, {"content-type", "application/grpc"}}; + auto response = codec_client_->makeRequestWithBody(request_headers, 1024); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(default_response_headers_, true); + + response->waitForEndStream(); + EXPECT_THAT(response->headers(), HttpStatusIs("200")); + + codec_client_->close(); +} + // This is a regression for https://github.com/envoyproxy/envoy/issues/2715 and validates that a // pending request is not sent on a connection that has been half-closed. TEST_P(IntegrationTest, UpstreamDisconnectWithTwoRequests) { From e9ffbc57c6b1023583ff96d80bb091c48a128d96 Mon Sep 17 00:00:00 2001 From: Rama Chavali Date: Tue, 12 Jan 2021 20:58:18 +0530 Subject: [PATCH 2/4] fix empty connection debug logs (#14666) Fixes #14661 Signed-off-by: Rama Chavali --- source/extensions/transport_sockets/tls/ssl_socket.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/extensions/transport_sockets/tls/ssl_socket.cc b/source/extensions/transport_sockets/tls/ssl_socket.cc index ba408c2d417f..4f0fa0ae4554 100644 --- a/source/extensions/transport_sockets/tls/ssl_socket.cc +++ b/source/extensions/transport_sockets/tls/ssl_socket.cc @@ -221,7 +221,9 @@ void SslSocket::drainErrorQueue() { ERR_func_error_string(err), ":", ERR_reason_error_string(err))); } - ENVOY_CONN_LOG(debug, "{}", callbacks_->connection(), failure_reason_); + if (!failure_reason_.empty()) { + ENVOY_CONN_LOG(debug, "{}", callbacks_->connection(), failure_reason_); + } if (saw_error && !saw_counted_error) { ctx_->stats().connection_error_.inc(); } From e53227e93b591552b3c85a63c4fb14ccc45c1c35 Mon Sep 17 00:00:00 2001 From: Iacopo Rozzo Date: Tue, 12 Jan 2021 17:07:31 +0100 Subject: [PATCH 3/4] tcp_proxy: ignore transfer encoding in HTTP/1.1 CONNECT responses (#14623) Commit Message: Ignore the transfer encoding header in CONNECT responses Additional Description: NONE Risk Level: low Testing: integration test Docs Changes: NONE Release Notes: https://github.com/irozzo-1A/envoy/blob/ignore-transfer-encoding/docs/root/version_history/current.rst#new-features Platform Specific Features: NONE Fixes #11308 Signed-off-by: Iacopo Rozzo --- docs/root/intro/arch_overview/http/upgrades.rst | 2 ++ docs/root/version_history/current.rst | 3 ++- source/common/http/http1/codec_impl.cc | 9 --------- test/integration/integration_test.cc | 11 +++++++---- .../tcp_tunneling_integration_test.cc | 16 +++++++++------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/docs/root/intro/arch_overview/http/upgrades.rst b/docs/root/intro/arch_overview/http/upgrades.rst index 6a4338d39ee5..ba015ff838a9 100644 --- a/docs/root/intro/arch_overview/http/upgrades.rst +++ b/docs/root/intro/arch_overview/http/upgrades.rst @@ -94,6 +94,8 @@ will synthesize 200 response headers, and then forward the TCP data as the HTTP For an example of proxying connect, please see :repo:`configs/proxy_connect.yaml ` For an example of terminating connect, please see :repo:`configs/terminate_connect.yaml ` +.. _tunneling-tcp-over-http: + Tunneling TCP over HTTP ^^^^^^^^^^^^^^^^^^^^^^^ Envoy also has support for tunneling raw TCP over HTTP CONNECT requests. Find diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index bb04894db478..3ef7e0a8cd04 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -19,6 +19,7 @@ Removed Config or Runtime New Features ------------ +* tcp_proxy: add support for converting raw TCP streams into HTTP/1.1 CONNECT requests. See :ref:`upgrade documentation ` for details. Deprecated ----------- \ No newline at end of file +---------- diff --git a/source/common/http/http1/codec_impl.cc b/source/common/http/http1/codec_impl.cc index f0ce0cfb5f88..bbbff2035344 100644 --- a/source/common/http/http1/codec_impl.cc +++ b/source/common/http/http1/codec_impl.cc @@ -1212,15 +1212,6 @@ Envoy::StatusOr ClientConnectionImpl::onHeadersComplete() { pending_response_.value().encoder_.connectRequest()) { ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT response.", connection_); handling_upgrade_ = true; - - // For responses to connect requests, do not accept the chunked - // encoding header: https://tools.ietf.org/html/rfc7231#section-4.3.6 - if (headers->TransferEncoding() && - absl::EqualsIgnoreCase(headers->TransferEncoding()->value().getStringView(), - Headers::get().TransferEncodingValues.Chunked)) { - RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidTransferEncoding)); - return codecProtocolError("http/1.1 protocol error: unsupported transfer encoding"); - } } if (strict_1xx_and_204_headers_ && (parser_.status_code < 200 || parser_.status_code == 204)) { diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index dc6e3f0b214e..348d5f22fc09 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -1697,10 +1697,13 @@ TEST_P(IntegrationTest, ConnectWithChunkedBody) { EXPECT_FALSE(absl::StrContains(data, "onnection")) << data; ASSERT_TRUE(fake_upstream_connection->write( "HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\n\r\nb\r\nHello World\r\n0\r\n\r\n")); - // The response will be rejected because chunked headers are not allowed with CONNECT upgrades. - // Envoy will send a local reply due to the invalid upstream response. - tcp_client->waitForDisconnect(false); - EXPECT_TRUE(absl::StartsWith(tcp_client->data(), "HTTP/1.1 503 Service Unavailable\r\n")); + tcp_client->waitForData("\r\n\r\n", false); + EXPECT_TRUE(absl::StartsWith(tcp_client->data(), "HTTP/1.1 200 OK\r\n")) << tcp_client->data(); + // Make sure the following payload is proxied without chunks or any other modifications. + ASSERT_TRUE(fake_upstream_connection->waitForData( + FakeRawConnection::waitForInexactMatch("\r\n\r\npayload"), &data)); + + tcp_client->close(); ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); } diff --git a/test/integration/tcp_tunneling_integration_test.cc b/test/integration/tcp_tunneling_integration_test.cc index aef3d5f66fef..1369dfa79c81 100644 --- a/test/integration/tcp_tunneling_integration_test.cc +++ b/test/integration/tcp_tunneling_integration_test.cc @@ -792,9 +792,7 @@ TEST_P(TcpTunnelingIntegrationTest, ContentLengthHeaderIgnoredHttp1) { ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } -// TODO(irozzo): temporarily disabled as a protocol error is thrown when -// transfer-encoding header is received in CONNECT responses. -TEST_P(TcpTunnelingIntegrationTest, DISABLED_TransferEncodingHeaderIgnoredHttp1) { +TEST_P(TcpTunnelingIntegrationTest, TransferEncodingHeaderIgnoredHttp1) { if (upstreamProtocol() == FakeHttpConnection::Type::HTTP2) { return; } @@ -812,13 +810,17 @@ TEST_P(TcpTunnelingIntegrationTest, DISABLED_TransferEncodingHeaderIgnoredHttp1) // Send upgrade headers downstream, fully establishing the connection. ASSERT_TRUE( - fake_upstream_connection->write("HTTP/1.1 299 OK\r\nTransfer-encoding: chunked\r\n\r\n")); + fake_upstream_connection->write("HTTP/1.1 200 OK\r\nTransfer-encoding: chunked\r\n\r\n")); // Now send some data and close the TCP client. - ASSERT_TRUE(tcp_client->write("hello", false)); + ASSERT_TRUE(tcp_client->write("hello")); + ASSERT_TRUE( + fake_upstream_connection->waitForData(FakeRawConnection::waitForInexactMatch("hello"))); + + // Close connections. + ASSERT_TRUE(fake_upstream_connection->close()); + ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); tcp_client->close(); - ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5)); - ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); } TEST_P(TcpTunnelingIntegrationTest, DeferTransmitDataUntilSuccessConnectResponseIsReceived) { From 7b5544bf2187cdb2e03519be275f81422a7d1676 Mon Sep 17 00:00:00 2001 From: Lizan Zhou Date: Tue, 12 Jan 2021 10:15:27 -0800 Subject: [PATCH 4/4] ci: fix docs tag build (#14653) Signed-off-by: Lizan Zhou --- .azure-pipelines/pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/pipelines.yml b/.azure-pipelines/pipelines.yml index ad0ef9b4636f..c09544790f0c 100644 --- a/.azure-pipelines/pipelines.yml +++ b/.azure-pipelines/pipelines.yml @@ -54,6 +54,7 @@ stages: - script: ci/run_envoy_docker.sh 'ci/do_ci.sh docs' workingDirectory: $(Build.SourcesDirectory) env: + AZP_BRANCH: $(Build.SourceBranch) ENVOY_DOCKER_BUILD_DIR: $(Build.StagingDirectory) BAZEL_REMOTE_CACHE: grpcs://remotebuildexecution.googleapis.com BAZEL_REMOTE_INSTANCE: projects/envoy-ci/instances/default_instance @@ -303,6 +304,7 @@ stages: - script: ci/run_envoy_docker.sh 'ci/do_ci.sh docs' workingDirectory: $(Build.SourcesDirectory) env: + AZP_BRANCH: $(Build.SourceBranch) ENVOY_DOCKER_BUILD_DIR: $(Build.StagingDirectory) BAZEL_REMOTE_CACHE: grpcs://remotebuildexecution.googleapis.com BAZEL_REMOTE_INSTANCE: projects/envoy-ci/instances/default_instance @@ -321,7 +323,6 @@ stages: workingDirectory: $(Build.SourcesDirectory) env: AZP_BRANCH: $(Build.SourceBranch) - AZP_SHA1: $(Build.SourceVersion) - stage: verify dependsOn: ["docker"]