diff --git a/api/BUILD b/api/BUILD index ed8743b793e39..c1c0c4f025be6 100644 --- a/api/BUILD +++ b/api/BUILD @@ -244,6 +244,7 @@ proto_library( "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", "//envoy/extensions/transport_sockets/tap/v3:pkg", "//envoy/extensions/transport_sockets/tls/v3:pkg", + "//envoy/extensions/upstreams/http/cluster_sensitive/v3:pkg", "//envoy/extensions/upstreams/http/generic/v3:pkg", "//envoy/extensions/upstreams/http/http/v3:pkg", "//envoy/extensions/upstreams/http/tcp/v3:pkg", diff --git a/api/envoy/extensions/upstreams/http/cluster_sensitive/v3/BUILD b/api/envoy/extensions/upstreams/http/cluster_sensitive/v3/BUILD new file mode 100644 index 0000000000000..ee92fb652582e --- /dev/null +++ b/api/envoy/extensions/upstreams/http/cluster_sensitive/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/upstreams/http/cluster_sensitive/v3/http_connection_pool.proto b/api/envoy/extensions/upstreams/http/cluster_sensitive/v3/http_connection_pool.proto new file mode 100644 index 0000000000000..bfed129ff9ddd --- /dev/null +++ b/api/envoy/extensions/upstreams/http/cluster_sensitive/v3/http_connection_pool.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package envoy.extensions.upstreams.http.cluster_sensitive.v3; + +import "udpa/annotations/status.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.upstreams.http.cluster_sensitive.v3"; +option java_outer_classname = "HttpConnectionPoolProtoOuterClass"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Http Connection Pool] + +// A connection pool which forwards downstream HTTP as HTTP to upstream. +// [#extension: envoy.upstreams.http.cluster_sensitive] +message HttpConnectionPoolProto { +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index d44a54640ca4a..ea388d43f9808 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -127,6 +127,7 @@ proto_library( "//envoy/extensions/transport_sockets/raw_buffer/v3:pkg", "//envoy/extensions/transport_sockets/tap/v3:pkg", "//envoy/extensions/transport_sockets/tls/v3:pkg", + "//envoy/extensions/upstreams/http/cluster_sensitive/v3:pkg", "//envoy/extensions/upstreams/http/generic/v3:pkg", "//envoy/extensions/upstreams/http/http/v3:pkg", "//envoy/extensions/upstreams/http/tcp/v3:pkg", diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 5e6702c3cb750..789690d59b12f 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -606,7 +606,7 @@ std::unique_ptr Filter::createConnPool() { cluster_->upstreamConfig().value()); } else { factory = &Envoy::Config::Utility::getAndCheckFactoryByName( - "envoy.filters.connection_pools.http.generic"); + "envoy.filters.connection_pools.http.cluster_sensitive"); } const bool should_tcp_proxy = route_entry_->connectConfig().has_value() && diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index ddc3dc9a0d508..0e2e35f52bff3 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -209,6 +209,7 @@ EXTENSIONS = { # "envoy.upstreams.http.http": "//source/extensions/upstreams/http/http:config", "envoy.upstreams.http.tcp": "//source/extensions/upstreams/http/tcp:config", + "envoy.upstreams.http.cluster_sensitive": "//source/extensions/upstreams/http/cluster_sensitive:config", # # Watchdog actions diff --git a/source/extensions/upstreams/http/cluster_sensitive/BUILD b/source/extensions/upstreams/http/cluster_sensitive/BUILD new file mode 100644 index 0000000000000..bf86f594ae40f --- /dev/null +++ b/source/extensions/upstreams/http/cluster_sensitive/BUILD @@ -0,0 +1,72 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_extension( + name = "config", + srcs = [ + "config.cc", + ], + hdrs = [ + "config.h", + ], + security_posture = "robust_to_untrusted_downstream", + visibility = ["//visibility:public"], + deps = [ + ":upstream_request_lib", + "//source/extensions/upstreams/http/http:upstream_request_lib", + "//source/extensions/upstreams/http/tcp:upstream_request_lib", + "@envoy_api//envoy/extensions/upstreams/http/cluster_sensitive/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "upstream_request_lib", + deps = [ + ":http_upstream_request_lib", + ":tcp_upstream_request_lib", + ], +) + +envoy_cc_library( + name = "http_upstream_request_lib", + srcs = [ + "http_upstream_request.cc", + ], + hdrs = [ + "http_upstream_request.h", + ], + deps = [ + "//include/envoy/http:codes_interface", + "//include/envoy/http:conn_pool_interface", + "//include/envoy/http:filter_interface", + "//include/envoy/upstream:cluster_manager_interface", + "//include/envoy/upstream:upstream_interface", + "//source/common/common:assert_lib", + "//source/common/common:minimal_logger_lib", + "//source/common/http:codes_lib", + "//source/common/http:header_map_lib", + "//source/common/http:headers_lib", + "//source/common/http:message_lib", + "//source/common/network:application_protocol_lib", + "//source/common/network:transport_socket_options_lib", + "//source/common/router:router_lib", + "//source/common/upstream:load_balancer_lib", + "//source/extensions/common/proxy_protocol:proxy_protocol_header_lib", + "//source/extensions/upstreams/http/http:upstream_request_lib", + ], +) + +envoy_cc_library( + name = "tcp_upstream_request_lib", + deps = [ + "//source/extensions/upstreams/http/http:upstream_request_lib", + ], +) diff --git a/source/extensions/upstreams/http/cluster_sensitive/config.cc b/source/extensions/upstreams/http/cluster_sensitive/config.cc new file mode 100644 index 0000000000000..4feb29546e096 --- /dev/null +++ b/source/extensions/upstreams/http/cluster_sensitive/config.cc @@ -0,0 +1,32 @@ +#include "extensions/upstreams/http/cluster_sensitive/config.h" + +#include "extensions/upstreams/http/cluster_sensitive/http_upstream_request.h" +#include "extensions/upstreams/http/tcp/upstream_request.h" + +namespace Envoy { +namespace Extensions { +namespace Upstreams { +namespace Http { +namespace ClusterSensitive { + +Router::GenericConnPoolPtr ClusterSensitiveGenericConnPoolFactory::createGenericConnPool( + Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, + absl::optional downstream_protocol, + Upstream::LoadBalancerContext* ctx) const { + if (is_connect) { + auto ret = std::make_unique(cm, is_connect, route_entry, + downstream_protocol, ctx); + return (ret->valid() ? std::move(ret) : nullptr); + } + auto ret = std::make_unique( + cm, is_connect, route_entry, downstream_protocol, ctx); + return (ret->valid() ? std::move(ret) : nullptr); +} + +REGISTER_FACTORY(ClusterSensitiveGenericConnPoolFactory, Router::GenericConnPoolFactory); + +} // namespace ClusterSensitive +} // namespace Http +} // namespace Upstreams +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/upstreams/http/cluster_sensitive/config.h b/source/extensions/upstreams/http/cluster_sensitive/config.h new file mode 100644 index 0000000000000..4a4d90fd809ad --- /dev/null +++ b/source/extensions/upstreams/http/cluster_sensitive/config.h @@ -0,0 +1,40 @@ +#pragma once + +#include "envoy/extensions/upstreams/http/cluster_sensitive/v3/http_connection_pool.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/router/router.h" + +namespace Envoy { +namespace Extensions { +namespace Upstreams { +namespace Http { +namespace ClusterSensitive { + +/** + * Config registration for the HttpConnPool. @see Router::GenericConnPoolFactory + */ +class ClusterSensitiveGenericConnPoolFactory : public Router::GenericConnPoolFactory { +public: + std::string name() const override { + return "envoy.filters.connection_pools.http.cluster_sensitive"; + } + std::string category() const override { return "envoy.upstreams"; } + Router::GenericConnPoolPtr + createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, + const Router::RouteEntry& route_entry, + absl::optional downstream_protocol, + Upstream::LoadBalancerContext* ctx) const override; + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::upstreams::http::cluster_sensitive::v3::HttpConnectionPoolProto>(); + } +}; + +DECLARE_FACTORY(ClusterSensitiveGenericConnPoolFactory); + +} // namespace ClusterSensitive +} // namespace Http +} // namespace Upstreams +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/upstreams/http/cluster_sensitive/http_upstream_request.cc b/source/extensions/upstreams/http/cluster_sensitive/http_upstream_request.cc new file mode 100644 index 0000000000000..6f945205a703c --- /dev/null +++ b/source/extensions/upstreams/http/cluster_sensitive/http_upstream_request.cc @@ -0,0 +1,75 @@ +#include "extensions/upstreams/http/cluster_sensitive/http_upstream_request.h" + +#include +#include + +#include "envoy/event/dispatcher.h" +#include "envoy/event/timer.h" +#include "envoy/grpc/status.h" +#include "envoy/http/conn_pool.h" +#include "envoy/upstream/cluster_manager.h" +#include "envoy/upstream/upstream.h" + +#include "common/common/assert.h" +#include "common/common/utility.h" +#include "common/http/codes.h" +#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/router/router.h" + +using Envoy::Router::GenericConnectionPoolCallbacks; + +namespace Envoy { +namespace Extensions { +namespace Upstreams { +namespace Http { +namespace ClusterSensitive { + +void HttpConnPool::newStream(GenericConnectionPoolCallbacks* callbacks) { + callbacks_ = callbacks; + // It's possible for a reset to happen inline within the newStream() call. In this case, we + // might get deleted inline as well. Only write the returned handle out if it is not nullptr to + // deal with this case. + Envoy::Http::ConnectionPool::Cancellable* handle = + conn_pool_->newStream(callbacks->upstreamToDownstream(), *this); + if (handle) { + conn_pool_stream_handle_ = handle; + } +} + +bool HttpConnPool::cancelAnyPendingStream() { + if (conn_pool_stream_handle_) { + conn_pool_stream_handle_->cancel(ConnectionPool::CancelPolicy::Default); + conn_pool_stream_handle_ = nullptr; + return true; + } + return false; +} + +absl::optional HttpConnPool::protocol() const { + return conn_pool_->protocol(); +} + +void HttpConnPool::onPoolFailure(ConnectionPool::PoolFailureReason reason, + absl::string_view transport_failure_reason, + Upstream::HostDescriptionConstSharedPtr host) { + callbacks_->onPoolFailure(reason, transport_failure_reason, host); +} + +void HttpConnPool::onPoolReady(Envoy::Http::RequestEncoder& request_encoder, + Upstream::HostDescriptionConstSharedPtr host, + const StreamInfo::StreamInfo& info) { + conn_pool_stream_handle_ = nullptr; + auto upstream = + std::make_unique(callbacks_->upstreamToDownstream(), &request_encoder, host); + callbacks_->onPoolReady(std::move(upstream), host, + request_encoder.getStream().connectionLocalAddress(), info); +} + +} // namespace ClusterSensitive +} // namespace Http +} // namespace Upstreams +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/upstreams/http/cluster_sensitive/http_upstream_request.h b/source/extensions/upstreams/http/cluster_sensitive/http_upstream_request.h new file mode 100644 index 0000000000000..ca78d280e64d0 --- /dev/null +++ b/source/extensions/upstreams/http/cluster_sensitive/http_upstream_request.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include + +#include "envoy/http/codes.h" +#include "envoy/http/conn_pool.h" + +#include "common/common/logger.h" +#include "common/config/well_known_names.h" +#include "common/http/header_map_impl.h" +#include "common/router/upstream_request.h" + +#include "extensions/upstreams/http/http/upstream_request.h" + +namespace Envoy { +namespace Extensions { +namespace Upstreams { +namespace Http { +namespace ClusterSensitive { + +class HttpConnPool : public Router::GenericConnPool, public Envoy::Http::ConnectionPool::Callbacks { +public: + // GenericConnPool + HttpConnPool(Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, + absl::optional downstream_protocol, + Upstream::LoadBalancerContext* ctx) { + ASSERT(!is_connect); + conn_pool_ = cm.httpConnPoolForCluster(route_entry.clusterName(), route_entry.priority(), + downstream_protocol, ctx); + } + void newStream(Router::GenericConnectionPoolCallbacks* callbacks) override; + bool cancelAnyPendingStream() override; + absl::optional protocol() const override; + + // Http::ConnectionPool::Callbacks + void onPoolFailure(ConnectionPool::PoolFailureReason reason, + absl::string_view transport_failure_reason, + Upstream::HostDescriptionConstSharedPtr host) override; + void onPoolReady(Envoy::Http::RequestEncoder& callbacks_encoder, + Upstream::HostDescriptionConstSharedPtr host, + const StreamInfo::StreamInfo& info) override; + Upstream::HostDescriptionConstSharedPtr host() const override { return conn_pool_->host(); } + + bool valid() { return conn_pool_ != nullptr; } + +private: + // Points to the actual connection pool to create streams from. + Envoy::Http::ConnectionPool::Instance* conn_pool_{}; + Envoy::Http::ConnectionPool::Cancellable* conn_pool_stream_handle_{}; + Router::GenericConnectionPoolCallbacks* callbacks_{}; +}; + +class HttpUpstream : public Router::GenericUpstream { +public: + HttpUpstream(Router::UpstreamToDownstream& upstream_request, Envoy::Http::RequestEncoder* encoder, + Upstream::HostDescriptionConstSharedPtr host) + : sub_upstream_(upstream_request, encoder), host_(host) {} + + // GenericUpstream + void encodeData(Buffer::Instance& data, bool end_stream) override { + sub_upstream_.encodeData(data, end_stream); + } + + void encodeMetadata(const Envoy::Http::MetadataMapVector& metadata_map_vector) override { + sub_upstream_.encodeMetadata(metadata_map_vector); + } + + void encodeHeaders(const Envoy::Http::RequestHeaderMap& headers, bool end_stream) override { + auto dup = Envoy::Http::RequestHeaderMapImpl::create(); + Envoy::Http::HeaderMapImpl::copyFrom(*dup, headers); + dup->setCopy(Envoy::Http::LowerCaseString("X-istio-test"), "lambdai"); + // Sanitize original port header. + dup->remove(Envoy::Http::LowerCaseString("X-istio-original-port")); + if (auto filter_metadata = host_->cluster().metadata().filter_metadata().find("istio"); + filter_metadata != host_->cluster().metadata().filter_metadata().end()) { + ENVOY_LOG_MISC(warn, "lambdai: find filter_metadata from {}", + host_->cluster().metadata().DebugString()); + const ProtobufWkt::Struct& data_struct = filter_metadata->second; + const auto& fields = data_struct.fields(); + if (auto iter = fields.find("original_port"); iter != fields.end()) { + if (iter->second.kind_case() == ProtobufWkt::Value::kStringValue) { + dup->setCopy(Envoy::Http::LowerCaseString("X-istio-original-port"), + iter->second.string_value()); + } + } + } + sub_upstream_.encodeHeaders(*dup, end_stream); + } + + void encodeTrailers(const Envoy::Http::RequestTrailerMap& trailers) override { + sub_upstream_.encodeTrailers(trailers); + } + + void readDisable(bool disable) override { sub_upstream_.readDisable(disable); } + + void resetStream() override { sub_upstream_.resetStream(); } + +private: + Upstreams::Http::Http::HttpUpstream sub_upstream_; + Upstream::HostDescriptionConstSharedPtr host_{}; +}; + +} // namespace ClusterSensitive +} // namespace Http +} // namespace Upstreams +} // namespace Extensions +} // namespace Envoy diff --git a/source/server/BUILD b/source/server/BUILD index 6c587d9d95d7b..544bb20aea75b 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -337,6 +337,7 @@ envoy_cc_library( "//source/extensions/filters/listener:well_known_names", "//source/extensions/filters/network/http_connection_manager:config", "//source/extensions/transport_sockets:well_known_names", + "//source/extensions/upstreams/http/cluster_sensitive:config", "//source/extensions/upstreams/http/generic:config", "@envoy_api//envoy/admin/v3:pkg_cc_proto", "@envoy_api//envoy/api/v2:pkg_cc_proto", diff --git a/test/common/http/BUILD b/test/common/http/BUILD index 7b19f269bbdd1..ab887db80d33b 100644 --- a/test/common/http/BUILD +++ b/test/common/http/BUILD @@ -23,6 +23,7 @@ envoy_cc_test( "//source/common/http:context_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", + "//source/extensions/upstreams/http/cluster_sensitive:config", "//source/extensions/upstreams/http/generic:config", "//test/mocks:common_lib", "//test/mocks/buffer:buffer_mocks", diff --git a/test/common/router/BUILD b/test/common/router/BUILD index 51121e73b8a4b..757c5f701bf83 100644 --- a/test/common/router/BUILD +++ b/test/common/router/BUILD @@ -290,6 +290,7 @@ envoy_cc_test( "//source/common/stream_info:uint32_accessor_lib", "//source/common/upstream:upstream_includes", "//source/common/upstream:upstream_lib", + "//source/extensions/upstreams/http/cluster_sensitive:config", "//source/extensions/upstreams/http/generic:config", "//source/extensions/upstreams/http/http:config", "//source/extensions/upstreams/http/tcp:config", @@ -325,6 +326,7 @@ envoy_cc_test( "//source/common/upstream:upstream_includes", "//source/common/upstream:upstream_lib", "//source/extensions/access_loggers/file:config", + "//source/extensions/upstreams/http/cluster_sensitive:config", "//source/extensions/upstreams/http/generic:config", "//test/common/http:common_lib", "//test/mocks/access_log:access_log_mocks", diff --git a/test/extensions/filters/http/common/fuzz/BUILD b/test/extensions/filters/http/common/fuzz/BUILD index ffc67eb4a2321..36ca528a84a45 100644 --- a/test/extensions/filters/http/common/fuzz/BUILD +++ b/test/extensions/filters/http/common/fuzz/BUILD @@ -73,6 +73,7 @@ envoy_cc_fuzz_test( "//source/common/config:utility_lib", "//source/common/protobuf:utility_lib", "//source/extensions/upstreams/http/generic:config", + "//source/extensions/upstreams/http/cluster_sensitive:config", "//test/config:utility_lib", "@envoy_api//envoy/service/auth/v3:pkg_cc_proto", "@envoy_api//envoy/service/auth/v2alpha:pkg_cc_proto", diff --git a/test/extensions/upstreams/http/cluster_sensitive/BUILD b/test/extensions/upstreams/http/cluster_sensitive/BUILD new file mode 100644 index 0000000000000..bac965b7dd537 --- /dev/null +++ b/test/extensions/upstreams/http/cluster_sensitive/BUILD @@ -0,0 +1,33 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "upstream_request_test", + srcs = ["upstream_request_test.cc"], + deps = [ + "//source/common/buffer:buffer_lib", + "//source/common/network:address_lib", + "//source/common/router:router_lib", + "//source/common/upstream:upstream_includes", + "//source/common/upstream:upstream_lib", + "//source/extensions/upstreams/http/cluster_sensitive:upstream_request_lib", + "//test/common/http:common_lib", + "//test/mocks:common_lib", + "//test/mocks/network:network_mocks", + "//test/mocks/router:router_filter_interface", + "//test/mocks/router:router_mocks", + "//test/mocks/server:factory_context_mocks", + "//test/mocks/server:instance_mocks", + "//test/mocks/upstream:upstream_mocks", + "//test/test_common:environment_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/upstreams/http/cluster_sensitive/upstream_request_test.cc b/test/extensions/upstreams/http/cluster_sensitive/upstream_request_test.cc new file mode 100644 index 0000000000000..9bd8c9b4dba4f --- /dev/null +++ b/test/extensions/upstreams/http/cluster_sensitive/upstream_request_test.cc @@ -0,0 +1,222 @@ +#include "common/buffer/buffer_impl.h" +#include "common/network/address_impl.h" +#include "common/router/config_impl.h" +#include "common/router/router.h" +#include "common/router/upstream_request.h" + +#include "extensions/upstreams/http/cluster_sensitive/http_upstream_request.h" + +#include "test/common/http/common.h" +#include "test/mocks/common.h" +#include "test/mocks/http/stream_encoder.h" +#include "test/mocks/router/mocks.h" +#include "test/mocks/router/router_filter_interface.h" +#include "test/mocks/server/factory_context.h" +#include "test/mocks/server/instance.h" +#include "test/mocks/tcp/mocks.h" +#include "test/mocks/upstream/host.h" +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using Envoy::Http::TestRequestHeaderMapImpl; +using Envoy::Router::UpstreamRequest; +using testing::_; +using testing::AnyNumber; +using testing::NiceMock; +using testing::Return; +using testing::ReturnRef; + +namespace Envoy { +namespace Extensions { +namespace Upstreams { +namespace Http { +namespace ClusterSensitive { + +// class ClusterSensitiveConnPoolTest : public ::testing::Test { +// public: +// ClusterSensitiveConnPoolTest() : host_(std::make_shared>()) { +// NiceMock route_entry; +// NiceMock cm; +// EXPECT_CALL(cm, tcpConnPoolForCluster(_, _, _)).WillOnce(Return(&mock_pool_)); +// conn_pool_ = std::make_unique(cm, true, route_entry, +// Envoy::Http::Protocol::Http11, nullptr); +// } + +// std::unique_ptr conn_pool_; +// Envoy::Tcp::ConnectionPool::MockInstance mock_pool_; +// Router::MockGenericConnectionPoolCallbacks mock_generic_callbacks_; +// std::shared_ptr> host_; +// NiceMock cancellable_; +// }; + +// TEST_F(ClusterSensitiveConnPoolTest, Basic) { +// NiceMock connection; + +// EXPECT_CALL(mock_pool_, newConnection(_)).WillOnce(Return(&cancellable_)); +// conn_pool_->newStream(&mock_generic_callbacks_); + +// EXPECT_CALL(mock_generic_callbacks_, upstreamToDownstream()); +// EXPECT_CALL(mock_generic_callbacks_, onPoolReady(_, _, _, _)); +// auto data = std::make_unique>(); +// EXPECT_CALL(*data, connection()).Times(AnyNumber()).WillRepeatedly(ReturnRef(connection)); +// conn_pool_->onPoolReady(std::move(data), host_); +// } + +// TEST_F(ClusterSensitiveConnPoolTest, OnPoolFailure) { +// EXPECT_CALL(mock_pool_, newConnection(_)).WillOnce(Return(&cancellable_)); +// conn_pool_->newStream(&mock_generic_callbacks_); + +// EXPECT_CALL(mock_generic_callbacks_, onPoolFailure(_, _, _)); +// conn_pool_->onPoolFailure(Envoy::Tcp::ConnectionPool::PoolFailureReason::LocalConnectionFailure, +// host_); + +// // Make sure that the pool failure nulled out the pending request. +// EXPECT_FALSE(conn_pool_->cancelAnyPendingStream()); +// } + +// TEST_F(ClusterSensitiveConnPoolTest, Cancel) { +// // Initially cancel should fail as there is no pending request. +// EXPECT_FALSE(conn_pool_->cancelAnyPendingStream()); + +// EXPECT_CALL(mock_pool_, newConnection(_)).WillOnce(Return(&cancellable_)); +// conn_pool_->newStream(&mock_generic_callbacks_); + +// // Canceling should now return true as there was an active request. +// EXPECT_TRUE(conn_pool_->cancelAnyPendingStream()); + +// // A second cancel should return false as there is not a pending request. +// EXPECT_FALSE(conn_pool_->cancelAnyPendingStream()); +// } + +class HttpUpstreamTest : public ::testing::Test { +public: + HttpUpstreamTest() { + cluster_metadata_ = std::make_shared( + TestUtility::parseYaml( + R"EOF( + filter_metadata: + istio: + original_port: "80" + )EOF")); + } + + ~HttpUpstreamTest() override {} + +protected: + Router::MockUpstreamToDownstream mock_upstream_to_downstream_; + // NiceMock mock_router_filter_; + // std::unique_ptr http_upstream_; + TestRequestHeaderMapImpl request_{{":method", "CONNECT"}, + {":path", "/"}, + {":protocol", "bytestream"}, + {":scheme", "https"}, + {":authority", "host"}}; + std::shared_ptr cluster_metadata_; +}; + +TEST_F(HttpUpstreamTest, TestAddedHeader) { + NiceMock encoder; + std::shared_ptr> host = + std::make_shared>(); + std::shared_ptr info{ + new ::testing::NiceMock()}; + ON_CALL(*host, cluster()).WillByDefault(ReturnRef(*info)); + Envoy::Http::TestRequestHeaderMapImpl headers{}; + HttpTestUtility::addDefaultHeaders(headers); + auto http_upstream = std::make_unique(mock_upstream_to_downstream_, &encoder, host); + EXPECT_CALL(encoder, encodeHeaders(HeaderHasValueRef("x-istio-test", "lambdai"), false)); + http_upstream->encodeHeaders(headers, false); +} + +TEST_F(HttpUpstreamTest, TestAddClusterInfo) { + NiceMock encoder; + std::shared_ptr> host = + std::make_shared>(); + std::shared_ptr info{ + new ::testing::NiceMock()}; + ON_CALL(*host, cluster()).WillByDefault(ReturnRef(*info)); + ON_CALL(*info, metadata()).WillByDefault(ReturnRef(*cluster_metadata_)); + auto http_upstream = std::make_unique(mock_upstream_to_downstream_, &encoder, host); + Envoy::Http::TestRequestHeaderMapImpl headers{}; + HttpTestUtility::addDefaultHeaders(headers); + EXPECT_CALL(encoder, encodeHeaders(HeaderHasValueRef("X-istio-original-port", "80"), false)); + http_upstream->encodeHeaders(headers, false); +} + +TEST_F(HttpUpstreamTest, TestBasicFlow) { + NiceMock encoder; + std::shared_ptr> host = + std::make_shared>(); + std::shared_ptr info{ + new ::testing::NiceMock()}; + ON_CALL(*host, cluster()).WillByDefault(ReturnRef(*info)); + Envoy::Http::TestRequestHeaderMapImpl headers{}; + HttpTestUtility::addDefaultHeaders(headers); + auto http_upstream = std::make_unique(mock_upstream_to_downstream_, &encoder, host); + { + EXPECT_CALL(encoder, encodeHeaders(HeaderHasValueRef("x-istio-test", "lambdai"), false)); + http_upstream->encodeHeaders(headers, false); + + auto metadata_map_ptr = std::make_unique(); + metadata_map_ptr->emplace("key", "value"); + Envoy::Http::MetadataMapVector metadata_map_vector; + metadata_map_vector.push_back(std::move(metadata_map_ptr)); + EXPECT_CALL(encoder, encodeMetadata(testing::Ref(metadata_map_vector))); + http_upstream->encodeMetadata(metadata_map_vector); + + Buffer::OwnedImpl body("abc"); + EXPECT_CALL(encoder, encodeData(BufferEqual(&body), false)); + http_upstream->encodeData(body, false); + + Envoy::Http::TestRequestTrailerMapImpl trailers{{"some", "request_trailer"}}; + EXPECT_CALL(encoder, encodeTrailers(HeaderMapEqualRef(&trailers))); + http_upstream->encodeTrailers(trailers); + } +} + +TEST_F(HttpUpstreamTest, TestReadDisable) { + + NiceMock encoder; + std::shared_ptr> host = + std::make_shared>(); + std::shared_ptr info{ + new ::testing::NiceMock()}; + ON_CALL(*host, cluster()).WillByDefault(ReturnRef(*info)); + Envoy::Http::TestRequestHeaderMapImpl headers{}; + HttpTestUtility::addDefaultHeaders(headers); + auto http_upstream = std::make_unique(mock_upstream_to_downstream_, &encoder, host); + + Envoy::Http::MockStream stream; + ON_CALL(encoder, getStream()).WillByDefault(ReturnRef(stream)); + EXPECT_CALL(stream, readDisable(true)); + http_upstream->readDisable(true); + + EXPECT_CALL(stream, readDisable(false)); + http_upstream->readDisable(false); +} + +TEST_F(HttpUpstreamTest, TestResetStream) { + NiceMock encoder; + std::shared_ptr> host = + std::make_shared>(); + std::shared_ptr info{ + new ::testing::NiceMock()}; + ON_CALL(*host, cluster()).WillByDefault(ReturnRef(*info)); + Envoy::Http::TestRequestHeaderMapImpl headers{}; + HttpTestUtility::addDefaultHeaders(headers); + auto http_upstream = std::make_unique(mock_upstream_to_downstream_, &encoder, host); + + Envoy::Http::MockStream stream; + ON_CALL(encoder, getStream()).WillByDefault(ReturnRef(stream)); + + EXPECT_CALL(stream, removeCallbacks(_)); + EXPECT_CALL(stream, resetStream(Envoy::Http::StreamResetReason::LocalReset)); + http_upstream->resetStream(); +} +} // namespace ClusterSensitive +} // namespace Http +} // namespace Upstreams +} // namespace Extensions +} // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index e3dbfb5d8731e..d87909b876985 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -1011,6 +1011,7 @@ envoy_cc_test( coverage = False, deps = [ ":http_protocol_integration_lib", + "//source/extensions/upstreams/http/cluster_sensitive:config", "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", ], ) diff --git a/tools/envoy_headersplit/code_corpus/fake_build b/tools/envoy_headersplit/code_corpus/fake_build index bce4828828a09..15389dcec1656 100644 --- a/tools/envoy_headersplit/code_corpus/fake_build +++ b/tools/envoy_headersplit/code_corpus/fake_build @@ -8,6 +8,7 @@ envoy_cc_test( "//source/common/http:context_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", + "//source/extensions/upstreams/http/cluster_sensitive:config", "//source/extensions/upstreams/http/generic:config", "//test/mocks:common_lib", "//test/mocks/buffer:buffer_mocks",