From 0e8c581fc7ed26d8d42f3655af0d26ebf87bd034 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 22 Jun 2021 19:00:39 +0000 Subject: [PATCH 01/49] Add a new HappyEyeballsConnectionImpl Signed-off-by: Ryan Hamilton --- source/common/network/BUILD | 10 ++ .../network/happy_eyeballs_connection_impl.cc | 153 ++++++++++++++++++ .../network/happy_eyeballs_connection_impl.h | 95 +++++++++++ source/common/upstream/BUILD | 1 + source/common/upstream/upstream_impl.cc | 4 + test/common/network/BUILD | 10 ++ .../happy_eyeballs_connection_impl_test.cc | 27 ++++ 7 files changed, 300 insertions(+) create mode 100644 source/common/network/happy_eyeballs_connection_impl.cc create mode 100644 source/common/network/happy_eyeballs_connection_impl.h create mode 100644 test/common/network/happy_eyeballs_connection_impl_test.cc diff --git a/source/common/network/BUILD b/source/common/network/BUILD index 529e25ad6c05d..6d85e13184c8a 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -98,6 +98,16 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "happy_eyeballs_connection_impl_lib", + srcs = ["happy_eyeballs_connection_impl.cc"], + hdrs = ["happy_eyeballs_connection_impl.h"], + deps = [ + ":connection_base_lib", + ":connection_lib", + ], +) + envoy_cc_library( name = "apple_dns_lib", srcs = select({ diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc new file mode 100644 index 0000000000000..2c03367fc7ff5 --- /dev/null +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -0,0 +1,153 @@ +#include "source/common/network/happy_eyeballs_connection_impl.h" + +namespace Envoy { +namespace Network { + +HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( + Event::Dispatcher& dispatcher, + Network::Address::InstanceConstSharedPtr address, + Network::Address::InstanceConstSharedPtr source_address, + Network::TransportSocketFactory& socket_factory, + Network::TransportSocketOptionsSharedPtr transport_socket_options, + const Network::ConnectionSocket::OptionsSharedPtr options) + : dispatcher_(dispatcher), + address_(address), + source_address_(source_address), + socket_factory_(socket_factory), + transport_socket_options_(transport_socket_options), + options_(options) { + connection_ = std::make_unique(dispatcher_, + address_, source_address_, + socket_factory_.createTransportSocket(std::move(transport_socket_options_)), + options_); +} + +HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; + +void HappyEyeballsConnectionImpl::connect() { + connection_->connect(); +} + +void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { + connection_->addWriteFilter(filter); +} +void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { + connection_->addFilter(filter); +} +void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { + connection_->addReadFilter(filter); +} +void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { + connection_->removeReadFilter(filter); +} +bool HappyEyeballsConnectionImpl::initializeReadFilters() { + return connection_->initializeReadFilters(); +} + +void HappyEyeballsConnectionImpl::addBytesSentCallback(ClientConnection::BytesSentCb cb) { + connection_->addBytesSentCallback(cb); +} +void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { + connection_->enableHalfClose(enabled); +} +bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { + return connection_->isHalfCloseEnabled(); +} +std::string HappyEyeballsConnectionImpl::nextProtocol() const { + return connection_->nextProtocol(); +} +void HappyEyeballsConnectionImpl::noDelay(bool enable) { + connection_->noDelay(enable); +} +void HappyEyeballsConnectionImpl::readDisable(bool disable) { + connection_->readDisable(disable); +} +void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { + connection_->detectEarlyCloseWhenReadDisabled(value); +} +bool HappyEyeballsConnectionImpl::readEnabled() const { + return connection_->readEnabled(); +} +const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { + return connection_->addressProvider(); +} +SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { + return connection_->addressProviderSharedPtr(); +} +absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { + return connection_->unixSocketPeerCredentials(); +} +Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { + return connection_->ssl(); +} +Connection::State HappyEyeballsConnectionImpl::state() const { + return connection_->state(); +} +bool HappyEyeballsConnectionImpl::connecting() const { + return connection_->connecting(); +} +void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) { + connection_->write(data, end_stream); +} +void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { + connection_->setBufferLimits(limit); +} +uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { + return connection_->bufferLimit(); +} +bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { + return connection_->aboveHighWatermark(); +} +const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { + return connection_->socketOptions(); +} +absl::string_view HappyEyeballsConnectionImpl::requestedServerName() const { + return connection_->requestedServerName(); +} +StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() { + return connection_->streamInfo(); +} +const StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() const { + return connection_->streamInfo(); +} +absl::string_view HappyEyeballsConnectionImpl::transportFailureReason() const { + return connection_->transportFailureReason(); +} +bool HappyEyeballsConnectionImpl::startSecureTransport() { + return connection_->startSecureTransport(); +} +absl::optional HappyEyeballsConnectionImpl::lastRoundTripTime() const { + return connection_->lastRoundTripTime(); +} +void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb) { + connection_->addConnectionCallbacks(cb); +} +void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& cb) { + connection_->removeConnectionCallbacks(cb); +} +void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { + connection_->close(type); +} +Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { + return connection_->dispatcher(); +} +uint64_t HappyEyeballsConnectionImpl::id() const { + return connection_->id(); +} +void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { + connection_->hashKey(hash); +} +void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stats) { + connection_->setConnectionStats(stats); +} +void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::milliseconds timeout) { + connection_->setDelayedCloseTimeout(timeout); +} + +// ScopeTrackedObject +void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) const { + connection_->dumpState(os, indent_level); +} + +} // namespace Network +} // namespace Envoy diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h new file mode 100644 index 0000000000000..f4ad5eda50f7a --- /dev/null +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "envoy/common/scope_tracker.h" +#include "envoy/network/connection.h" +#include "envoy/network/transport_socket.h" + +#include "absl/types/optional.h" + +#include "source/common/network/connection_impl.h" + +namespace Envoy { +namespace Network { + +/** + * Implementation of Network::Connection, Network::FilterManagerConnection and + * Envoy::ScopeTrackedObject. + */ +class HappyEyeballsConnectionImpl : public ClientConnection { +public: + HappyEyeballsConnectionImpl(Event::Dispatcher& dispatcher, + Network::Address::InstanceConstSharedPtr address, + Network::Address::InstanceConstSharedPtr source_address, + Network::TransportSocketFactory& socket_factory, + Network::TransportSocketOptionsSharedPtr transport_socket_options, + const Network::ConnectionSocket::OptionsSharedPtr options); + + ~HappyEyeballsConnectionImpl() override; + + // Network::ClientConnection + void connect() override; + + // Network::FilterManager + void addWriteFilter(WriteFilterSharedPtr filter) override; + void addFilter(FilterSharedPtr filter) override; + void addReadFilter(ReadFilterSharedPtr filter) override; + void removeReadFilter(ReadFilterSharedPtr filter) override; + bool initializeReadFilters() override; + + // Network::Connection + void addBytesSentCallback(BytesSentCb cb) override; + void enableHalfClose(bool enabled) override; + bool isHalfCloseEnabled() override; + std::string nextProtocol() const override; + void noDelay(bool enable) override; + void readDisable(bool disable) override; + void detectEarlyCloseWhenReadDisabled(bool value) override; + bool readEnabled() const override; + const SocketAddressProvider& addressProvider() const override; + SocketAddressProviderSharedPtr addressProviderSharedPtr() const override; + absl::optional unixSocketPeerCredentials() const override; + Ssl::ConnectionInfoConstSharedPtr ssl() const override; + State state() const override; + bool connecting() const override; + void write(Buffer::Instance& data, bool end_stream) override; + void setBufferLimits(uint32_t limit) override; + uint32_t bufferLimit() const override; + bool aboveHighWatermark() const override; + const ConnectionSocket::OptionsSharedPtr& socketOptions() const override; + absl::string_view requestedServerName() const override; + StreamInfo::StreamInfo& streamInfo() override; + const StreamInfo::StreamInfo& streamInfo() const override; + absl::string_view transportFailureReason() const override; + bool startSecureTransport() override; + absl::optional lastRoundTripTime() const override; + void addConnectionCallbacks(ConnectionCallbacks& cb) override; + void removeConnectionCallbacks(ConnectionCallbacks& cb) override; + void close(ConnectionCloseType type) override; + Event::Dispatcher& dispatcher() override; + uint64_t id() const override; + void hashKey(std::vector& hash) const override; + void setConnectionStats(const ConnectionStats& stats) override; + void setDelayedCloseTimeout(std::chrono::milliseconds timeout) override; + + // ScopeTrackedObject + void dumpState(std::ostream& os, int indent_level) const override; + +private: + std::unique_ptr connection_; + std::unique_ptr pending_connection_; + Event::Dispatcher& dispatcher_; + Network::Address::InstanceConstSharedPtr address_; + Network::Address::InstanceConstSharedPtr source_address_; + Network::TransportSocketFactory& socket_factory_; + Network::TransportSocketOptionsSharedPtr transport_socket_options_; + const Network::ConnectionSocket::OptionsSharedPtr options_; +}; + +} // namespace Network +} // namespace Envoy diff --git a/source/common/upstream/BUILD b/source/common/upstream/BUILD index 901d530d882df..77cb9030af5a9 100644 --- a/source/common/upstream/BUILD +++ b/source/common/upstream/BUILD @@ -471,6 +471,7 @@ envoy_cc_library( "//source/common/http/http2:codec_stats_lib", "//source/common/http:utility_lib", "//source/common/network:address_lib", + "//source/common/network:happy_eyeballs_connection_impl_lib", "//source/common/network:resolver_lib", "//source/common/network:socket_option_factory_lib", "//source/common/network:socket_option_lib", diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 83169b162d354..599105b1ca12e 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -36,6 +36,7 @@ #include "source/common/http/http2/codec_stats.h" #include "source/common/http/utility.h" #include "source/common/network/address_impl.h" +#include "source/common/network/happy_eyeballs_connection_impl.h" #include "source/common/network/resolver_impl.h" #include "source/common/network/socket_option_factory.h" #include "source/common/network/socket_option_impl.h" @@ -337,6 +338,9 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu connection_options = options; } ASSERT(!address->envoyInternalAddress()); + if (true) { + return std::make_unique(dispatcher, address, cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options); + } Network::ClientConnectionPtr connection = dispatcher.createClientConnection( address, cluster.sourceAddress(), socket_factory.createTransportSocket(std::move(transport_socket_options)), diff --git a/test/common/network/BUILD b/test/common/network/BUILD index ce5f097cd5f5e..f0a2729e79531 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -97,6 +97,16 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "happy_eyeballs_connection_impl_test", + srcs = ["happy_eyeballs_connection_impl_test.cc"], + deps = [ + "//source/common/network:happy_eyeballs_connection_impl_lib", + "//test/mocks/event:event_mocks", + "//test/mocks/network:network_mocks", + ], +) + envoy_cc_test( name = "apple_dns_impl_test", srcs = select({ diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc new file mode 100644 index 0000000000000..1b42850ba31fc --- /dev/null +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -0,0 +1,27 @@ +#include "source/common/network/happy_eyeballs_connection_impl.h" + +#include "test/mocks/event/mocks.h" +#include "test/mocks/network/transport_socket.h" + +namespace Envoy { +namespace Network { + +void voo() { + Event::MockDispatcher dispatcher_; + MockTransportSocketFactory transport_socket_factory; + Network::TransportSocketOptionsSharedPtr transport_socket_options; + const Network::ConnectionSocket::OptionsSharedPtr options; + + + HappyEyeballsConnectionImpl impl_(dispatcher_, + Network::Address::InstanceConstSharedPtr(), + Network::Address::InstanceConstSharedPtr(), + transport_socket_factory, + transport_socket_options, + options); +} + + + +} // namespace Network +} // namespace Envoy From 2d0bcdcbc5e8bd850be0cae17bf8b3948b085c8c Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 2 Jul 2021 17:15:29 +0000 Subject: [PATCH 02/49] Cleanup Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 12 +++-- .../network/happy_eyeballs_connection_impl.h | 6 ++- source/common/upstream/upstream_impl.cc | 17 ++++--- test/common/network/BUILD | 1 + .../happy_eyeballs_connection_impl_test.cc | 50 +++++++++++++------ 5 files changed, 58 insertions(+), 28 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 2c03367fc7ff5..c70bea6de5e73 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -16,10 +16,7 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( socket_factory_(socket_factory), transport_socket_options_(transport_socket_options), options_(options) { - connection_ = std::make_unique(dispatcher_, - address_, source_address_, - socket_factory_.createTransportSocket(std::move(transport_socket_options_)), - options_); + connection_ = createConnection(); } HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; @@ -149,5 +146,12 @@ void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) connection_->dumpState(os, indent_level); } +std::unique_ptr HappyEyeballsConnectionImpl::createConnection() { + return dispatcher_.createClientConnection( + address_, source_address_, + socket_factory_.createTransportSocket(transport_socket_options_), + options_); +} + } // namespace Network } // namespace Envoy diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index f4ad5eda50f7a..ad400f9c0966e 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -81,8 +81,10 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void dumpState(std::ostream& os, int indent_level) const override; private: - std::unique_ptr connection_; - std::unique_ptr pending_connection_; + std::unique_ptr createConnection(); + + std::unique_ptr connection_; + std::unique_ptr pending_connection_; Event::Dispatcher& dispatcher_; Network::Address::InstanceConstSharedPtr address_; Network::Address::InstanceConstSharedPtr source_address_; diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 599105b1ca12e..67801aed2bc36 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -338,13 +338,16 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu connection_options = options; } ASSERT(!address->envoyInternalAddress()); - if (true) { - return std::make_unique(dispatcher, address, cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options); - } - Network::ClientConnectionPtr connection = dispatcher.createClientConnection( - address, cluster.sourceAddress(), - socket_factory.createTransportSocket(std::move(transport_socket_options)), - connection_options); + Network::ClientConnectionPtr connection = + + true ? + std::make_unique(dispatcher, address, cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options) + : + dispatcher.createClientConnection( + address, cluster.sourceAddress(), + socket_factory.createTransportSocket(std::move(transport_socket_options)), + connection_options); + connection->setBufferLimits(cluster.perConnectionBufferLimitBytes()); cluster.createNetworkFilterChain(*connection); return connection; diff --git a/test/common/network/BUILD b/test/common/network/BUILD index f0a2729e79531..ff5fe23e0d627 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -102,6 +102,7 @@ envoy_cc_test( srcs = ["happy_eyeballs_connection_impl_test.cc"], deps = [ "//source/common/network:happy_eyeballs_connection_impl_lib", + "//source/common/network:socket_option_lib", "//test/mocks/event:event_mocks", "//test/mocks/network:network_mocks", ], diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 1b42850ba31fc..ea7504ee1b222 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -1,27 +1,47 @@ #include "source/common/network/happy_eyeballs_connection_impl.h" +#include "source/common/network/transport_socket_options_impl.h" + #include "test/mocks/event/mocks.h" #include "test/mocks/network/transport_socket.h" +#include "test/mocks/network/connection.h" + +using testing::Return; namespace Envoy { namespace Network { -void voo() { +class HappyEyeballsConnectionImplTest : public testing::Test { + public: + HappyEyeballsConnectionImplTest() + : transport_socket_options_(std::make_shared()), + options_(std::make_shared()) { + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + connection_ = new NiceMock(); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce(Return(connection_)); + impl_ = std::make_unique(dispatcher_, + Network::Address::InstanceConstSharedPtr(), + Network::Address::InstanceConstSharedPtr(), + transport_socket_factory_, + transport_socket_options_, + options_); + + + } + + protected: Event::MockDispatcher dispatcher_; - MockTransportSocketFactory transport_socket_factory; - Network::TransportSocketOptionsSharedPtr transport_socket_options; - const Network::ConnectionSocket::OptionsSharedPtr options; - - - HappyEyeballsConnectionImpl impl_(dispatcher_, - Network::Address::InstanceConstSharedPtr(), - Network::Address::InstanceConstSharedPtr(), - transport_socket_factory, - transport_socket_options, - options); -} - - + MockTransportSocketFactory transport_socket_factory_; + TransportSocketOptionsSharedPtr transport_socket_options_; + const ConnectionSocket::OptionsSharedPtr options_; + Network::MockClientConnection* connection_; + std::unique_ptr impl_; +}; + +TEST_F(HappyEyeballsConnectionImplTest, Connect) { + EXPECT_CALL(*connection_, connect()); + impl_->connect(); +}; } // namespace Network } // namespace Envoy From 7d9f86b78cbfdb24fd3a63b7edd3d79fd573ade9 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Mon, 28 Jun 2021 21:52:12 +0000 Subject: [PATCH 03/49] WIP Signed-off-by: Ryan Hamilton --- source/common/conn_pool/conn_pool_base.cc | 11 ++ source/common/http/conn_pool_base.h | 2 + .../network/happy_eyeballs_connection_impl.cc | 133 +++++++++++++++++- .../network/happy_eyeballs_connection_impl.h | 44 ++++++ source/common/upstream/health_checker_impl.cc | 10 ++ 5 files changed, 194 insertions(+), 6 deletions(-) diff --git a/source/common/conn_pool/conn_pool_base.cc b/source/common/conn_pool/conn_pool_base.cc index c6276446f4a9d..381d6a46ff352 100644 --- a/source/common/conn_pool/conn_pool_base.cc +++ b/source/common/conn_pool/conn_pool_base.cc @@ -388,6 +388,7 @@ void ConnPoolImplBase::checkForDrained() { void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view failure_reason, Network::ConnectionEvent event) { + std::cerr << __FUNCTION__ << " here! 1\n"; if (client.state() == ActiveClient::State::CONNECTING) { ASSERT(connecting_stream_capacity_ >= client.effectiveConcurrentStreamLimit()); connecting_stream_capacity_ -= client.effectiveConcurrentStreamLimit(); @@ -398,6 +399,7 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view client.connect_timer_.reset(); } + std::cerr << __FUNCTION__ << " here! 2\n"; if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { state_.decrConnectingAndConnectedStreamCapacity(client.currentUnusedCapacity()); @@ -454,16 +456,25 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view tryCreateNewConnections(); } } else if (event == Network::ConnectionEvent::Connected) { + std::cerr << __FUNCTION__ << " here! 3 " << client.conn_connect_ms_.get() << "\n"; client.conn_connect_ms_->complete(); + std::cerr << __FUNCTION__ << " here! 4\n"; client.conn_connect_ms_.reset(); + std::cerr << __FUNCTION__ << " here! 4\n"; ASSERT(client.state() == ActiveClient::State::CONNECTING); + std::cerr << __FUNCTION__ << " here! 4\n"; transitionActiveClientState(client, ActiveClient::State::READY); + std::cerr << __FUNCTION__ << " here! 4\n"; // At this point, for the mixed ALPN pool, the client may be deleted. Do not // refer to client after this point. + std::cerr << __FUNCTION__ << " here! 5\n"; onConnected(client); + std::cerr << __FUNCTION__ << " here! 6\n"; onUpstreamReady(); + std::cerr << __FUNCTION__ << " here! 7\n"; checkForDrained(); + std::cerr << __FUNCTION__ << " here! 8\n"; } } diff --git a/source/common/http/conn_pool_base.h b/source/common/http/conn_pool_base.h index 0d72454786204..1e87f8cd5be70 100644 --- a/source/common/http/conn_pool_base.h +++ b/source/common/http/conn_pool_base.h @@ -124,6 +124,8 @@ class ActiveClient : public Envoy::ConnectionPool::ActiveClient { void close() override { codec_client_->close(); } virtual Http::RequestEncoder& newStreamEncoder(Http::ResponseDecoder& response_decoder) PURE; void onEvent(Network::ConnectionEvent event) override { + std::cerr << __FUNCTION__ << " here! this: " << this << " event: " << static_cast(event) << " codec_client_:" << codec_client_.get() << "\n"; + //ASSERT(false); parent_.onConnectionEvent(*this, codec_client_->connectionFailureReason(), event); } uint32_t numActiveStreams() const override { return codec_client_->numActiveRequests(); } diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index c70bea6de5e73..6b92d2d65fb2a 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -22,122 +22,194 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; void HappyEyeballsConnectionImpl::connect() { + ASSERT(!connect_finished_); connection_->connect(); } void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { + ASSERT(connect_finished_); connection_->addWriteFilter(filter); } void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { + ASSERT(connect_finished_); connection_->addFilter(filter); } void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { - connection_->addReadFilter(filter); + if (connect_finished_) { + connection_->addReadFilter(filter); + return; + } + read_filters_.push_back(filter); } void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { + ASSERT(connect_finished_); connection_->removeReadFilter(filter); } bool HappyEyeballsConnectionImpl::initializeReadFilters() { + ASSERT(connect_finished_); return connection_->initializeReadFilters(); } void HappyEyeballsConnectionImpl::addBytesSentCallback(ClientConnection::BytesSentCb cb) { + ASSERT(connect_finished_); connection_->addBytesSentCallback(cb); } void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { + ASSERT(connect_finished_); connection_->enableHalfClose(enabled); } bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { + ASSERT(connect_finished_); return connection_->isHalfCloseEnabled(); } std::string HappyEyeballsConnectionImpl::nextProtocol() const { + ASSERT(connect_finished_); return connection_->nextProtocol(); } void HappyEyeballsConnectionImpl::noDelay(bool enable) { + if (!connect_finished_) { + state_.noDelay_ = enable; + } connection_->noDelay(enable); } void HappyEyeballsConnectionImpl::readDisable(bool disable) { + ASSERT(connect_finished_); connection_->readDisable(disable); } void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { + if (!connect_finished_) { + state_.detectEarlyCloseWhenReadDisabled_ = value; + } connection_->detectEarlyCloseWhenReadDisabled(value); } bool HappyEyeballsConnectionImpl::readEnabled() const { - return connection_->readEnabled(); + ASSERT(connect_finished_); + return connection_->readEnabled(); } const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { + ASSERT(connect_finished_); return connection_->addressProvider(); } SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { + ASSERT(connect_finished_); return connection_->addressProviderSharedPtr(); } absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { + ASSERT(connect_finished_); return connection_->unixSocketPeerCredentials(); } Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { + ASSERT(connect_finished_); return connection_->ssl(); } Connection::State HappyEyeballsConnectionImpl::state() const { + ASSERT(connect_finished_); return connection_->state(); } bool HappyEyeballsConnectionImpl::connecting() const { + ASSERT(connect_finished_ || connection_->connecting()); return connection_->connecting(); } void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) { - connection_->write(data, end_stream); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + if (connect_finished_) { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + connection_->write(data, end_stream); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + return; + } + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + //state_.write_buffer_ = std::make_unique(); + state_.write_buffer_->move(data); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + state_.end_stream_ = end_stream; + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { + ASSERT(connect_finished_); connection_->setBufferLimits(limit); } uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { + // TODO - huh? + //ASSERT(connect_finished_); return connection_->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { + ASSERT(connect_finished_); return connection_->aboveHighWatermark(); } const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { + ASSERT(connect_finished_); return connection_->socketOptions(); } absl::string_view HappyEyeballsConnectionImpl::requestedServerName() const { + ASSERT(connect_finished_); return connection_->requestedServerName(); } StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() { + ASSERT(connect_finished_); return connection_->streamInfo(); } const StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() const { + ASSERT(connect_finished_); return connection_->streamInfo(); } absl::string_view HappyEyeballsConnectionImpl::transportFailureReason() const { + ASSERT(connect_finished_); return connection_->transportFailureReason(); } bool HappyEyeballsConnectionImpl::startSecureTransport() { + ASSERT(connect_finished_); return connection_->startSecureTransport(); } absl::optional HappyEyeballsConnectionImpl::lastRoundTripTime() const { + ASSERT(connect_finished_); return connection_->lastRoundTripTime(); } void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb) { - connection_->addConnectionCallbacks(cb); + if (connect_finished_) { + connection_->addConnectionCallbacks(cb); + return; + } + cbs_.push_back(&cb); } void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& cb) { - connection_->removeConnectionCallbacks(cb); + if (connect_finished_) { + connection_->removeConnectionCallbacks(cb); + return; + } + auto i = cbs_.begin(); + while (i != cbs_.end()) { + if (*i == &cb) { + cbs_.erase(i); + return; + } + } + ASSERT(false); } void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { + ASSERT(connect_finished_); connection_->close(type); } Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { + //ASSERT(connect_finished_); return connection_->dispatcher(); } uint64_t HappyEyeballsConnectionImpl::id() const { + ASSERT(connect_finished_); return connection_->id(); } void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { + ASSERT(connect_finished_); connection_->hashKey(hash); } void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stats) { + // ASSERT(connect_finished_); + // TODO more connection_->setConnectionStats(stats); } void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::milliseconds timeout) { + ASSERT(connect_finished_); connection_->setDelayedCloseTimeout(timeout); } @@ -146,11 +218,60 @@ void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) connection_->dumpState(os, indent_level); } + std::unique_ptr HappyEyeballsConnectionImpl::createConnection() { - return dispatcher_.createClientConnection( + auto connection = dispatcher_.createClientConnection( address_, source_address_, socket_factory_.createTransportSocket(transport_socket_options_), options_); + callbacks_wrapper_ = std::make_unique(*this, *connection); + connection->addConnectionCallbacks(*callbacks_wrapper_); + return connection; +} + +void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper) { + std::cerr << __FUNCTION__ << " " << static_cast(event) << " 1\n"; + ASSERT(wrapper == callbacks_wrapper_.get()); + connect_finished_ = true; + + connection_->removeConnectionCallbacks(*callbacks_wrapper_); + callbacks_wrapper_ = nullptr; + for (auto cb : cbs_) { + if (cb) { + connection_->addConnectionCallbacks(*cb); + } + } + if (event == ConnectionEvent::Connected) { + for (auto filter : read_filters_) { + connection_->addReadFilter(filter); + } + } + /* + if (state_.write_buffer_ || state_.end_stream_) { + connection_->write(*state_.write_buffer_, state_.end_stream_); + } + */ + + std::vector cbs; + cbs.swap(cbs_); + /* + for (auto cb : cbs) { + std::cerr << __FUNCTION__ << " calling cb->onEvent\n"; + cb->onEvent(event); + std::cerr << __FUNCTION__ << " done\n"; + } + */ + std::cerr << __FUNCTION__ << " finished\n"; +} + +void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* wrapper) { + ASSERT(connect_finished_); + ASSERT(wrapper == callbacks_wrapper_.get()); +} + +void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper) { + ASSERT(connect_finished_); + ASSERT(wrapper == callbacks_wrapper_.get()); } } // namespace Network diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index ad400f9c0966e..1eb464c86e3e3 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -81,10 +81,54 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void dumpState(std::ostream& os, int indent_level) const override; private: + class ConnectionCallbacksWrapper : public ConnectionCallbacks { + public: + ConnectionCallbacksWrapper(HappyEyeballsConnectionImpl& parent, + ClientConnection& connection) + : parent_(parent), connection_(connection) {} + + void onEvent(ConnectionEvent event) override { + parent_.onEvent(event, this); + } + + void onAboveWriteBufferHighWatermark() override { + parent_.onAboveWriteBufferHighWatermark(this); + } + + void onBelowWriteBufferLowWatermark() override { + parent_.onBelowWriteBufferLowWatermark(this); + } + + ClientConnection& connection() { return connection_; } + + private: + HappyEyeballsConnectionImpl& parent_; + ClientConnection& connection_; + }; + std::unique_ptr createConnection(); + void onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper); + + void onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* wrapper); + + void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); + + struct State { + bool detectEarlyCloseWhenReadDisabled_; + bool noDelay_; + Buffer::InstancePtr write_buffer_; + bool end_stream_; + }; + std::unique_ptr connection_; + std::vector cbs_; + std::vector read_filters_; + State state_; + bool connect_finished_ = false; + std::unique_ptr callbacks_wrapper_; std::unique_ptr pending_connection_; + Event::Dispatcher& dispatcher_; Network::Address::InstanceConstSharedPtr address_; Network::Address::InstanceConstSharedPtr source_address_; diff --git a/source/common/upstream/health_checker_impl.cc b/source/common/upstream/health_checker_impl.cc index 2859e58767584..983448dab2a62 100644 --- a/source/common/upstream/health_checker_impl.cc +++ b/source/common/upstream/health_checker_impl.cc @@ -546,17 +546,24 @@ void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onEvent(Network::Connect // TODO(lilika) : Support connection pooling void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onInterval() { if (!client_) { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_ = host_ ->createHealthCheckConnection(parent_.dispatcher_, parent_.transportSocketOptions(), parent_.transportSocketMatchMetadata().get()) .connection_; + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; session_callbacks_ = std::make_shared(*this); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->addConnectionCallbacks(*session_callbacks_); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->addReadFilter(session_callbacks_); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; expect_close_ = false; + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->connect(); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->noDelay(true); } @@ -566,8 +573,11 @@ void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onInterval() { data.add(segment.data(), segment.size()); } + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->write(data, false); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onTimeout() { From 6ee6483d06eea94735a9ae9eca250c6998e05460 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 29 Jun 2021 20:43:56 +0000 Subject: [PATCH 04/49] More WIP Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 35 ++++++++++++++----- .../network/happy_eyeballs_connection_impl.h | 9 +++-- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 6b92d2d65fb2a..0a3a6aae1a0aa 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -17,6 +17,10 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( transport_socket_options_(transport_socket_options), options_(options) { connection_ = createConnection(); + state_.write_buffer_ = dispatcher_.getWatermarkFactory().create( + [this]() -> void { this->onWriteBufferLowWatermark(); }, + [this]() -> void { this->onWriteBufferHighWatermark(); }, + []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); } HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; @@ -83,7 +87,9 @@ void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { connection_->detectEarlyCloseWhenReadDisabled(value); } bool HappyEyeballsConnectionImpl::readEnabled() const { - ASSERT(connect_finished_); + if (!connect_finished_) { + return true; + } return connection_->readEnabled(); } const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { @@ -103,7 +109,10 @@ Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { return connection_->ssl(); } Connection::State HappyEyeballsConnectionImpl::state() const { - ASSERT(connect_finished_); + //ASSERT( + if (!connect_finished_) { + ASSERT(connection_->state() == Connection::State::Open); + } return connection_->state(); } bool HappyEyeballsConnectionImpl::connecting() const { @@ -118,8 +127,8 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; return; } + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; - //state_.write_buffer_ = std::make_unique(); state_.write_buffer_->move(data); std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; state_.end_stream_ = end_stream; @@ -135,7 +144,10 @@ uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { return connection_->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { - ASSERT(connect_finished_); + if (!connect_finished_) { + return false; + } + return connection_->aboveHighWatermark(); } const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { @@ -188,7 +200,8 @@ void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& ASSERT(false); } void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { - ASSERT(connect_finished_); + // TODO(XXX) + //ASSERT(connect_finished_); connection_->close(type); } Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { @@ -246,11 +259,10 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb connection_->addReadFilter(filter); } } - /* - if (state_.write_buffer_ || state_.end_stream_) { + + if (state_.write_buffer_->length() > 0 || state_.end_stream_) { connection_->write(*state_.write_buffer_, state_.end_stream_); } - */ std::vector cbs; cbs.swap(cbs_); @@ -274,5 +286,12 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallb ASSERT(wrapper == callbacks_wrapper_.get()); } +void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { + ASSERT(false); +} +void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { + ASSERT(false); +} + } // namespace Network } // namespace Envoy diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 1eb464c86e3e3..5243797a244b1 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -114,11 +114,14 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); + void onWriteBufferHighWatermark(); + void onWriteBufferLowWatermark(); + struct State { - bool detectEarlyCloseWhenReadDisabled_; - bool noDelay_; + bool detectEarlyCloseWhenReadDisabled_ = false; + bool noDelay_ = false; Buffer::InstancePtr write_buffer_; - bool end_stream_; + bool end_stream_ = false; }; std::unique_ptr connection_; From 70d5ccea9b0a5fe97fe387b307541a0784d3e570 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 1 Jul 2021 17:13:50 +0000 Subject: [PATCH 05/49] more wip Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 0a3a6aae1a0aa..74176e2f0ed81 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -17,10 +17,6 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( transport_socket_options_(transport_socket_options), options_(options) { connection_ = createConnection(); - state_.write_buffer_ = dispatcher_.getWatermarkFactory().create( - [this]() -> void { this->onWriteBufferLowWatermark(); }, - [this]() -> void { this->onWriteBufferHighWatermark(); }, - []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); } HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; @@ -88,6 +84,7 @@ void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { } bool HappyEyeballsConnectionImpl::readEnabled() const { if (!connect_finished_) { + ASSERT_EQ(connection_->readEnabled(), true); return true; } return connection_->readEnabled(); @@ -129,6 +126,10 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) } std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + state_.write_buffer_ = dispatcher_.getWatermarkFactory().create( + [this]() -> void { this->onWriteBufferLowWatermark(); }, + [this]() -> void { this->onWriteBufferHighWatermark(); }, + []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); state_.write_buffer_->move(data); std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; state_.end_stream_ = end_stream; @@ -144,10 +145,11 @@ uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { return connection_->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { + /* if (!connect_finished_) { return false; } - + */ return connection_->aboveHighWatermark(); } const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { @@ -260,7 +262,8 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb } } - if (state_.write_buffer_->length() > 0 || state_.end_stream_) { + if ((state_.write_buffer_ && state_.write_buffer_->length() > 0) || state_.end_stream_) { + ASSERT(false); connection_->write(*state_.write_buffer_, state_.end_stream_); } From 608cde5622d637de7d4d3a8dfacb120b1a1e8a92 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 9 Jul 2021 17:32:48 +0000 Subject: [PATCH 06/49] WIP Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 287 ++++++++++++------ .../network/happy_eyeballs_connection_impl.h | 65 ++-- source/common/upstream/upstream_impl.cc | 5 +- .../happy_eyeballs_connection_impl_test.cc | 245 ++++++++++++++- 4 files changed, 483 insertions(+), 119 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 74176e2f0ed81..64ad10a0003bb 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -1,201 +1,208 @@ #include "source/common/network/happy_eyeballs_connection_impl.h" +#include + namespace Envoy { namespace Network { HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( Event::Dispatcher& dispatcher, - Network::Address::InstanceConstSharedPtr address, + const std::vector& address_list, Network::Address::InstanceConstSharedPtr source_address, Network::TransportSocketFactory& socket_factory, Network::TransportSocketOptionsSharedPtr transport_socket_options, const Network::ConnectionSocket::OptionsSharedPtr options) : dispatcher_(dispatcher), - address_(address), + address_list_(address_list), source_address_(source_address), socket_factory_(socket_factory), transport_socket_options_(transport_socket_options), - options_(options) { - connection_ = createConnection(); + options_(options), + next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { + connections_.push_back(createNextConnection()); } HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; void HappyEyeballsConnectionImpl::connect() { ASSERT(!connect_finished_); - connection_->connect(); + connections_[0]->connect(); + maybeScheduleNextAttempt(); } void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { ASSERT(connect_finished_); - connection_->addWriteFilter(filter); + connections_[0]->addWriteFilter(filter); } void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { ASSERT(connect_finished_); - connection_->addFilter(filter); + connections_[0]->addFilter(filter); } void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { if (connect_finished_) { - connection_->addReadFilter(filter); + connections_[0]->addReadFilter(filter); return; } - read_filters_.push_back(filter); + post_connect_state_.read_filters_.push_back(filter); } void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { ASSERT(connect_finished_); - connection_->removeReadFilter(filter); + connections_[0]->removeReadFilter(filter); } bool HappyEyeballsConnectionImpl::initializeReadFilters() { ASSERT(connect_finished_); - return connection_->initializeReadFilters(); + return connections_[0]->initializeReadFilters(); } void HappyEyeballsConnectionImpl::addBytesSentCallback(ClientConnection::BytesSentCb cb) { ASSERT(connect_finished_); - connection_->addBytesSentCallback(cb); + connections_[0]->addBytesSentCallback(cb); } void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { ASSERT(connect_finished_); - connection_->enableHalfClose(enabled); + connections_[0]->enableHalfClose(enabled); } bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { ASSERT(connect_finished_); - return connection_->isHalfCloseEnabled(); + return connections_[0]->isHalfCloseEnabled(); } std::string HappyEyeballsConnectionImpl::nextProtocol() const { ASSERT(connect_finished_); - return connection_->nextProtocol(); + return connections_[0]->nextProtocol(); } void HappyEyeballsConnectionImpl::noDelay(bool enable) { if (!connect_finished_) { - state_.noDelay_ = enable; + per_connection_state_.no_delay_ = enable; + } + for (auto& connection : connections_) { + connection->noDelay(enable); } - connection_->noDelay(enable); } void HappyEyeballsConnectionImpl::readDisable(bool disable) { ASSERT(connect_finished_); - connection_->readDisable(disable); + connections_[0]->readDisable(disable); } void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { if (!connect_finished_) { - state_.detectEarlyCloseWhenReadDisabled_ = value; + per_connection_state_.detect_early_close_when_read_disabled_ = value; + } + for (auto& connection : connections_) { + connection->detectEarlyCloseWhenReadDisabled(value); } - connection_->detectEarlyCloseWhenReadDisabled(value); } bool HappyEyeballsConnectionImpl::readEnabled() const { if (!connect_finished_) { - ASSERT_EQ(connection_->readEnabled(), true); + ASSERT(connections_[0]->readEnabled()); return true; } - return connection_->readEnabled(); + return connections_[0]->readEnabled(); } const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { ASSERT(connect_finished_); - return connection_->addressProvider(); + return connections_[0]->addressProvider(); } SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { ASSERT(connect_finished_); - return connection_->addressProviderSharedPtr(); + return connections_[0]->addressProviderSharedPtr(); } absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { ASSERT(connect_finished_); - return connection_->unixSocketPeerCredentials(); + return connections_[0]->unixSocketPeerCredentials(); } Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { ASSERT(connect_finished_); - return connection_->ssl(); + return connections_[0]->ssl(); } Connection::State HappyEyeballsConnectionImpl::state() const { - //ASSERT( if (!connect_finished_) { - ASSERT(connection_->state() == Connection::State::Open); + ASSERT(connections_[0]->state() == Connection::State::Open); } - return connection_->state(); + return connections_[0]->state(); } bool HappyEyeballsConnectionImpl::connecting() const { - ASSERT(connect_finished_ || connection_->connecting()); - return connection_->connecting(); + ASSERT(connect_finished_ || connections_[0]->connecting()); + return connections_[0]->connecting(); } void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; if (connect_finished_) { std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; - connection_->write(data, end_stream); + connections_[0]->write(data, end_stream); std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; return; } std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; - state_.write_buffer_ = dispatcher_.getWatermarkFactory().create( - [this]() -> void { this->onWriteBufferLowWatermark(); }, - [this]() -> void { this->onWriteBufferHighWatermark(); }, - []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); - state_.write_buffer_->move(data); + post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().create( + [this]() -> void { this->onWriteBufferLowWatermark(); }, + [this]() -> void { this->onWriteBufferHighWatermark(); }, + []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); + post_connect_state_.write_buffer_.value()->move(data); std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; - state_.end_stream_ = end_stream; + post_connect_state_.end_stream_ = end_stream; std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { ASSERT(connect_finished_); - connection_->setBufferLimits(limit); + connections_[0]->setBufferLimits(limit); } uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { // TODO - huh? //ASSERT(connect_finished_); - return connection_->bufferLimit(); + return connections_[0]->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { /* - if (!connect_finished_) { + if (!connect_finished_) { return false; - } + } */ - return connection_->aboveHighWatermark(); + return connections_[0]->aboveHighWatermark(); } const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { ASSERT(connect_finished_); - return connection_->socketOptions(); + return connections_[0]->socketOptions(); } absl::string_view HappyEyeballsConnectionImpl::requestedServerName() const { ASSERT(connect_finished_); - return connection_->requestedServerName(); + return connections_[0]->requestedServerName(); } StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() { ASSERT(connect_finished_); - return connection_->streamInfo(); + return connections_[0]->streamInfo(); } const StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() const { ASSERT(connect_finished_); - return connection_->streamInfo(); + return connections_[0]->streamInfo(); } absl::string_view HappyEyeballsConnectionImpl::transportFailureReason() const { ASSERT(connect_finished_); - return connection_->transportFailureReason(); + return connections_[0]->transportFailureReason(); } bool HappyEyeballsConnectionImpl::startSecureTransport() { ASSERT(connect_finished_); - return connection_->startSecureTransport(); + return connections_[0]->startSecureTransport(); } absl::optional HappyEyeballsConnectionImpl::lastRoundTripTime() const { ASSERT(connect_finished_); - return connection_->lastRoundTripTime(); + return connections_[0]->lastRoundTripTime(); } void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb) { if (connect_finished_) { - connection_->addConnectionCallbacks(cb); + connections_[0]->addConnectionCallbacks(cb); return; } - cbs_.push_back(&cb); + post_connect_state_.connection_callbacks_.push_back(&cb); } void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& cb) { if (connect_finished_) { - connection_->removeConnectionCallbacks(cb); + connections_[0]->removeConnectionCallbacks(cb); return; } - auto i = cbs_.begin(); - while (i != cbs_.end()) { + auto i = post_connect_state_.connection_callbacks_.begin(); + while (i != post_connect_state_.connection_callbacks_.end()) { if (*i == &cb) { - cbs_.erase(i); + post_connect_state_.connection_callbacks_.erase(i); return; } } @@ -204,89 +211,199 @@ void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { // TODO(XXX) //ASSERT(connect_finished_); - connection_->close(type); + connections_[0]->close(type); } Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { //ASSERT(connect_finished_); - return connection_->dispatcher(); + return connections_[0]->dispatcher(); } uint64_t HappyEyeballsConnectionImpl::id() const { ASSERT(connect_finished_); - return connection_->id(); + return connections_[0]->id(); } void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { ASSERT(connect_finished_); - connection_->hashKey(hash); + connections_[0]->hashKey(hash); } void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stats) { // ASSERT(connect_finished_); // TODO more - connection_->setConnectionStats(stats); + connections_[0]->setConnectionStats(stats); } void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::milliseconds timeout) { ASSERT(connect_finished_); - connection_->setDelayedCloseTimeout(timeout); + connections_[0]->setDelayedCloseTimeout(timeout); } // ScopeTrackedObject void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) const { - connection_->dumpState(os, indent_level); + ASSERT(connect_finished_); + connections_[0]->dumpState(os, indent_level); } -std::unique_ptr HappyEyeballsConnectionImpl::createConnection() { +std::unique_ptr HappyEyeballsConnectionImpl::createNextConnection() { + ASSERT(next_address_ < address_list_.size()); + std::cerr << __FUNCTION__ << "\n"; auto connection = dispatcher_.createClientConnection( - address_, source_address_, + address_list_[next_address_++], source_address_, socket_factory_.createTransportSocket(transport_socket_options_), options_); - callbacks_wrapper_ = std::make_unique(*this, *connection); - connection->addConnectionCallbacks(*callbacks_wrapper_); + std::cerr << "connection: " << connection.get() << std::endl; + callbacks_wrappers_.push_back(std::make_unique(*this, *connection)); + std::cerr << "connection: " << connection.get() << std::endl; + connection->addConnectionCallbacks(*callbacks_wrappers_.back()); + std::cerr << "connection: " << connection.get() << std::endl; + + if (per_connection_state_.detect_early_close_when_read_disabled_.has_value()) { + connection->detectEarlyCloseWhenReadDisabled(per_connection_state_.detect_early_close_when_read_disabled_.value()); + } + if (per_connection_state_.no_delay_.has_value()) { + connection->noDelay(per_connection_state_.no_delay_.value()); + } return connection; } +void HappyEyeballsConnectionImpl::tryAnotherConnection() { + std::cerr << __FUNCTION__ << "\n"; + connections_.push_back(createNextConnection()); + connections_.back()->connect(); + maybeScheduleNextAttempt(); +} + +void HappyEyeballsConnectionImpl::maybeScheduleNextAttempt() { + std::cerr << __FUNCTION__ << "\n"; + if (next_address_ >= address_list_.size()) { + return; + } + std::cerr << __FUNCTION__ << "\n"; + next_attempt_timer_->enableTimer(std::chrono::milliseconds(300)); +} + void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper) { std::cerr << __FUNCTION__ << " " << static_cast(event) << " 1\n"; - ASSERT(wrapper == callbacks_wrapper_.get()); + //ASSERT(wrapper == callbacks_wrapper_.get()); + + wrapper->connection().removeConnectionCallbacks(*wrapper); + if (event != ConnectionEvent::Connected) { + if (next_attempt_timer_->enabled()) { + next_attempt_timer_->disableTimer(); + tryAnotherConnection(); + } + if (connections_.size() > 1) { + // Nuke this connection and associated callbacks and let a subsequent attempt proceed. + cleanupWrapperAndConnection(wrapper); + return; + } + //ASSERT(false); /// XXX have coverage for this case. + } + connect_finished_ = true; - connection_->removeConnectionCallbacks(*callbacks_wrapper_); - callbacks_wrapper_ = nullptr; - for (auto cb : cbs_) { + // Clean up other connections. + std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; + std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; + + { + auto it = connections_.begin(); + while (it != connections_.end()) { + if (it->get() != &(wrapper->connection())) { + (*it)->close(ConnectionCloseType::NoFlush); + it = connections_.erase(it); + } else { + ++it; + } + } + } + { + auto it = callbacks_wrappers_.begin(); + while (it != callbacks_wrappers_.end()) { + if (it->get() != wrapper) { + it = callbacks_wrappers_.erase(it); + } else { + ++it; + } + } + } + /* + std::remove_if(connections_.begin(), connections_.end(), [wrapper](std::unique_ptr& connection) { return connection.get() != &(wrapper->connection()); }); + std::remove_if(callbacks_wrappers_.begin(), callbacks_wrappers_.end(), [wrapper](std::unique_ptr& callbacks_wrapper) { return callbacks_wrapper.get() != wrapper; }); + */ + std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; + std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; + ASSERT(connections_.size() == 1); + ASSERT(callbacks_wrappers_.size() == 1); + + callbacks_wrappers_.clear(); + // Apply post-connect state to the final socket. + for (auto cb : post_connect_state_.connection_callbacks_) { if (cb) { - connection_->addConnectionCallbacks(*cb); + connections_[0]->addConnectionCallbacks(*cb); } } if (event == ConnectionEvent::Connected) { - for (auto filter : read_filters_) { - connection_->addReadFilter(filter); + for (auto filter : post_connect_state_.read_filters_) { + connections_[0]->addReadFilter(filter); } } - if ((state_.write_buffer_ && state_.write_buffer_->length() > 0) || state_.end_stream_) { - ASSERT(false); - connection_->write(*state_.write_buffer_, state_.end_stream_); + if (post_connect_state_.write_buffer_.has_value()) { + //ASSERT(false); + // write_buffer_ and end_stream_ are both set together in write(). + ASSERT(post_connect_state_.end_stream_.has_value()); + connections_[0]->write(*post_connect_state_.write_buffer_.value(), post_connect_state_.end_stream_.value()); } std::vector cbs; - cbs.swap(cbs_); + cbs.swap(post_connect_state_.connection_callbacks_); /* - for (auto cb : cbs) { + for (auto cb : cbs) { std::cerr << __FUNCTION__ << " calling cb->onEvent\n"; cb->onEvent(event); std::cerr << __FUNCTION__ << " done\n"; - } + } */ std::cerr << __FUNCTION__ << " finished\n"; } -void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* wrapper) { +void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper) { + std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; + std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; + + { + auto it = connections_.begin(); + while (it != connections_.end()) { + if (it->get() == &(wrapper->connection())) { + (*it)->close(ConnectionCloseType::NoFlush); + it = connections_.erase(it); + } else { + ++it; + } + } + } + { + auto it = callbacks_wrappers_.begin(); + while (it != callbacks_wrappers_.end()) { + if (it->get() == wrapper) { + it = callbacks_wrappers_.erase(it); + } else { + ++it; + } + } + } + + std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; + std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; +} + +void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { ASSERT(connect_finished_); - ASSERT(wrapper == callbacks_wrapper_.get()); + //ASSERT(wrapper == callbacks_wrapper_.get()); } -void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper) { +void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { ASSERT(connect_finished_); - ASSERT(wrapper == callbacks_wrapper_.get()); + //ASSERT(wrapper == callbacks_wrapper_.get()); } void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 5243797a244b1..2ad6f467bc2a5 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -24,11 +24,11 @@ namespace Network { class HappyEyeballsConnectionImpl : public ClientConnection { public: HappyEyeballsConnectionImpl(Event::Dispatcher& dispatcher, - Network::Address::InstanceConstSharedPtr address, - Network::Address::InstanceConstSharedPtr source_address, - Network::TransportSocketFactory& socket_factory, - Network::TransportSocketOptionsSharedPtr transport_socket_options, - const Network::ConnectionSocket::OptionsSharedPtr options); + const std::vector& address_list, + Address::InstanceConstSharedPtr source_address, + TransportSocketFactory& socket_factory, + TransportSocketOptionsSharedPtr transport_socket_options, + const ConnectionSocket::OptionsSharedPtr options); ~HappyEyeballsConnectionImpl() override; @@ -81,6 +81,8 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void dumpState(std::ostream& os, int indent_level) const override; private: + // ConnectionCallbacks which will be set on an ClientConnection which + // sends connection events back to the HappyEyeballsConnectionImpl. class ConnectionCallbacksWrapper : public ConnectionCallbacks { public: ConnectionCallbacksWrapper(HappyEyeballsConnectionImpl& parent, @@ -99,6 +101,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { parent_.onBelowWriteBufferLowWatermark(this); } + // Not needed? interesting. ClientConnection& connection() { return connection_; } private: @@ -106,7 +109,9 @@ class HappyEyeballsConnectionImpl : public ClientConnection { ClientConnection& connection_; }; - std::unique_ptr createConnection(); + std::unique_ptr createNextConnection(); + void tryAnotherConnection(); + void maybeScheduleNextAttempt(); void onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper); @@ -117,27 +122,41 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void onWriteBufferHighWatermark(); void onWriteBufferLowWatermark(); - struct State { - bool detectEarlyCloseWhenReadDisabled_ = false; - bool noDelay_ = false; - Buffer::InstancePtr write_buffer_; - bool end_stream_ = false; + void cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper); + + // State which needs to be applie to every connection attempt. + struct PerConnectionState { + absl::optional detect_early_close_when_read_disabled_; + absl::optional no_delay_; }; - std::unique_ptr connection_; - std::vector cbs_; - std::vector read_filters_; - State state_; - bool connect_finished_ = false; - std::unique_ptr callbacks_wrapper_; - std::unique_ptr pending_connection_; + // State which needs to be saved and applied only to the final connection + // attempt. + struct PostConnectState { + std::vector connection_callbacks_; + std::vector read_filters_; + absl::optional write_buffer_; + absl::optional end_stream_; + }; Event::Dispatcher& dispatcher_; - Network::Address::InstanceConstSharedPtr address_; - Network::Address::InstanceConstSharedPtr source_address_; - Network::TransportSocketFactory& socket_factory_; - Network::TransportSocketOptionsSharedPtr transport_socket_options_; - const Network::ConnectionSocket::OptionsSharedPtr options_; + const std::vector& address_list_; + size_t next_address_ = 0; + Address::InstanceConstSharedPtr source_address_; + TransportSocketFactory& socket_factory_; + TransportSocketOptionsSharedPtr transport_socket_options_; + const ConnectionSocket::OptionsSharedPtr options_; + + // Set of active connections. + std::vector> connections_; + std::vector> callbacks_wrappers_; + + // True when connect() has finished, either success or failure. + bool connect_finished_ = false; + Event::TimerPtr next_attempt_timer_; + + PerConnectionState per_connection_state_; + PostConnectState post_connect_state_; }; } // namespace Network diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 67801aed2bc36..d247705bd8854 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -339,10 +339,11 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu } ASSERT(!address->envoyInternalAddress()); Network::ClientConnectionPtr connection = - + /* true ? - std::make_unique(dispatcher, address, cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options) + std::make_unique(dispatcher, addressList(), cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options) : + */ dispatcher.createClientConnection( address, cluster.sourceAddress(), socket_factory.createTransportSocket(std::move(transport_socket_options)), diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index ea7504ee1b222..c8d27d31aaedc 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -1,5 +1,6 @@ #include "source/common/network/happy_eyeballs_connection_impl.h" +#include "source/common/network/address_impl.h" #include "source/common/network/transport_socket_options_impl.h" #include "test/mocks/event/mocks.h" @@ -7,6 +8,7 @@ #include "test/mocks/network/connection.h" using testing::Return; +using testing::StrictMock; namespace Envoy { namespace Network { @@ -14,34 +16,259 @@ namespace Network { class HappyEyeballsConnectionImplTest : public testing::Test { public: HappyEyeballsConnectionImplTest() - : transport_socket_options_(std::make_shared()), - options_(std::make_shared()) { + : failover_timer_(new testing::StrictMock(&dispatcher_)), + transport_socket_options_(std::make_shared()), + options_(std::make_shared()), + address_list_({ + std::make_shared("127.0.0.1"), + std::make_shared("127.0.0.2"), + std::make_shared("127.0.0.3") }) { EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - connection_ = new NiceMock(); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce(Return(connection_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[0], _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + + // This timer will be returned and armed as the happy eyeballs connection creates the next connection timer. + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(true)); + next_connections_.push_back(std::make_unique>()); + std::cerr << __LINE__ << std::endl; impl_ = std::make_unique(dispatcher_, - Network::Address::InstanceConstSharedPtr(), - Network::Address::InstanceConstSharedPtr(), + address_list_, + Address::InstanceConstSharedPtr(), transport_socket_factory_, transport_socket_options_, options_); + std::cerr << __LINE__ << std::endl; + } + MockClientConnection* createNextConnection() { + std::cerr << __LINE__ << " size: " << next_connections_.size() << std::endl; + created_connections_.push_back(next_connections_.front().release()); + next_connections_.pop_front(); + EXPECT_CALL(*created_connections_.back(), addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& cb) -> void { connection_callbacks_.push_back(&cb);})); + return created_connections_.back(); } protected: Event::MockDispatcher dispatcher_; + testing::StrictMock* failover_timer_; MockTransportSocketFactory transport_socket_factory_; TransportSocketOptionsSharedPtr transport_socket_options_; const ConnectionSocket::OptionsSharedPtr options_; - Network::MockClientConnection* connection_; + const std::vector address_list_; + std::vector*> created_connections_; + std::vector connection_callbacks_; + std::deque>> next_connections_; std::unique_ptr impl_; }; TEST_F(HappyEyeballsConnectionImplTest, Connect) { - EXPECT_CALL(*connection_, connect()); + EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); -}; +} + +TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + // Let the second attempt timeout to start the third and final attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + // Since there are no more address to connect to, the fallback timer will not + // be rescheduled. + failover_timer_->invokeCallback(); +} + +TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); +} + + +TEST_F(HappyEyeballsConnectionImplTest, ConnectFirstSuccess) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); +} + +TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + // Verify that calls are delegated to the right connection. + EXPECT_CALL(*created_connections_[0], connecting()).WillOnce(Return(false)); + EXPECT_FALSE(impl_->connecting()); +} + +TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that calls are delegated to the right connection. + EXPECT_CALL(*created_connections_[1], connecting()).WillOnce(Return(false)); + EXPECT_FALSE(impl_->connecting()); +} + +TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSucceeds) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + // When the second attempt fails, the third and final attempt will be started. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(false)); + // Since there are no more address to connect to, the fallback timer will not + // be rescheduled. + ASSERT_EQ(2, created_connections_.size()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); + std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; + connection_callbacks_[1]->onEvent(ConnectionEvent::RemoteClose); + std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; + + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; + connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); + std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; +} + +TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { + EXPECT_CALL(*created_connections_[0], noDelay(true)); + impl_->noDelay(true); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + // noDelay() should be applied to the newly created connection. + EXPECT_CALL(*next_connections_.back(), noDelay(true)); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that noDelay calls are delegated to the remaingin connection. + EXPECT_CALL(*created_connections_[1], noDelay(false)); + impl_->noDelay(false); +} + +TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ + EXPECT_CALL(*created_connections_[0], detectEarlyCloseWhenReadDisabled(true)); + impl_->detectEarlyCloseWhenReadDisabled(true); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + // detectEarlyCloseWhenReadDisabled() should be applied to the newly created connection. + EXPECT_CALL(*next_connections_.back(), detectEarlyCloseWhenReadDisabled(true)); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that detectEarlyCloseWhenReadDisabled calls are delegated to the remaingin connection. + EXPECT_CALL(*created_connections_[1], detectEarlyCloseWhenReadDisabled(false)); + impl_->detectEarlyCloseWhenReadDisabled(false); +} + +TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { + Buffer::OwnedImpl data("hello world"); + bool end_stream = false; + + impl_->write(data, end_stream); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*created_connections_[0], write(_, _)).WillOnce( + Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_EQ("hello world", data.toString()); + EXPECT_FALSE(end_stream); + ;}) + ); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); +} } // namespace Network } // namespace Envoy From 3650ba15e8830b94a57622562c53df164a5b9480 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Sun, 11 Jul 2021 23:30:07 +0000 Subject: [PATCH 07/49] unit tests pass Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 2 +- test/common/network/happy_eyeballs_connection_impl_test.cc | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 64ad10a0003bb..85267dcf670bc 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -286,7 +286,7 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb wrapper->connection().removeConnectionCallbacks(*wrapper); if (event != ConnectionEvent::Connected) { - if (next_attempt_timer_->enabled()) { + if (next_address_ < address_list_.size()) { next_attempt_timer_->disableTimer(); tryAnotherConnection(); } diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index c8d27d31aaedc..a127af75301c2 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -87,7 +87,6 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)).WillOnce( testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); // Since there are no more address to connect to, the fallback timer will not // be rescheduled. failover_timer_->invokeCallback(); @@ -103,6 +102,9 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); } @@ -179,7 +181,6 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(false)); // Since there are no more address to connect to, the fallback timer will not // be rescheduled. ASSERT_EQ(2, created_connections_.size()); From d0e5230a13a1a9e5c8605ef4f2ed8d57cd6fab0c Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 13 Jul 2021 15:44:20 +0000 Subject: [PATCH 08/49] More tests Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 66 ++- .../network/happy_eyeballs_connection_impl.h | 1 + .../happy_eyeballs_connection_impl_test.cc | 390 ++++++++++++++++++ 3 files changed, 444 insertions(+), 13 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 85267dcf670bc..cc453ece4e44a 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -34,10 +34,12 @@ void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { ASSERT(connect_finished_); connections_[0]->addWriteFilter(filter); } + void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { ASSERT(connect_finished_); connections_[0]->addFilter(filter); } + void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { if (connect_finished_) { connections_[0]->addReadFilter(filter); @@ -45,31 +47,37 @@ void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { } post_connect_state_.read_filters_.push_back(filter); } + void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { ASSERT(connect_finished_); connections_[0]->removeReadFilter(filter); } + bool HappyEyeballsConnectionImpl::initializeReadFilters() { ASSERT(connect_finished_); return connections_[0]->initializeReadFilters(); } -void HappyEyeballsConnectionImpl::addBytesSentCallback(ClientConnection::BytesSentCb cb) { +void HappyEyeballsConnectionImpl::addBytesSentCallback(Connection::BytesSentCb cb) { ASSERT(connect_finished_); connections_[0]->addBytesSentCallback(cb); } + void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { ASSERT(connect_finished_); connections_[0]->enableHalfClose(enabled); } + bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { ASSERT(connect_finished_); return connections_[0]->isHalfCloseEnabled(); } + std::string HappyEyeballsConnectionImpl::nextProtocol() const { ASSERT(connect_finished_); return connections_[0]->nextProtocol(); } + void HappyEyeballsConnectionImpl::noDelay(bool enable) { if (!connect_finished_) { per_connection_state_.no_delay_ = enable; @@ -78,10 +86,12 @@ void HappyEyeballsConnectionImpl::noDelay(bool enable) { connection->noDelay(enable); } } + void HappyEyeballsConnectionImpl::readDisable(bool disable) { ASSERT(connect_finished_); connections_[0]->readDisable(disable); } + void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { if (!connect_finished_) { per_connection_state_.detect_early_close_when_read_disabled_ = value; @@ -90,39 +100,46 @@ void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { connection->detectEarlyCloseWhenReadDisabled(value); } } + bool HappyEyeballsConnectionImpl::readEnabled() const { if (!connect_finished_) { ASSERT(connections_[0]->readEnabled()); - return true; } return connections_[0]->readEnabled(); } + const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { ASSERT(connect_finished_); return connections_[0]->addressProvider(); } + SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { ASSERT(connect_finished_); return connections_[0]->addressProviderSharedPtr(); } -absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { + +absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { ASSERT(connect_finished_); return connections_[0]->unixSocketPeerCredentials(); } + Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { ASSERT(connect_finished_); return connections_[0]->ssl(); } + Connection::State HappyEyeballsConnectionImpl::state() const { if (!connect_finished_) { ASSERT(connections_[0]->state() == Connection::State::Open); } return connections_[0]->state(); } + bool HappyEyeballsConnectionImpl::connecting() const { ASSERT(connect_finished_ || connections_[0]->connecting()); return connections_[0]->connecting(); } + void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) { std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; if (connect_finished_) { @@ -142,16 +159,20 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) post_connect_state_.end_stream_ = end_stream; std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } + void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { ASSERT(connect_finished_); connections_[0]->setBufferLimits(limit); } + uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { + // XXX // TODO - huh? //ASSERT(connect_finished_); return connections_[0]->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { + // XXX /* if (!connect_finished_) { return false; @@ -159,34 +180,42 @@ bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { */ return connections_[0]->aboveHighWatermark(); } + const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { ASSERT(connect_finished_); return connections_[0]->socketOptions(); } + absl::string_view HappyEyeballsConnectionImpl::requestedServerName() const { ASSERT(connect_finished_); return connections_[0]->requestedServerName(); } + StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() { ASSERT(connect_finished_); return connections_[0]->streamInfo(); } + const StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() const { ASSERT(connect_finished_); return connections_[0]->streamInfo(); } + absl::string_view HappyEyeballsConnectionImpl::transportFailureReason() const { ASSERT(connect_finished_); return connections_[0]->transportFailureReason(); } + bool HappyEyeballsConnectionImpl::startSecureTransport() { ASSERT(connect_finished_); return connections_[0]->startSecureTransport(); } + absl::optional HappyEyeballsConnectionImpl::lastRoundTripTime() const { ASSERT(connect_finished_); return connections_[0]->lastRoundTripTime(); } + void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb) { if (connect_finished_) { connections_[0]->addConnectionCallbacks(cb); @@ -194,11 +223,14 @@ void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb } post_connect_state_.connection_callbacks_.push_back(&cb); } + void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& cb) { if (connect_finished_) { + std::cerr << __FUNCTION__ << " removing\n"; connections_[0]->removeConnectionCallbacks(cb); return; } + std::cerr << __FUNCTION__ << " removing from queue\n"; auto i = post_connect_state_.connection_callbacks_.begin(); while (i != post_connect_state_.connection_callbacks_.end()) { if (*i == &cb) { @@ -213,35 +245,41 @@ void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { //ASSERT(connect_finished_); connections_[0]->close(type); } + Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { - //ASSERT(connect_finished_); + ASSERT(&dispatcher_ == &connections_[0]->dispatcher()); return connections_[0]->dispatcher(); } + uint64_t HappyEyeballsConnectionImpl::id() const { ASSERT(connect_finished_); return connections_[0]->id(); } + void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { ASSERT(connect_finished_); connections_[0]->hashKey(hash); } + void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stats) { - // ASSERT(connect_finished_); - // TODO more - connections_[0]->setConnectionStats(stats); + if (!connect_finished_) { + per_connection_state_.connection_stats_ = &stats; + } + for (auto& connection : connections_) { + connection->setConnectionStats(stats); + } } + void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::milliseconds timeout) { ASSERT(connect_finished_); connections_[0]->setDelayedCloseTimeout(timeout); } -// ScopeTrackedObject void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) const { ASSERT(connect_finished_); connections_[0]->dumpState(os, indent_level); } - std::unique_ptr HappyEyeballsConnectionImpl::createNextConnection() { ASSERT(next_address_ < address_list_.size()); std::cerr << __FUNCTION__ << "\n"; @@ -261,6 +299,9 @@ std::unique_ptr HappyEyeballsConnectionImpl::createNextConnect if (per_connection_state_.no_delay_.has_value()) { connection->noDelay(per_connection_state_.no_delay_.value()); } + if (per_connection_state_.connection_stats_.has_value()) { + connection->setConnectionStats(*per_connection_state_.connection_stats_.value()); + } return connection; } @@ -299,6 +340,7 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb } connect_finished_ = true; + next_attempt_timer_->disableTimer(); // Clean up other connections. std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; @@ -397,13 +439,11 @@ void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallback } void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { - ASSERT(connect_finished_); - //ASSERT(wrapper == callbacks_wrapper_.get()); + ASSERT(false); } void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { - ASSERT(connect_finished_); - //ASSERT(wrapper == callbacks_wrapper_.get()); + ASSERT(false); } void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 2ad6f467bc2a5..bc042c04e0113 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -128,6 +128,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { struct PerConnectionState { absl::optional detect_early_close_when_read_disabled_; absl::optional no_delay_; + absl::optional connection_stats_; }; // State which needs to be saved and applied only to the final connection diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index a127af75301c2..2b6038cbf16dd 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -6,8 +6,10 @@ #include "test/mocks/event/mocks.h" #include "test/mocks/network/transport_socket.h" #include "test/mocks/network/connection.h" +#include "test/mocks/network/mocks.h" using testing::Return; +using testing::ReturnRef; using testing::StrictMock; namespace Envoy { @@ -96,6 +98,8 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); + // When the first connection attempt fails, the next attempt will be immediately + // started and the timer will be armed for the third attempe. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( @@ -113,6 +117,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFirstSuccess) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } @@ -130,6 +135,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); failover_timer_->invokeCallback(); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); @@ -152,6 +158,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); failover_timer_->invokeCallback(); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -215,6 +222,7 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); failover_timer_->invokeCallback(); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -242,6 +250,7 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); failover_timer_->invokeCallback(); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -251,6 +260,101 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ impl_->detectEarlyCloseWhenReadDisabled(false); } +TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ + MockReadFilterCallbacks callbacks; + ReadFilterSharedPtr filter = std::make_shared(); + filter->initializeReadFilterCallbacks(callbacks); + // The filter will be captured by the impl and not passed to the connection until it completes. + impl_->addReadFilter(filter); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + // addReadFilter() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], addReadFilter(filter)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + ReadFilterSharedPtr filter2 = std::make_shared(); + filter2->initializeReadFilterCallbacks(callbacks); + // Verify that detectEarlyCloseWhenReadDisabled calls are delegated to the remaingin connection. + EXPECT_CALL(*created_connections_[1], addReadFilter(filter2)); + impl_->addReadFilter(filter2); +} + +TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks){ + MockConnectionCallbacks callbacks; + // The filter will be captured by the impl and not passed to the connection until it completes. + impl_->addConnectionCallbacks(callbacks); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + // addConnectionCallbacks() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { + EXPECT_EQ(&c, &callbacks); + })); + //EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(callbacks)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + MockConnectionCallbacks callbacks2; + // Verify that detectEarlyCloseWhenReadDisabled calls are delegated to the remaingin connection. + EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { + EXPECT_EQ(&c, &callbacks2); + })); + impl_->addConnectionCallbacks(callbacks2); +} + +TEST_F(HappyEyeballsConnectionImplTest, RemoveConnectionCallbacks){ + MockConnectionCallbacks callbacks; + MockConnectionCallbacks callbacks2; + // The callbacks will be captured by the impl and not passed to the connection until it completes. + impl_->addConnectionCallbacks(callbacks); + impl_->addConnectionCallbacks(callbacks2); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + impl_->removeConnectionCallbacks(callbacks); + + // addConnectionCallbacks() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { + EXPECT_EQ(&c, &callbacks2); + })); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + // Verify that removeConnectionCallbacks calls are delegated to the remaingin connection. + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { + EXPECT_EQ(&c, &callbacks2); + })); + impl_->removeConnectionCallbacks(callbacks2); +} + TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { Buffer::OwnedImpl data("hello world"); bool end_stream = false; @@ -260,6 +364,7 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); // The call to write() will be replayed on the underlying connection. EXPECT_CALL(*created_connections_[0], write(_, _)).WillOnce( @@ -271,5 +376,290 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } + +struct MockConnectionStats { + Connection::ConnectionStats toBufferStats() { + return {rx_total_, rx_current_, tx_total_, + tx_current_, &bind_errors_, &delayed_close_timeouts_}; + } + + StrictMock rx_total_; + StrictMock rx_current_; + StrictMock tx_total_; + StrictMock tx_current_; + StrictMock bind_errors_; + StrictMock delayed_close_timeouts_; +}; + +TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ + MockConnectionStats stats; + Connection::ConnectionStats cs = { stats.rx_total_, stats.rx_current_, stats.tx_total_, stats.tx_current_, &stats.bind_errors_, &stats.delayed_close_timeouts_}; + EXPECT_CALL(*created_connections_[0], setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { + EXPECT_EQ(&s, &cs); + })); + impl_->setConnectionStats(cs); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + // setConnectionStats() should be applied to the newly created connection. + EXPECT_CALL(*next_connections_.back(), setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { + EXPECT_EQ(&s, &cs); + })); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + /* + // Verify that setConnectionStats calls are delegated to the remaingin connection. + MockConnectionStats stats2; + EXPECT_CALL(*created_connections_[1], setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { + EXPECT_EQ(&s, &stats2); + })); + impl_->setConnectionStats(stats2); + */ +} + +TEST_F(HappyEyeballsConnectionImplTest, state) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*created_connections_[0], state()).WillRepeatedly(Return(Connection::State::Open)); + EXPECT_EQ(Connection::State::Open, impl_->state()); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], state()).WillOnce(Return(Connection::State::Closing)); + EXPECT_EQ(Connection::State::Closing, impl_->state()); +} + +TEST_F(HappyEyeballsConnectionImplTest, connecting) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*created_connections_[0], connecting()).WillRepeatedly(Return(true)); + EXPECT_TRUE(impl_->connecting()); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], connecting()).WillRepeatedly(Return(false)); + EXPECT_FALSE(impl_->connecting()); +} + +// Tests for HappyEyeballsConnectionImpl methods which must only be called after connect() +// has finised. + +TEST_F(HappyEyeballsConnectionImplTest, addWriteFilter) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + MockWriteFilterCallbacks callbacks; + WriteFilterSharedPtr filter = std::make_shared(); + filter->initializeWriteFilterCallbacks(callbacks); + EXPECT_CALL(*created_connections_[0], addWriteFilter(filter)); + impl_->addWriteFilter(filter); +} + +TEST_F(HappyEyeballsConnectionImplTest, addFilter) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + MockReadFilterCallbacks read_callbacks; + MockWriteFilterCallbacks write_callbacks; + FilterSharedPtr filter = std::make_shared(); + filter->initializeReadFilterCallbacks(read_callbacks); + filter->initializeWriteFilterCallbacks(write_callbacks); + EXPECT_CALL(*created_connections_[0], addFilter(filter)); + impl_->addFilter(filter); +} + +TEST_F(HappyEyeballsConnectionImplTest, addBytesSentCallback) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + std::function cb = [](uint64_t) { return true; }; + EXPECT_CALL(*created_connections_[0], addBytesSentCallback(_)); + impl_->addBytesSentCallback(cb); +} + +TEST_F(HappyEyeballsConnectionImplTest, enableHalfClose) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); + impl_->enableHalfClose(true); +} + +TEST_F(HappyEyeballsConnectionImplTest, isHalfCloseEnabled) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(true)); + EXPECT_TRUE(impl_->isHalfCloseEnabled()); +} + +TEST_F(HappyEyeballsConnectionImplTest, nextProtocol) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], nextProtocol()).WillOnce(Return("h3")); + EXPECT_EQ("h3", impl_->nextProtocol()); +} + +TEST_F(HappyEyeballsConnectionImplTest, readDisable) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], readDisable(true)); + impl_->readDisable(true); +} + +TEST_F(HappyEyeballsConnectionImplTest, readEnabled) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], readEnabled()).WillOnce(Return(true)); + EXPECT_TRUE(impl_->readEnabled()); +} + +TEST_F(HappyEyeballsConnectionImplTest, addressProvider) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + + const SocketAddressSetterImpl provider(std::make_shared(80), + std::make_shared(80)); + EXPECT_CALL(*created_connections_[0], addressProvider()).WillOnce(ReturnRef(provider)); + impl_->addressProvider(); +} + +TEST_F(HappyEyeballsConnectionImplTest, addressProviderSharedPtr) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + + SocketAddressProviderSharedPtr provider = std::make_shared(std::make_shared(80), + std::make_shared(80)); + EXPECT_CALL(*created_connections_[0], addressProviderSharedPtr()).WillOnce(Return(provider)); + EXPECT_EQ(provider, impl_->addressProviderSharedPtr()); +} + +TEST_F(HappyEyeballsConnectionImplTest, unixSocketPeerCredentials) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + + EXPECT_CALL(*created_connections_[0], unixSocketPeerCredentials()).WillOnce(Return(absl::optional())); + EXPECT_FALSE(impl_->unixSocketPeerCredentials().has_value()); +} + +TEST_F(HappyEyeballsConnectionImplTest, ssl) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + + Ssl::ConnectionInfoConstSharedPtr ssl = nullptr; + EXPECT_CALL(*created_connections_[0], ssl()).WillOnce(Return(ssl)); + EXPECT_EQ(ssl, impl_->ssl()); +} + +TEST_F(HappyEyeballsConnectionImplTest, setBufferLimits) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], setBufferLimits(42)); + impl_->setBufferLimits(42); +} + +TEST_F(HappyEyeballsConnectionImplTest, requestedServerName) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], requestedServerName()).WillOnce(Return("name")); + EXPECT_EQ("name", impl_->requestedServerName()); +} + +TEST_F(HappyEyeballsConnectionImplTest, setDelayedCloseTimeout) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], setDelayedCloseTimeout(std::chrono::milliseconds(5))); + EXPECT_EQ("name", impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5))); +} + } // namespace Network } // namespace Envoy From cf2a0808722baf4381b104e3e59a34bce0ea8f34 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 13 Jul 2021 17:05:55 +0000 Subject: [PATCH 09/49] All unit tests Signed-off-by: Ryan Hamilton --- .../happy_eyeballs_connection_impl_test.cc | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 2b6038cbf16dd..fac66f46060b6 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -7,6 +7,7 @@ #include "test/mocks/network/transport_socket.h" #include "test/mocks/network/connection.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/stream_info/mocks.h" using testing::Return; using testing::ReturnRef; @@ -52,6 +53,15 @@ class HappyEyeballsConnectionImplTest : public testing::Test { return created_connections_.back(); } + void connectFirstAttempt() { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + } + protected: Event::MockDispatcher dispatcher_; testing::StrictMock* failover_timer_; @@ -637,6 +647,19 @@ TEST_F(HappyEyeballsConnectionImplTest, setBufferLimits) { impl_->setBufferLimits(42); } +TEST_F(HappyEyeballsConnectionImplTest, socketOptions) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + ConnectionSocket::OptionsSharedPtr options = nullptr; + EXPECT_CALL(*created_connections_[0], socketOptions()).WillOnce(ReturnRef(options));; + EXPECT_EQ(options, impl_->socketOptions()); +} + TEST_F(HappyEyeballsConnectionImplTest, requestedServerName) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -649,6 +672,19 @@ TEST_F(HappyEyeballsConnectionImplTest, requestedServerName) { EXPECT_EQ("name", impl_->requestedServerName()); } +TEST_F(HappyEyeballsConnectionImplTest, streamInfo) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + StreamInfo::MockStreamInfo info; + EXPECT_CALL(*created_connections_[0], streamInfo()).WillOnce(ReturnRef(info)); + EXPECT_EQ(&impl_->streamInfo(), &info); +} + TEST_F(HappyEyeballsConnectionImplTest, setDelayedCloseTimeout) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -658,7 +694,44 @@ TEST_F(HappyEyeballsConnectionImplTest, setDelayedCloseTimeout) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); EXPECT_CALL(*created_connections_[0], setDelayedCloseTimeout(std::chrono::milliseconds(5))); - EXPECT_EQ("name", impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5))); + impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5)); +} + +TEST_F(HappyEyeballsConnectionImplTest, transportFailureReason) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], transportFailureReason()).WillOnce(Return("reason")); + EXPECT_EQ("reason", impl_->transportFailureReason()); +} + +TEST_F(HappyEyeballsConnectionImplTest, startSecureTransport) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + EXPECT_CALL(*created_connections_[0], startSecureTransport()); + impl_->startSecureTransport(); +} + +TEST_F(HappyEyeballsConnectionImplTest, lastRoundTripTime) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + absl::optional rtt = std::chrono::milliseconds(5); + EXPECT_CALL(*created_connections_[0], lastRoundTripTime()).WillOnce(Return(rtt)); + EXPECT_EQ(rtt, impl_->lastRoundTripTime()); } } // namespace Network From ae4b12001e0eb97988ddab1475bcaee8efdbf113 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 13 Jul 2021 17:25:22 +0000 Subject: [PATCH 10/49] Cleanup Signed-off-by: Ryan Hamilton --- .../happy_eyeballs_connection_impl_test.cc | 202 ++++-------------- 1 file changed, 39 insertions(+), 163 deletions(-) diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index fac66f46060b6..beb0928709851 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -34,19 +34,15 @@ class HappyEyeballsConnectionImplTest : public testing::Test { EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(true)); next_connections_.push_back(std::make_unique>()); - std::cerr << __LINE__ << std::endl; impl_ = std::make_unique(dispatcher_, address_list_, Address::InstanceConstSharedPtr(), transport_socket_factory_, transport_socket_options_, options_); - std::cerr << __LINE__ << std::endl; - } MockClientConnection* createNextConnection() { - std::cerr << __LINE__ << " size: " << next_connections_.size() << std::endl; created_connections_.push_back(next_connections_.front().release()); next_connections_.pop_front(); EXPECT_CALL(*created_connections_.back(), addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& cb) -> void { connection_callbacks_.push_back(&cb);})); @@ -203,15 +199,11 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc ASSERT_EQ(2, created_connections_.size()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); - std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; connection_callbacks_[1]->onEvent(ConnectionEvent::RemoteClose); - std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); - std::cerr << __FUNCTION__ << ":" << __LINE__ << " \n"; } TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { @@ -237,7 +229,7 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); - // Verify that noDelay calls are delegated to the remaingin connection. + // Verify that noDelay calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], noDelay(false)); impl_->noDelay(false); } @@ -265,7 +257,7 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); - // Verify that detectEarlyCloseWhenReadDisabled calls are delegated to the remaingin connection. + // Verify that detectEarlyCloseWhenReadDisabled() calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], detectEarlyCloseWhenReadDisabled(false)); impl_->detectEarlyCloseWhenReadDisabled(false); } @@ -298,7 +290,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ ReadFilterSharedPtr filter2 = std::make_shared(); filter2->initializeReadFilterCallbacks(callbacks); - // Verify that detectEarlyCloseWhenReadDisabled calls are delegated to the remaingin connection. + // Verify that addReadFilter() calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], addReadFilter(filter2)); impl_->addReadFilter(filter2); } @@ -324,14 +316,13 @@ TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks){ EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks); })); - //EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(callbacks)); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); MockConnectionCallbacks callbacks2; - // Verify that detectEarlyCloseWhenReadDisabled calls are delegated to the remaingin connection. + // Verify that addConnectionCallbacks() calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks2); })); @@ -358,7 +349,7 @@ TEST_F(HappyEyeballsConnectionImplTest, RemoveConnectionCallbacks){ EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); - // Verify that removeConnectionCallbacks calls are delegated to the remaingin connection. + // Verify that removeConnectionCallbacks calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks2); })); @@ -386,24 +377,15 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } - -struct MockConnectionStats { - Connection::ConnectionStats toBufferStats() { - return {rx_total_, rx_current_, tx_total_, - tx_current_, &bind_errors_, &delayed_close_timeouts_}; - } - - StrictMock rx_total_; - StrictMock rx_current_; - StrictMock tx_total_; - StrictMock tx_current_; - StrictMock bind_errors_; - StrictMock delayed_close_timeouts_; -}; - TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ - MockConnectionStats stats; - Connection::ConnectionStats cs = { stats.rx_total_, stats.rx_current_, stats.tx_total_, stats.tx_current_, &stats.bind_errors_, &stats.delayed_close_timeouts_}; + StrictMock rx_total; + StrictMock rx_current; + StrictMock tx_total; + StrictMock tx_current; + StrictMock bind_errors; + StrictMock delayed_close_timeouts; + + Connection::ConnectionStats cs = { rx_total, rx_current, tx_total, tx_current, &bind_errors, &delayed_close_timeouts}; EXPECT_CALL(*created_connections_[0], setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs); })); @@ -430,14 +412,12 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); - /* - // Verify that setConnectionStats calls are delegated to the remaingin connection. - MockConnectionStats stats2; + // Verify that setConnectionStats calls are delegated to the remaining connection. + Connection::ConnectionStats cs2 = { rx_total, rx_current, tx_total, tx_current, &bind_errors, &delayed_close_timeouts}; EXPECT_CALL(*created_connections_[1], setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { - EXPECT_EQ(&s, &stats2); + EXPECT_EQ(&s, &cs2); })); - impl_->setConnectionStats(stats2); - */ + impl_->setConnectionStats(cs2); } TEST_F(HappyEyeballsConnectionImplTest, state) { @@ -474,12 +454,7 @@ TEST_F(HappyEyeballsConnectionImplTest, connecting) { // has finised. TEST_F(HappyEyeballsConnectionImplTest, addWriteFilter) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); MockWriteFilterCallbacks callbacks; WriteFilterSharedPtr filter = std::make_shared(); @@ -489,12 +464,7 @@ TEST_F(HappyEyeballsConnectionImplTest, addWriteFilter) { } TEST_F(HappyEyeballsConnectionImplTest, addFilter) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); MockReadFilterCallbacks read_callbacks; MockWriteFilterCallbacks write_callbacks; @@ -506,12 +476,7 @@ TEST_F(HappyEyeballsConnectionImplTest, addFilter) { } TEST_F(HappyEyeballsConnectionImplTest, addBytesSentCallback) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); std::function cb = [](uint64_t) { return true; }; EXPECT_CALL(*created_connections_[0], addBytesSentCallback(_)); @@ -519,73 +484,42 @@ TEST_F(HappyEyeballsConnectionImplTest, addBytesSentCallback) { } TEST_F(HappyEyeballsConnectionImplTest, enableHalfClose) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); impl_->enableHalfClose(true); } TEST_F(HappyEyeballsConnectionImplTest, isHalfCloseEnabled) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(true)); EXPECT_TRUE(impl_->isHalfCloseEnabled()); } TEST_F(HappyEyeballsConnectionImplTest, nextProtocol) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], nextProtocol()).WillOnce(Return("h3")); EXPECT_EQ("h3", impl_->nextProtocol()); } TEST_F(HappyEyeballsConnectionImplTest, readDisable) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], readDisable(true)); impl_->readDisable(true); } TEST_F(HappyEyeballsConnectionImplTest, readEnabled) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], readEnabled()).WillOnce(Return(true)); EXPECT_TRUE(impl_->readEnabled()); } TEST_F(HappyEyeballsConnectionImplTest, addressProvider) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); - + connectFirstAttempt(); const SocketAddressSetterImpl provider(std::make_shared(80), std::make_shared(80)); @@ -594,41 +528,23 @@ TEST_F(HappyEyeballsConnectionImplTest, addressProvider) { } TEST_F(HappyEyeballsConnectionImplTest, addressProviderSharedPtr) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); - + connectFirstAttempt(); - SocketAddressProviderSharedPtr provider = std::make_shared(std::make_shared(80), - std::make_shared(80)); + SocketAddressProviderSharedPtr provider = std::make_shared(std::make_shared("127.0.0.2"), + std::make_shared("127.0.0.1")); EXPECT_CALL(*created_connections_[0], addressProviderSharedPtr()).WillOnce(Return(provider)); EXPECT_EQ(provider, impl_->addressProviderSharedPtr()); } TEST_F(HappyEyeballsConnectionImplTest, unixSocketPeerCredentials) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); - + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], unixSocketPeerCredentials()).WillOnce(Return(absl::optional())); EXPECT_FALSE(impl_->unixSocketPeerCredentials().has_value()); } TEST_F(HappyEyeballsConnectionImplTest, ssl) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); - + connectFirstAttempt(); Ssl::ConnectionInfoConstSharedPtr ssl = nullptr; EXPECT_CALL(*created_connections_[0], ssl()).WillOnce(Return(ssl)); @@ -636,24 +552,14 @@ TEST_F(HappyEyeballsConnectionImplTest, ssl) { } TEST_F(HappyEyeballsConnectionImplTest, setBufferLimits) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], setBufferLimits(42)); impl_->setBufferLimits(42); } TEST_F(HappyEyeballsConnectionImplTest, socketOptions) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); ConnectionSocket::OptionsSharedPtr options = nullptr; EXPECT_CALL(*created_connections_[0], socketOptions()).WillOnce(ReturnRef(options));; @@ -661,24 +567,14 @@ TEST_F(HappyEyeballsConnectionImplTest, socketOptions) { } TEST_F(HappyEyeballsConnectionImplTest, requestedServerName) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], requestedServerName()).WillOnce(Return("name")); EXPECT_EQ("name", impl_->requestedServerName()); } TEST_F(HappyEyeballsConnectionImplTest, streamInfo) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); StreamInfo::MockStreamInfo info; EXPECT_CALL(*created_connections_[0], streamInfo()).WillOnce(ReturnRef(info)); @@ -686,48 +582,28 @@ TEST_F(HappyEyeballsConnectionImplTest, streamInfo) { } TEST_F(HappyEyeballsConnectionImplTest, setDelayedCloseTimeout) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], setDelayedCloseTimeout(std::chrono::milliseconds(5))); impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5)); } TEST_F(HappyEyeballsConnectionImplTest, transportFailureReason) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], transportFailureReason()).WillOnce(Return("reason")); EXPECT_EQ("reason", impl_->transportFailureReason()); } TEST_F(HappyEyeballsConnectionImplTest, startSecureTransport) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], startSecureTransport()); impl_->startSecureTransport(); } TEST_F(HappyEyeballsConnectionImplTest, lastRoundTripTime) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); absl::optional rtt = std::chrono::milliseconds(5); EXPECT_CALL(*created_connections_[0], lastRoundTripTime()).WillOnce(Return(rtt)); From 866cbd1fb214aa52851a9f2b60e9f1d1194f10c2 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 13 Jul 2021 17:29:13 +0000 Subject: [PATCH 11/49] Cleanup Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 6 +++--- .../happy_eyeballs_connection_impl_test.cc | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index cc453ece4e44a..820b008009b5d 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -166,9 +166,9 @@ void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { } uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { - // XXX - // TODO - huh? - //ASSERT(connect_finished_); + if (!connect_finished_) { + return 0; + } return connections_[0]->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index beb0928709851..3b3187e84e6bf 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -377,6 +377,22 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } +TEST_F(HappyEyeballsConnectionImplTest, BufferLimit) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Always returns 0 until connected. + EXPECT_EQ(0, impl_->bufferLimit()); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + // Delegates to the connection once connected. + EXPECT_CALL(*created_connections_[0], bufferLimit()).WillOnce(Return(42)); + EXPECT_EQ(42, impl_->bufferLimit()); +} + TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ StrictMock rx_total; StrictMock rx_current; From fa98c2b5a568f10c6d09b69ec3a0e37fa07b6c5d Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 13 Jul 2021 18:03:25 +0000 Subject: [PATCH 12/49] All but close()! Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 10 +++++----- .../happy_eyeballs_connection_impl_test.cc | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 820b008009b5d..63a8e1a861088 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -171,13 +171,13 @@ uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { } return connections_[0]->bufferLimit(); } + bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { - // XXX - /* - if (!connect_finished_) { + if (!connect_finished_) { + // TODO(rch): Either prohibit write before correct or eliminate infinite buffering. return false; - } - */ + } + return connections_[0]->aboveHighWatermark(); } diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 3b3187e84e6bf..cadae085323fd 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -393,6 +393,22 @@ TEST_F(HappyEyeballsConnectionImplTest, BufferLimit) { EXPECT_EQ(42, impl_->bufferLimit()); } +TEST_F(HappyEyeballsConnectionImplTest, AboveHighWatermark) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_FALSE(impl_->aboveHighWatermark()); + + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + + // Delegates to the connection once connected. + EXPECT_CALL(*created_connections_[0], aboveHighWatermark()).WillOnce(Return(true)); + EXPECT_TRUE(impl_->aboveHighWatermark()); +} + TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ StrictMock rx_total; StrictMock rx_current; From f7c14b653567878c396e3c5fd927ac002b73080c Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 13 Jul 2021 20:15:15 +0000 Subject: [PATCH 13/49] Close tests Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 25 ++++++++++++++-- .../happy_eyeballs_connection_impl_test.cc | 29 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 63a8e1a861088..16210df39b2ca 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -241,8 +241,29 @@ void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& ASSERT(false); } void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { - // TODO(XXX) - //ASSERT(connect_finished_); + if (connect_finished_) { + connections_[0]->close(type); + return; + } + + connect_finished_ = true; + next_attempt_timer_->disableTimer(); + for (size_t i = 0; i < connections_.size(); ++i) { + connections_[i]->removeConnectionCallbacks(*callbacks_wrappers_[i]); + if (i != 0) { + // Wait to close the final connection until the post-connection callbacks + // have been added. + connections_[i]->close(ConnectionCloseType::NoFlush); + } + } + connections_.resize(1); + callbacks_wrappers_.clear(); + + for (auto cb : post_connect_state_.connection_callbacks_) { + if (cb) { + connections_[0]->addConnectionCallbacks(*cb); + } + } connections_[0]->close(type); } diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index cadae085323fd..d9c319f0bea86 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -262,6 +262,35 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ impl_->detectEarlyCloseWhenReadDisabled(false); } +TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttempt) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::FlushWrite)); + EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); + + impl_->close(ConnectionCloseType::FlushWrite); +} + +TEST_F(HappyEyeballsConnectionImplTest, CloseAfterAttemptComplete) { + connectFirstAttempt(); + + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::FlushWrite)); + impl_->close(ConnectionCloseType::FlushWrite); +} + TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ MockReadFilterCallbacks callbacks; ReadFilterSharedPtr filter = std::make_shared(); From eca10094fe410c88fc718d1eac19266ea265b33d Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 14 Jul 2021 17:25:41 +0000 Subject: [PATCH 14/49] setBufferLimits Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 40 +++++-- .../network/happy_eyeballs_connection_impl.h | 8 +- .../happy_eyeballs_connection_impl_test.cc | 103 +++++++++++++----- 3 files changed, 108 insertions(+), 43 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 16210df39b2ca..f3345372174ae 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -7,11 +7,11 @@ namespace Network { HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( Event::Dispatcher& dispatcher, - const std::vector& address_list, - Network::Address::InstanceConstSharedPtr source_address, - Network::TransportSocketFactory& socket_factory, - Network::TransportSocketOptionsSharedPtr transport_socket_options, - const Network::ConnectionSocket::OptionsSharedPtr options) + const std::vector& address_list, + Address::InstanceConstSharedPtr source_address, + TransportSocketFactory& socket_factory, + TransportSocketOptionsConstSharedPtr transport_socket_options, + const ConnectionSocket::OptionsSharedPtr options) : dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), @@ -150,10 +150,13 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) } std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; - post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().create( + post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( [this]() -> void { this->onWriteBufferLowWatermark(); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); + if (per_connection_state_.buffer_limits_.has_value()) { + post_connect_state_.write_buffer_.value()->setWatermarks(per_connection_state_.buffer_limits_.value()); + } post_connect_state_.write_buffer_.value()->move(data); std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; post_connect_state_.end_stream_ = end_stream; @@ -161,12 +164,23 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) } void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { - ASSERT(connect_finished_); - connections_[0]->setBufferLimits(limit); + if (!connect_finished_) { + ASSERT(!per_connection_state_.buffer_limits_.has_value()); + per_connection_state_.buffer_limits_ = limit; + if (post_connect_state_.write_buffer_.has_value()) { + post_connect_state_.write_buffer_.value()->setWatermarks(per_connection_state_.buffer_limits_.value()); + } + } + for (auto& connection : connections_) { + connection->setBufferLimits(limit); + } } uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { if (!connect_finished_) { + if (per_connection_state_.buffer_limits_.has_value()) { + return per_connection_state_.buffer_limits_.value(); + } return 0; } return connections_[0]->bufferLimit(); @@ -174,8 +188,7 @@ uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { if (!connect_finished_) { - // TODO(rch): Either prohibit write before correct or eliminate infinite buffering. - return false; + return above_write_high_water_mark_; } return connections_[0]->aboveHighWatermark(); @@ -323,6 +336,9 @@ std::unique_ptr HappyEyeballsConnectionImpl::createNextConnect if (per_connection_state_.connection_stats_.has_value()) { connection->setConnectionStats(*per_connection_state_.connection_stats_.value()); } + if (per_connection_state_.buffer_limits_.has_value()) { + connection->setBufferLimits(per_connection_state_.buffer_limits_.value()); + } return connection; } @@ -468,8 +484,10 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallb } void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { - ASSERT(false); + ASSERT(!above_write_high_water_mark_); + above_write_high_water_mark_ = true; } + void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { ASSERT(false); } diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index bc042c04e0113..8a867dc507355 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -27,7 +27,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { const std::vector& address_list, Address::InstanceConstSharedPtr source_address, TransportSocketFactory& socket_factory, - TransportSocketOptionsSharedPtr transport_socket_options, + TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options); ~HappyEyeballsConnectionImpl() override; @@ -129,6 +129,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { absl::optional detect_early_close_when_read_disabled_; absl::optional no_delay_; absl::optional connection_stats_; + absl::optional buffer_limits_; }; // State which needs to be saved and applied only to the final connection @@ -141,11 +142,11 @@ class HappyEyeballsConnectionImpl : public ClientConnection { }; Event::Dispatcher& dispatcher_; - const std::vector& address_list_; + const std::vector& address_list_; size_t next_address_ = 0; Address::InstanceConstSharedPtr source_address_; TransportSocketFactory& socket_factory_; - TransportSocketOptionsSharedPtr transport_socket_options_; + TransportSocketOptionsConstSharedPtr transport_socket_options_; const ConnectionSocket::OptionsSharedPtr options_; // Set of active connections. @@ -156,6 +157,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { bool connect_finished_ = false; Event::TimerPtr next_attempt_timer_; + bool above_write_high_water_mark_ = false; PerConnectionState per_connection_state_; PostConnectState post_connect_state_; }; diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index d9c319f0bea86..9c8782ad8e5fc 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -62,7 +62,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { Event::MockDispatcher dispatcher_; testing::StrictMock* failover_timer_; MockTransportSocketFactory transport_socket_factory_; - TransportSocketOptionsSharedPtr transport_socket_options_; + TransportSocketOptionsConstSharedPtr transport_socket_options_; const ConnectionSocket::OptionsSharedPtr options_; const std::vector address_list_; std::vector*> created_connections_; @@ -406,6 +406,58 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } +TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*created_connections_[0], setBufferLimits(42)); + impl_->setBufferLimits(42); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + // setBufferLimits() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], setBufferLimits(42)); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that removeConnectionCallbacks calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], setBufferLimits(420)); + impl_->setBufferLimits(42); +} + +TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + Buffer::OwnedImpl data("hello world"); + bool end_stream = false; + + EXPECT_CALL(*created_connections_[0], setBufferLimits(data.length() - 1)); + impl_->setBufferLimits(data.length() - 1); + + impl_->write(data, end_stream); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*created_connections_[0], write(_, _)).WillOnce( + Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_EQ("hello world", data.toString()); + EXPECT_FALSE(end_stream); + ;}) + ); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); +} + TEST_F(HappyEyeballsConnectionImplTest, BufferLimit) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -481,7 +533,7 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ impl_->setConnectionStats(cs2); } -TEST_F(HappyEyeballsConnectionImplTest, state) { +TEST_F(HappyEyeballsConnectionImplTest, State) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -496,7 +548,7 @@ TEST_F(HappyEyeballsConnectionImplTest, state) { EXPECT_EQ(Connection::State::Closing, impl_->state()); } -TEST_F(HappyEyeballsConnectionImplTest, connecting) { +TEST_F(HappyEyeballsConnectionImplTest, Connecting) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -514,7 +566,7 @@ TEST_F(HappyEyeballsConnectionImplTest, connecting) { // Tests for HappyEyeballsConnectionImpl methods which must only be called after connect() // has finised. -TEST_F(HappyEyeballsConnectionImplTest, addWriteFilter) { +TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilter) { connectFirstAttempt(); MockWriteFilterCallbacks callbacks; @@ -524,7 +576,7 @@ TEST_F(HappyEyeballsConnectionImplTest, addWriteFilter) { impl_->addWriteFilter(filter); } -TEST_F(HappyEyeballsConnectionImplTest, addFilter) { +TEST_F(HappyEyeballsConnectionImplTest, AddFilter) { connectFirstAttempt(); MockReadFilterCallbacks read_callbacks; @@ -536,7 +588,7 @@ TEST_F(HappyEyeballsConnectionImplTest, addFilter) { impl_->addFilter(filter); } -TEST_F(HappyEyeballsConnectionImplTest, addBytesSentCallback) { +TEST_F(HappyEyeballsConnectionImplTest, AddBytesSentCallback) { connectFirstAttempt(); std::function cb = [](uint64_t) { return true; }; @@ -544,42 +596,42 @@ TEST_F(HappyEyeballsConnectionImplTest, addBytesSentCallback) { impl_->addBytesSentCallback(cb); } -TEST_F(HappyEyeballsConnectionImplTest, enableHalfClose) { +TEST_F(HappyEyeballsConnectionImplTest, EnableHalfClose) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); impl_->enableHalfClose(true); } -TEST_F(HappyEyeballsConnectionImplTest, isHalfCloseEnabled) { +TEST_F(HappyEyeballsConnectionImplTest, IsHalfCloseEnabled) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(true)); EXPECT_TRUE(impl_->isHalfCloseEnabled()); } -TEST_F(HappyEyeballsConnectionImplTest, nextProtocol) { +TEST_F(HappyEyeballsConnectionImplTest, NextProtocol) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], nextProtocol()).WillOnce(Return("h3")); EXPECT_EQ("h3", impl_->nextProtocol()); } -TEST_F(HappyEyeballsConnectionImplTest, readDisable) { +TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], readDisable(true)); impl_->readDisable(true); } -TEST_F(HappyEyeballsConnectionImplTest, readEnabled) { +TEST_F(HappyEyeballsConnectionImplTest, ReadEnabled) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], readEnabled()).WillOnce(Return(true)); EXPECT_TRUE(impl_->readEnabled()); } -TEST_F(HappyEyeballsConnectionImplTest, addressProvider) { +TEST_F(HappyEyeballsConnectionImplTest, AddressProvider) { connectFirstAttempt(); const SocketAddressSetterImpl provider(std::make_shared(80), @@ -588,7 +640,7 @@ TEST_F(HappyEyeballsConnectionImplTest, addressProvider) { impl_->addressProvider(); } -TEST_F(HappyEyeballsConnectionImplTest, addressProviderSharedPtr) { +TEST_F(HappyEyeballsConnectionImplTest, AddressProviderSharedPtr) { connectFirstAttempt(); SocketAddressProviderSharedPtr provider = std::make_shared(std::make_shared("127.0.0.2"), @@ -597,14 +649,14 @@ TEST_F(HappyEyeballsConnectionImplTest, addressProviderSharedPtr) { EXPECT_EQ(provider, impl_->addressProviderSharedPtr()); } -TEST_F(HappyEyeballsConnectionImplTest, unixSocketPeerCredentials) { +TEST_F(HappyEyeballsConnectionImplTest, UnixSocketPeerCredentials) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], unixSocketPeerCredentials()).WillOnce(Return(absl::optional())); EXPECT_FALSE(impl_->unixSocketPeerCredentials().has_value()); } -TEST_F(HappyEyeballsConnectionImplTest, ssl) { +TEST_F(HappyEyeballsConnectionImplTest, Ssl) { connectFirstAttempt(); Ssl::ConnectionInfoConstSharedPtr ssl = nullptr; @@ -612,14 +664,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ssl) { EXPECT_EQ(ssl, impl_->ssl()); } -TEST_F(HappyEyeballsConnectionImplTest, setBufferLimits) { - connectFirstAttempt(); - - EXPECT_CALL(*created_connections_[0], setBufferLimits(42)); - impl_->setBufferLimits(42); -} - -TEST_F(HappyEyeballsConnectionImplTest, socketOptions) { +TEST_F(HappyEyeballsConnectionImplTest, SocketOptions) { connectFirstAttempt(); ConnectionSocket::OptionsSharedPtr options = nullptr; @@ -627,14 +672,14 @@ TEST_F(HappyEyeballsConnectionImplTest, socketOptions) { EXPECT_EQ(options, impl_->socketOptions()); } -TEST_F(HappyEyeballsConnectionImplTest, requestedServerName) { +TEST_F(HappyEyeballsConnectionImplTest, RequestedServerName) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], requestedServerName()).WillOnce(Return("name")); EXPECT_EQ("name", impl_->requestedServerName()); } -TEST_F(HappyEyeballsConnectionImplTest, streamInfo) { +TEST_F(HappyEyeballsConnectionImplTest, StreamInfo) { connectFirstAttempt(); StreamInfo::MockStreamInfo info; @@ -642,28 +687,28 @@ TEST_F(HappyEyeballsConnectionImplTest, streamInfo) { EXPECT_EQ(&impl_->streamInfo(), &info); } -TEST_F(HappyEyeballsConnectionImplTest, setDelayedCloseTimeout) { +TEST_F(HappyEyeballsConnectionImplTest, SetDelayedCloseTimeout) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], setDelayedCloseTimeout(std::chrono::milliseconds(5))); impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5)); } -TEST_F(HappyEyeballsConnectionImplTest, transportFailureReason) { +TEST_F(HappyEyeballsConnectionImplTest, TransportFailureReason) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], transportFailureReason()).WillOnce(Return("reason")); EXPECT_EQ("reason", impl_->transportFailureReason()); } -TEST_F(HappyEyeballsConnectionImplTest, startSecureTransport) { +TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransport) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], startSecureTransport()); impl_->startSecureTransport(); } -TEST_F(HappyEyeballsConnectionImplTest, lastRoundTripTime) { +TEST_F(HappyEyeballsConnectionImplTest, LastRoundTripTime) { connectFirstAttempt(); absl::optional rtt = std::chrono::milliseconds(5); From e6f2c19258f2746ea144c533e8d74add84624224 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 14 Jul 2021 17:26:44 +0000 Subject: [PATCH 15/49] setBufferLimits Signed-off-by: Ryan Hamilton --- .../common/network/happy_eyeballs_connection_impl.cc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index f3345372174ae..f67eb2f2c0a47 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -141,26 +141,20 @@ bool HappyEyeballsConnectionImpl::connecting() const { } void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; if (connect_finished_) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connections_[0]->write(data, end_stream); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; return; } - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( - [this]() -> void { this->onWriteBufferLowWatermark(); }, + []() -> void { ASSERT(false); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, - []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); + []() -> void { ASSERT(false) }); if (per_connection_state_.buffer_limits_.has_value()) { post_connect_state_.write_buffer_.value()->setWatermarks(per_connection_state_.buffer_limits_.value()); } post_connect_state_.write_buffer_.value()->move(data); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; post_connect_state_.end_stream_ = end_stream; - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { From d038acb7e6454a6c54e402814494aa3558855b6a Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 14 Jul 2021 18:44:47 +0000 Subject: [PATCH 16/49] Cleanup Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 71 ++----------------- .../network/happy_eyeballs_connection_impl.h | 3 +- .../happy_eyeballs_connection_impl_test.cc | 12 +++- 3 files changed, 18 insertions(+), 68 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index f67eb2f2c0a47..817af22dda338 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -149,7 +149,7 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( []() -> void { ASSERT(false); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, - []() -> void { ASSERT(false) }); + []() -> void { ASSERT(false); }); if (per_connection_state_.buffer_limits_.has_value()) { post_connect_state_.write_buffer_.value()->setWatermarks(per_connection_state_.buffer_limits_.value()); } @@ -233,11 +233,9 @@ void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& cb) { if (connect_finished_) { - std::cerr << __FUNCTION__ << " removing\n"; connections_[0]->removeConnectionCallbacks(cb); return; } - std::cerr << __FUNCTION__ << " removing from queue\n"; auto i = post_connect_state_.connection_callbacks_.begin(); while (i != post_connect_state_.connection_callbacks_.end()) { if (*i == &cb) { @@ -310,16 +308,12 @@ void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) std::unique_ptr HappyEyeballsConnectionImpl::createNextConnection() { ASSERT(next_address_ < address_list_.size()); - std::cerr << __FUNCTION__ << "\n"; auto connection = dispatcher_.createClientConnection( address_list_[next_address_++], source_address_, socket_factory_.createTransportSocket(transport_socket_options_), options_); - std::cerr << "connection: " << connection.get() << std::endl; callbacks_wrappers_.push_back(std::make_unique(*this, *connection)); - std::cerr << "connection: " << connection.get() << std::endl; connection->addConnectionCallbacks(*callbacks_wrappers_.back()); - std::cerr << "connection: " << connection.get() << std::endl; if (per_connection_state_.detect_early_close_when_read_disabled_.has_value()) { connection->detectEarlyCloseWhenReadDisabled(per_connection_state_.detect_early_close_when_read_disabled_.value()); @@ -337,25 +331,19 @@ std::unique_ptr HappyEyeballsConnectionImpl::createNextConnect } void HappyEyeballsConnectionImpl::tryAnotherConnection() { - std::cerr << __FUNCTION__ << "\n"; connections_.push_back(createNextConnection()); connections_.back()->connect(); maybeScheduleNextAttempt(); } void HappyEyeballsConnectionImpl::maybeScheduleNextAttempt() { - std::cerr << __FUNCTION__ << "\n"; if (next_address_ >= address_list_.size()) { return; } - std::cerr << __FUNCTION__ << "\n"; next_attempt_timer_->enableTimer(std::chrono::milliseconds(300)); } void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper) { - std::cerr << __FUNCTION__ << " " << static_cast(event) << " 1\n"; - //ASSERT(wrapper == callbacks_wrapper_.get()); - wrapper->connection().removeConnectionCallbacks(*wrapper); if (event != ConnectionEvent::Connected) { if (next_address_ < address_list_.size()) { @@ -367,47 +355,25 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb cleanupWrapperAndConnection(wrapper); return; } - //ASSERT(false); /// XXX have coverage for this case. } connect_finished_ = true; next_attempt_timer_->disableTimer(); - // Clean up other connections. - std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; - std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; - - { + // Close and delete up other connections. auto it = connections_.begin(); while (it != connections_.end()) { if (it->get() != &(wrapper->connection())) { + (*it)->removeConnectionCallbacks(*wrapper); (*it)->close(ConnectionCloseType::NoFlush); it = connections_.erase(it); } else { ++it; } } - } - { - auto it = callbacks_wrappers_.begin(); - while (it != callbacks_wrappers_.end()) { - if (it->get() != wrapper) { - it = callbacks_wrappers_.erase(it); - } else { - ++it; - } - } - } - /* - std::remove_if(connections_.begin(), connections_.end(), [wrapper](std::unique_ptr& connection) { return connection.get() != &(wrapper->connection()); }); - std::remove_if(callbacks_wrappers_.begin(), callbacks_wrappers_.end(), [wrapper](std::unique_ptr& callbacks_wrapper) { return callbacks_wrapper.get() != wrapper; }); - */ - std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; - std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; ASSERT(connections_.size() == 1); - ASSERT(callbacks_wrappers_.size() == 1); - callbacks_wrappers_.clear(); + // Apply post-connect state to the final socket. for (auto cb : post_connect_state_.connection_callbacks_) { if (cb) { @@ -429,23 +395,10 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb std::vector cbs; cbs.swap(post_connect_state_.connection_callbacks_); - /* - for (auto cb : cbs) { - std::cerr << __FUNCTION__ << " calling cb->onEvent\n"; - cb->onEvent(event); - std::cerr << __FUNCTION__ << " done\n"; - } - */ - std::cerr << __FUNCTION__ << " finished\n"; } void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper) { - std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; - std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; - - { - auto it = connections_.begin(); - while (it != connections_.end()) { + for (auto it = connections_.begin(); it != connections_.end();) { if (it->get() == &(wrapper->connection())) { (*it)->close(ConnectionCloseType::NoFlush); it = connections_.erase(it); @@ -453,20 +406,14 @@ void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallback ++it; } } - } - { - auto it = callbacks_wrappers_.begin(); - while (it != callbacks_wrappers_.end()) { + + for (auto it = callbacks_wrappers_.begin(); it != callbacks_wrappers_.end(); ) { if (it->get() == wrapper) { it = callbacks_wrappers_.erase(it); } else { ++it; } } - } - - std::cerr << __FUNCTION__ << " " << connections_.size() << " \n"; - std::cerr << __FUNCTION__ << " " << callbacks_wrappers_.size() << " \n"; } void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { @@ -482,9 +429,5 @@ void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { above_write_high_water_mark_ = true; } -void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { - ASSERT(false); -} - } // namespace Network } // namespace Envoy diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 8a867dc507355..bdea8fb40c1c5 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -120,7 +120,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); void onWriteBufferHighWatermark(); - void onWriteBufferLowWatermark(); void cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper); @@ -157,7 +156,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { bool connect_finished_ = false; Event::TimerPtr next_attempt_timer_; - bool above_write_high_water_mark_ = false; + bool above_write_high_water_mark_ = false; PerConnectionState per_connection_state_; PostConnectState post_connect_state_; }; diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 9c8782ad8e5fc..9d53a00c47733 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -143,6 +143,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); @@ -166,6 +167,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -226,6 +228,7 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -254,6 +257,7 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -314,6 +318,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ EXPECT_CALL(*created_connections_[1], addReadFilter(filter)); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -347,6 +352,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks){ })); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); @@ -420,18 +426,19 @@ TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); // setBufferLimits() should be applied to the newly created connection. - EXPECT_CALL(*created_connections_[1], setBufferLimits(42)); + EXPECT_CALL(*next_connections_.back(), setBufferLimits(42)); EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); // Verify that removeConnectionCallbacks calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], setBufferLimits(420)); - impl_->setBufferLimits(42); + impl_->setBufferLimits(420); } TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { @@ -522,6 +529,7 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); From 3b7a5b10795ed48fe2b61bd262dd630019b76603 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 15 Jul 2021 19:24:10 +0000 Subject: [PATCH 17/49] Working Signed-off-by: Ryan Hamilton --- source/common/conn_pool/conn_pool_base.cc | 11 --- source/common/http/conn_pool_base.h | 2 - source/common/upstream/health_checker_impl.cc | 10 --- source/common/upstream/logical_host.cc | 4 +- source/common/upstream/upstream_impl.cc | 27 ++++++-- source/common/upstream/upstream_impl.h | 4 ++ test/common/upstream/upstream_impl_test.cc | 67 +++++++++++++++++++ 7 files changed, 94 insertions(+), 31 deletions(-) diff --git a/source/common/conn_pool/conn_pool_base.cc b/source/common/conn_pool/conn_pool_base.cc index 381d6a46ff352..c6276446f4a9d 100644 --- a/source/common/conn_pool/conn_pool_base.cc +++ b/source/common/conn_pool/conn_pool_base.cc @@ -388,7 +388,6 @@ void ConnPoolImplBase::checkForDrained() { void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view failure_reason, Network::ConnectionEvent event) { - std::cerr << __FUNCTION__ << " here! 1\n"; if (client.state() == ActiveClient::State::CONNECTING) { ASSERT(connecting_stream_capacity_ >= client.effectiveConcurrentStreamLimit()); connecting_stream_capacity_ -= client.effectiveConcurrentStreamLimit(); @@ -399,7 +398,6 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view client.connect_timer_.reset(); } - std::cerr << __FUNCTION__ << " here! 2\n"; if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { state_.decrConnectingAndConnectedStreamCapacity(client.currentUnusedCapacity()); @@ -456,25 +454,16 @@ void ConnPoolImplBase::onConnectionEvent(ActiveClient& client, absl::string_view tryCreateNewConnections(); } } else if (event == Network::ConnectionEvent::Connected) { - std::cerr << __FUNCTION__ << " here! 3 " << client.conn_connect_ms_.get() << "\n"; client.conn_connect_ms_->complete(); - std::cerr << __FUNCTION__ << " here! 4\n"; client.conn_connect_ms_.reset(); - std::cerr << __FUNCTION__ << " here! 4\n"; ASSERT(client.state() == ActiveClient::State::CONNECTING); - std::cerr << __FUNCTION__ << " here! 4\n"; transitionActiveClientState(client, ActiveClient::State::READY); - std::cerr << __FUNCTION__ << " here! 4\n"; // At this point, for the mixed ALPN pool, the client may be deleted. Do not // refer to client after this point. - std::cerr << __FUNCTION__ << " here! 5\n"; onConnected(client); - std::cerr << __FUNCTION__ << " here! 6\n"; onUpstreamReady(); - std::cerr << __FUNCTION__ << " here! 7\n"; checkForDrained(); - std::cerr << __FUNCTION__ << " here! 8\n"; } } diff --git a/source/common/http/conn_pool_base.h b/source/common/http/conn_pool_base.h index 1e87f8cd5be70..0d72454786204 100644 --- a/source/common/http/conn_pool_base.h +++ b/source/common/http/conn_pool_base.h @@ -124,8 +124,6 @@ class ActiveClient : public Envoy::ConnectionPool::ActiveClient { void close() override { codec_client_->close(); } virtual Http::RequestEncoder& newStreamEncoder(Http::ResponseDecoder& response_decoder) PURE; void onEvent(Network::ConnectionEvent event) override { - std::cerr << __FUNCTION__ << " here! this: " << this << " event: " << static_cast(event) << " codec_client_:" << codec_client_.get() << "\n"; - //ASSERT(false); parent_.onConnectionEvent(*this, codec_client_->connectionFailureReason(), event); } uint32_t numActiveStreams() const override { return codec_client_->numActiveRequests(); } diff --git a/source/common/upstream/health_checker_impl.cc b/source/common/upstream/health_checker_impl.cc index 983448dab2a62..2859e58767584 100644 --- a/source/common/upstream/health_checker_impl.cc +++ b/source/common/upstream/health_checker_impl.cc @@ -546,24 +546,17 @@ void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onEvent(Network::Connect // TODO(lilika) : Support connection pooling void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onInterval() { if (!client_) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_ = host_ ->createHealthCheckConnection(parent_.dispatcher_, parent_.transportSocketOptions(), parent_.transportSocketMatchMetadata().get()) .connection_; - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; session_callbacks_ = std::make_shared(*this); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->addConnectionCallbacks(*session_callbacks_); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->addReadFilter(session_callbacks_); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; expect_close_ = false; - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->connect(); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->noDelay(true); } @@ -573,11 +566,8 @@ void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onInterval() { data.add(segment.data(), segment.size()); } - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; client_->write(data, false); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } void TcpHealthCheckerImpl::TcpActiveHealthCheckSession::onTimeout() { diff --git a/source/common/upstream/logical_host.cc b/source/common/upstream/logical_host.cc index 847fcf83a9bc8..1f9b6375f7f7b 100644 --- a/source/common/upstream/logical_host.cc +++ b/source/common/upstream/logical_host.cc @@ -7,8 +7,10 @@ Upstream::Host::CreateConnectionData LogicalHost::createConnection( Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options) const { const auto current_address = address(); + const std::vector& address_list = addressList(); + return {HostImpl::createConnection( - dispatcher, cluster(), current_address, transportSocketFactory(), options, + dispatcher, cluster(), current_address, address_list, transportSocketFactory(), options, override_transport_socket_options_ != nullptr ? override_transport_socket_options_ : transport_socket_options), std::make_shared(current_address, shared_from_this())}; diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index d247705bd8854..6c09178d2b64b 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -282,7 +282,7 @@ Network::TransportSocketFactory& HostDescriptionImpl::resolveTransportSocketFact Host::CreateConnectionData HostImpl::createConnection( Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options) const { - return {createConnection(dispatcher, cluster(), address(), transportSocketFactory(), options, + return {createConnection(dispatcher, cluster(), address(), addressList(), transportSocketFactory(), options, transport_socket_options), shared_from_this()}; } @@ -313,7 +313,7 @@ Host::CreateConnectionData HostImpl::createHealthCheckConnection( Network::TransportSocketFactory& factory = (metadata != nullptr) ? resolveTransportSocketFactory(healthCheckAddress(), metadata) : transportSocketFactory(); - return {createConnection(dispatcher, cluster(), healthCheckAddress(), factory, nullptr, + return {createConnection(dispatcher, cluster(), healthCheckAddress(), {}, factory, nullptr, transport_socket_options), shared_from_this()}; } @@ -321,36 +321,49 @@ Host::CreateConnectionData HostImpl::createHealthCheckConnection( Network::ClientConnectionPtr HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& cluster, const Network::Address::InstanceConstSharedPtr& address, + const std::vector& address_list, Network::TransportSocketFactory& socket_factory, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options) { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; Network::ConnectionSocket::OptionsSharedPtr connection_options; if (cluster.clusterSocketOptions() != nullptr) { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; if (options) { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection_options = std::make_shared(); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; *connection_options = *options; + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; std::copy(cluster.clusterSocketOptions()->begin(), cluster.clusterSocketOptions()->end(), std::back_inserter(*connection_options)); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } else { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection_options = cluster.clusterSocketOptions(); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } } else { + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection_options = options; + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; ASSERT(!address->envoyInternalAddress()); Network::ClientConnectionPtr connection = - /* - true ? - std::make_unique(dispatcher, addressList(), cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options) - : - */ + address_list.size() > 1 ? + std::make_unique(dispatcher, address_list, cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options) + : dispatcher.createClientConnection( address, cluster.sourceAddress(), socket_factory.createTransportSocket(std::move(transport_socket_options)), connection_options); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection->setBufferLimits(cluster.perConnectionBufferLimitBytes()); + std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; cluster.createNetworkFilterChain(*connection); + std::cerr << __FUNCTION__ << ":" << __LINE__ << " " << connection.get() << std::endl; return connection; } diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index ec2eb3e147392..5cc5ef1a2b5f2 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -153,6 +153,9 @@ class HostDescriptionImpl : virtual public HostDescription, const envoy::config::core::v3::Metadata* metadata) const; MonotonicTime creationTime() const override { return creation_time_; } + void setAddressList(const std::vector& address_list) { + address_list_ = address_list; + } protected: void setAddress(Network::Address::InstanceConstSharedPtr address) { address_ = address; } @@ -265,6 +268,7 @@ class HostImpl : public HostDescriptionImpl, static Network::ClientConnectionPtr createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& cluster, const Network::Address::InstanceConstSharedPtr& address, + const std::vector& address_list, Network::TransportSocketFactory& socket_factory, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options); diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index 00577b8c4df78..f2a151c3ec38b 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -1307,6 +1307,73 @@ TEST_F(HostImplTest, HostnameCanaryAndLocality) { EXPECT_EQ(1, host.priority()); } +TEST_F(HostImplTest, CreateConnection) { + MockClusterMockPrioritySet cluster; + envoy::config::core::v3::Metadata metadata; + Config::Metadata::mutableMetadataValue(metadata, Config::MetadataFilters::get().ENVOY_LB, + Config::MetadataEnvoyLbKeys::get().CANARY) + .set_bool_value(true); + envoy::config::core::v3::Locality locality; + locality.set_region("oceania"); + locality.set_zone("hello"); + locality.set_sub_zone("world"); + Network::Address::InstanceConstSharedPtr address = Network::Utility::resolveUrl("tcp://10.0.0.1:1234"); + auto host = std::make_shared(cluster.info_, "lyft.com", address, + std::make_shared(metadata), 1, locality, + envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 1, + envoy::config::core::v3::UNKNOWN, simTime()); + + testing::StrictMock dispatcher; + Network::TransportSocketOptionsConstSharedPtr transport_socket_options; + Network::ConnectionSocket::OptionsSharedPtr options; + Network::MockTransportSocketFactory socket_factory; + + auto connection = new testing::StrictMock(); + EXPECT_CALL(*connection, setBufferLimits(0)); + EXPECT_CALL(dispatcher, createClientConnection_(_, _, _, _)).WillOnce(Return(connection)); + + Envoy::Upstream::Host::CreateConnectionData connection_data = host->createConnection(dispatcher, options, transport_socket_options); + EXPECT_EQ(connection, connection_data.connection_.get()); +} + +TEST_F(HostImplTest, CreateConnectionHappyEyeballs) { + MockClusterMockPrioritySet cluster; + envoy::config::core::v3::Metadata metadata; + Config::Metadata::mutableMetadataValue(metadata, Config::MetadataFilters::get().ENVOY_LB, + Config::MetadataEnvoyLbKeys::get().CANARY) + .set_bool_value(true); + envoy::config::core::v3::Locality locality; + locality.set_region("oceania"); + locality.set_zone("hello"); + locality.set_sub_zone("world"); + Network::Address::InstanceConstSharedPtr address = Network::Utility::resolveUrl("tcp://10.0.0.1:1234"); + auto host = std::make_shared(cluster.info_, "lyft.com", address, + std::make_shared(metadata), 1, locality, + envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 1, + envoy::config::core::v3::UNKNOWN, simTime()); + + testing::StrictMock dispatcher; + Network::TransportSocketOptionsConstSharedPtr transport_socket_options; + Network::ConnectionSocket::OptionsSharedPtr options; + Network::MockTransportSocketFactory socket_factory; + + std::vector address_list = { + Network::Utility::resolveUrl("tcp://10.0.0.1:1235"), + address, + }; + host->setAddressList(address_list); + auto connection = new testing::StrictMock(); + EXPECT_CALL(*connection, setBufferLimits(0)); + EXPECT_CALL(*connection, addConnectionCallbacks(_)); + // The underlying connection should be created with the first address in the list. + EXPECT_CALL(dispatcher, createClientConnection_(address_list[0], _, _, _)).WillOnce(Return(connection)); + EXPECT_CALL(dispatcher, createTimer_(_)); + + Envoy::Upstream::Host::CreateConnectionData connection_data = host->createConnection(dispatcher, options, transport_socket_options); + // The created connection will be wrapped in a HappyEyeballsConnectionImpl. + EXPECT_NE(connection, connection_data.connection_.get()); +} + TEST_F(HostImplTest, HealthFlags) { MockClusterMockPrioritySet cluster; HostSharedPtr host = makeTestHost(cluster.info_, "tcp://10.0.0.1:1234", simTime(), 1); From 0ac5d9145793032ea246b65cd3850471ca7fdbb0 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 15 Jul 2021 19:25:07 +0000 Subject: [PATCH 18/49] Cleanup Signed-off-by: Ryan Hamilton --- source/common/upstream/upstream_impl.cc | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 6c09178d2b64b..7e16e3797c1c7 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -325,30 +325,19 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu Network::TransportSocketFactory& socket_factory, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; Network::ConnectionSocket::OptionsSharedPtr connection_options; if (cluster.clusterSocketOptions() != nullptr) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; if (options) { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection_options = std::make_shared(); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; *connection_options = *options; - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; std::copy(cluster.clusterSocketOptions()->begin(), cluster.clusterSocketOptions()->end(), std::back_inserter(*connection_options)); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } else { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection_options = cluster.clusterSocketOptions(); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } } else { - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection_options = options; - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; } - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; ASSERT(!address->envoyInternalAddress()); Network::ClientConnectionPtr connection = address_list.size() > 1 ? @@ -358,12 +347,9 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu address, cluster.sourceAddress(), socket_factory.createTransportSocket(std::move(transport_socket_options)), connection_options); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; connection->setBufferLimits(cluster.perConnectionBufferLimitBytes()); - std::cerr << __FUNCTION__ << ":" << __LINE__ << std::endl; cluster.createNetworkFilterChain(*connection); - std::cerr << __FUNCTION__ << ":" << __LINE__ << " " << connection.get() << std::endl; return connection; } From 9f6ac8879b78198ac3438f510427f38ba012d72a Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 15 Jul 2021 19:27:08 +0000 Subject: [PATCH 19/49] Format Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 46 ++-- .../network/happy_eyeballs_connection_impl.h | 19 +- source/common/upstream/logical_host.cc | 9 +- source/common/upstream/upstream_impl.cc | 33 +-- source/common/upstream/upstream_impl.h | 1 + .../happy_eyeballs_connection_impl_test.cc | 214 +++++++++--------- test/common/upstream/upstream_impl_test.cc | 37 +-- 7 files changed, 187 insertions(+), 172 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 817af22dda338..e34c05bccc7c1 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -6,17 +6,12 @@ namespace Envoy { namespace Network { HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( - Event::Dispatcher& dispatcher, - const std::vector& address_list, - Address::InstanceConstSharedPtr source_address, - TransportSocketFactory& socket_factory, + Event::Dispatcher& dispatcher, const std::vector& address_list, + Address::InstanceConstSharedPtr source_address, TransportSocketFactory& socket_factory, TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) - : dispatcher_(dispatcher), - address_list_(address_list), - source_address_(source_address), - socket_factory_(socket_factory), - transport_socket_options_(transport_socket_options), + : dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), + socket_factory_(socket_factory), transport_socket_options_(transport_socket_options), options_(options), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { connections_.push_back(createNextConnection()); @@ -118,7 +113,8 @@ SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderShare return connections_[0]->addressProviderSharedPtr(); } -absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { +absl::optional +HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { ASSERT(connect_finished_); return connections_[0]->unixSocketPeerCredentials(); } @@ -147,11 +143,11 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) } post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( - []() -> void { ASSERT(false); }, - [this]() -> void { this->onWriteBufferHighWatermark(); }, + []() -> void { ASSERT(false); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, []() -> void { ASSERT(false); }); if (per_connection_state_.buffer_limits_.has_value()) { - post_connect_state_.write_buffer_.value()->setWatermarks(per_connection_state_.buffer_limits_.value()); + post_connect_state_.write_buffer_.value()->setWatermarks( + per_connection_state_.buffer_limits_.value()); } post_connect_state_.write_buffer_.value()->move(data); post_connect_state_.end_stream_ = end_stream; @@ -162,7 +158,8 @@ void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { ASSERT(!per_connection_state_.buffer_limits_.has_value()); per_connection_state_.buffer_limits_ = limit; if (post_connect_state_.write_buffer_.has_value()) { - post_connect_state_.write_buffer_.value()->setWatermarks(per_connection_state_.buffer_limits_.value()); + post_connect_state_.write_buffer_.value()->setWatermarks( + per_connection_state_.buffer_limits_.value()); } } for (auto& connection : connections_) { @@ -310,13 +307,13 @@ std::unique_ptr HappyEyeballsConnectionImpl::createNextConnect ASSERT(next_address_ < address_list_.size()); auto connection = dispatcher_.createClientConnection( address_list_[next_address_++], source_address_, - socket_factory_.createTransportSocket(transport_socket_options_), - options_); + socket_factory_.createTransportSocket(transport_socket_options_), options_); callbacks_wrappers_.push_back(std::make_unique(*this, *connection)); connection->addConnectionCallbacks(*callbacks_wrappers_.back()); if (per_connection_state_.detect_early_close_when_read_disabled_.has_value()) { - connection->detectEarlyCloseWhenReadDisabled(per_connection_state_.detect_early_close_when_read_disabled_.value()); + connection->detectEarlyCloseWhenReadDisabled( + per_connection_state_.detect_early_close_when_read_disabled_.value()); } if (per_connection_state_.no_delay_.has_value()) { connection->noDelay(per_connection_state_.no_delay_.value()); @@ -343,7 +340,8 @@ void HappyEyeballsConnectionImpl::maybeScheduleNextAttempt() { next_attempt_timer_->enableTimer(std::chrono::milliseconds(300)); } -void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper) { +void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, + ConnectionCallbacksWrapper* wrapper) { wrapper->connection().removeConnectionCallbacks(*wrapper); if (event != ConnectionEvent::Connected) { if (next_address_ < address_list_.size()) { @@ -387,10 +385,10 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallb } if (post_connect_state_.write_buffer_.has_value()) { - //ASSERT(false); // write_buffer_ and end_stream_ are both set together in write(). ASSERT(post_connect_state_.end_stream_.has_value()); - connections_[0]->write(*post_connect_state_.write_buffer_.value(), post_connect_state_.end_stream_.value()); + connections_[0]->write(*post_connect_state_.write_buffer_.value(), + post_connect_state_.end_stream_.value()); } std::vector cbs; @@ -407,7 +405,7 @@ void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallback } } - for (auto it = callbacks_wrappers_.begin(); it != callbacks_wrappers_.end(); ) { + for (auto it = callbacks_wrappers_.begin(); it != callbacks_wrappers_.end();) { if (it->get() == wrapper) { it = callbacks_wrappers_.erase(it); } else { @@ -416,11 +414,13 @@ void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallback } } -void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { +void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark( + ConnectionCallbacksWrapper* /*wrapper*/) { ASSERT(false); } -void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* /*wrapper*/) { +void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( + ConnectionCallbacksWrapper* /*wrapper*/) { ASSERT(false); } diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index bdea8fb40c1c5..886882a820732 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -10,10 +10,10 @@ #include "envoy/network/connection.h" #include "envoy/network/transport_socket.h" -#include "absl/types/optional.h" - #include "source/common/network/connection_impl.h" +#include "absl/types/optional.h" + namespace Envoy { namespace Network { @@ -84,27 +84,22 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // ConnectionCallbacks which will be set on an ClientConnection which // sends connection events back to the HappyEyeballsConnectionImpl. class ConnectionCallbacksWrapper : public ConnectionCallbacks { - public: - ConnectionCallbacksWrapper(HappyEyeballsConnectionImpl& parent, - ClientConnection& connection) + public: + ConnectionCallbacksWrapper(HappyEyeballsConnectionImpl& parent, ClientConnection& connection) : parent_(parent), connection_(connection) {} - void onEvent(ConnectionEvent event) override { - parent_.onEvent(event, this); - } + void onEvent(ConnectionEvent event) override { parent_.onEvent(event, this); } void onAboveWriteBufferHighWatermark() override { parent_.onAboveWriteBufferHighWatermark(this); } - void onBelowWriteBufferLowWatermark() override { - parent_.onBelowWriteBufferLowWatermark(this); - } + void onBelowWriteBufferLowWatermark() override { parent_.onBelowWriteBufferLowWatermark(this); } // Not needed? interesting. ClientConnection& connection() { return connection_; } - private: + private: HappyEyeballsConnectionImpl& parent_; ClientConnection& connection_; }; diff --git a/source/common/upstream/logical_host.cc b/source/common/upstream/logical_host.cc index 1f9b6375f7f7b..ef8620d4e6395 100644 --- a/source/common/upstream/logical_host.cc +++ b/source/common/upstream/logical_host.cc @@ -9,10 +9,11 @@ Upstream::Host::CreateConnectionData LogicalHost::createConnection( const auto current_address = address(); const std::vector& address_list = addressList(); - return {HostImpl::createConnection( - dispatcher, cluster(), current_address, address_list, transportSocketFactory(), options, - override_transport_socket_options_ != nullptr ? override_transport_socket_options_ - : transport_socket_options), + return {HostImpl::createConnection(dispatcher, cluster(), current_address, address_list, + transportSocketFactory(), options, + override_transport_socket_options_ != nullptr + ? override_transport_socket_options_ + : transport_socket_options), std::make_shared(current_address, shared_from_this())}; } diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index 7e16e3797c1c7..3b569fb3b14df 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -282,8 +282,8 @@ Network::TransportSocketFactory& HostDescriptionImpl::resolveTransportSocketFact Host::CreateConnectionData HostImpl::createConnection( Event::Dispatcher& dispatcher, const Network::ConnectionSocket::OptionsSharedPtr& options, Network::TransportSocketOptionsConstSharedPtr transport_socket_options) const { - return {createConnection(dispatcher, cluster(), address(), addressList(), transportSocketFactory(), options, - transport_socket_options), + return {createConnection(dispatcher, cluster(), address(), addressList(), + transportSocketFactory(), options, transport_socket_options), shared_from_this()}; } @@ -318,13 +318,13 @@ Host::CreateConnectionData HostImpl::createHealthCheckConnection( shared_from_this()}; } -Network::ClientConnectionPtr -HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& cluster, - const Network::Address::InstanceConstSharedPtr& address, - const std::vector& address_list, - Network::TransportSocketFactory& socket_factory, - const Network::ConnectionSocket::OptionsSharedPtr& options, - Network::TransportSocketOptionsConstSharedPtr transport_socket_options) { +Network::ClientConnectionPtr HostImpl::createConnection( + Event::Dispatcher& dispatcher, const ClusterInfo& cluster, + const Network::Address::InstanceConstSharedPtr& address, + const std::vector& address_list, + Network::TransportSocketFactory& socket_factory, + const Network::ConnectionSocket::OptionsSharedPtr& options, + Network::TransportSocketOptionsConstSharedPtr transport_socket_options) { Network::ConnectionSocket::OptionsSharedPtr connection_options; if (cluster.clusterSocketOptions() != nullptr) { if (options) { @@ -340,13 +340,14 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu } ASSERT(!address->envoyInternalAddress()); Network::ClientConnectionPtr connection = - address_list.size() > 1 ? - std::make_unique(dispatcher, address_list, cluster.sourceAddress(), socket_factory,transport_socket_options, connection_options) - : - dispatcher.createClientConnection( - address, cluster.sourceAddress(), - socket_factory.createTransportSocket(std::move(transport_socket_options)), - connection_options); + address_list.size() > 1 + ? std::make_unique( + dispatcher, address_list, cluster.sourceAddress(), socket_factory, + transport_socket_options, connection_options) + : dispatcher.createClientConnection( + address, cluster.sourceAddress(), + socket_factory.createTransportSocket(std::move(transport_socket_options)), + connection_options); connection->setBufferLimits(cluster.perConnectionBufferLimitBytes()); cluster.createNetworkFilterChain(*connection); diff --git a/source/common/upstream/upstream_impl.h b/source/common/upstream/upstream_impl.h index 5cc5ef1a2b5f2..6674082cd8d45 100644 --- a/source/common/upstream/upstream_impl.h +++ b/source/common/upstream/upstream_impl.h @@ -156,6 +156,7 @@ class HostDescriptionImpl : virtual public HostDescription, void setAddressList(const std::vector& address_list) { address_list_ = address_list; } + protected: void setAddress(Network::Address::InstanceConstSharedPtr address) { address_ = address; } diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 9d53a00c47733..6e38698166d34 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -1,12 +1,11 @@ -#include "source/common/network/happy_eyeballs_connection_impl.h" - #include "source/common/network/address_impl.h" +#include "source/common/network/happy_eyeballs_connection_impl.h" #include "source/common/network/transport_socket_options_impl.h" #include "test/mocks/event/mocks.h" -#include "test/mocks/network/transport_socket.h" #include "test/mocks/network/connection.h" #include "test/mocks/network/mocks.h" +#include "test/mocks/network/transport_socket.h" #include "test/mocks/stream_info/mocks.h" using testing::Return; @@ -17,35 +16,35 @@ namespace Envoy { namespace Network { class HappyEyeballsConnectionImplTest : public testing::Test { - public: +public: HappyEyeballsConnectionImplTest() : failover_timer_(new testing::StrictMock(&dispatcher_)), transport_socket_options_(std::make_shared()), options_(std::make_shared()), - address_list_({ - std::make_shared("127.0.0.1"), - std::make_shared("127.0.0.2"), - std::make_shared("127.0.0.3") }) { + address_list_({std::make_shared("127.0.0.1"), + std::make_shared("127.0.0.2"), + std::make_shared("127.0.0.3")}) { EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[0], _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[0], _, _, _)) + .WillOnce(testing::InvokeWithoutArgs( + this, &HappyEyeballsConnectionImplTest::createNextConnection)); - // This timer will be returned and armed as the happy eyeballs connection creates the next connection timer. - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + // This timer will be returned and armed as the happy eyeballs connection creates the next + // connection timer. + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(true)); next_connections_.push_back(std::make_unique>()); - impl_ = std::make_unique(dispatcher_, - address_list_, - Address::InstanceConstSharedPtr(), - transport_socket_factory_, - transport_socket_options_, - options_); + impl_ = std::make_unique( + dispatcher_, address_list_, Address::InstanceConstSharedPtr(), transport_socket_factory_, + transport_socket_options_, options_); } MockClientConnection* createNextConnection() { created_connections_.push_back(next_connections_.front().release()); next_connections_.pop_front(); - EXPECT_CALL(*created_connections_.back(), addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& cb) -> void { connection_callbacks_.push_back(&cb);})); + EXPECT_CALL(*created_connections_.back(), addConnectionCallbacks(_)) + .WillOnce( + Invoke([&](ConnectionCallbacks& cb) -> void { connection_callbacks_.push_back(&cb); })); return created_connections_.back(); } @@ -58,7 +57,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } - protected: +protected: Event::MockDispatcher dispatcher_; testing::StrictMock* failover_timer_; MockTransportSocketFactory transport_socket_factory_; @@ -83,17 +82,19 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); // Let the second attempt timeout to start the third and final attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); // Since there are no more address to connect to, the fallback timer will not // be rescheduled. @@ -108,17 +109,17 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { // started and the timer will be armed for the third attempe. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); } - TEST_F(HappyEyeballsConnectionImplTest, ConnectFirstSuccess) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -135,10 +136,11 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -159,10 +161,11 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -183,17 +186,19 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); // When the second attempt fails, the third and final attempt will be started. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); EXPECT_CALL(*failover_timer_, disableTimer()); // Since there are no more address to connect to, the fallback timer will not @@ -218,12 +223,13 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); // noDelay() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), noDelay(true)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -237,7 +243,7 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { impl_->noDelay(false); } -TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ +TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled) { EXPECT_CALL(*created_connections_[0], detectEarlyCloseWhenReadDisabled(true)); impl_->detectEarlyCloseWhenReadDisabled(true); @@ -247,12 +253,13 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled){ // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); // detectEarlyCloseWhenReadDisabled() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), detectEarlyCloseWhenReadDisabled(true)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -273,10 +280,11 @@ TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttempt) { // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -295,7 +303,7 @@ TEST_F(HappyEyeballsConnectionImplTest, CloseAfterAttemptComplete) { impl_->close(ConnectionCloseType::FlushWrite); } -TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ +TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter) { MockReadFilterCallbacks callbacks; ReadFilterSharedPtr filter = std::make_shared(); filter->initializeReadFilterCallbacks(callbacks); @@ -308,10 +316,11 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); // addReadFilter() should be applied to the newly created connection. @@ -329,7 +338,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter){ impl_->addReadFilter(filter2); } -TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks){ +TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks) { MockConnectionCallbacks callbacks; // The filter will be captured by the impl and not passed to the connection until it completes. impl_->addConnectionCallbacks(callbacks); @@ -340,16 +349,16 @@ TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks){ // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); // addConnectionCallbacks() should be applied to the newly created connection. - EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { - EXPECT_EQ(&c, &callbacks); - })); + EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)) + .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks); })); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -358,13 +367,12 @@ TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks){ MockConnectionCallbacks callbacks2; // Verify that addConnectionCallbacks() calls are delegated to the remaining connection. - EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { - EXPECT_EQ(&c, &callbacks2); - })); + EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)) + .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks2); })); impl_->addConnectionCallbacks(callbacks2); } -TEST_F(HappyEyeballsConnectionImplTest, RemoveConnectionCallbacks){ +TEST_F(HappyEyeballsConnectionImplTest, RemoveConnectionCallbacks) { MockConnectionCallbacks callbacks; MockConnectionCallbacks callbacks2; // The callbacks will be captured by the impl and not passed to the connection until it completes. @@ -377,17 +385,15 @@ TEST_F(HappyEyeballsConnectionImplTest, RemoveConnectionCallbacks){ impl_->removeConnectionCallbacks(callbacks); // addConnectionCallbacks() should be applied to the newly created connection. - EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { - EXPECT_EQ(&c, &callbacks2); - })); + EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_)) + .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks2); })); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); // Verify that removeConnectionCallbacks calls are delegated to the remaining connection. - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)).WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { - EXPECT_EQ(&c, &callbacks2); - })); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)) + .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks2); })); impl_->removeConnectionCallbacks(callbacks2); } @@ -403,12 +409,12 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); // The call to write() will be replayed on the underlying connection. - EXPECT_CALL(*created_connections_[0], write(_, _)).WillOnce( - Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_CALL(*created_connections_[0], write(_, _)) + .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { EXPECT_EQ("hello world", data.toString()); EXPECT_FALSE(end_stream); - ;}) - ); + ; + })); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } @@ -422,12 +428,13 @@ TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); // setBufferLimits() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), setBufferLimits(42)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -456,12 +463,12 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); // The call to write() will be replayed on the underlying connection. - EXPECT_CALL(*created_connections_[0], write(_, _)).WillOnce( - Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_CALL(*created_connections_[0], write(_, _)) + .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { EXPECT_EQ("hello world", data.toString()); EXPECT_FALSE(end_stream); - ;}) - ); + ; + })); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } @@ -497,7 +504,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AboveHighWatermark) { EXPECT_TRUE(impl_->aboveHighWatermark()); } -TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ +TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats) { StrictMock rx_total; StrictMock rx_current; StrictMock tx_total; @@ -505,10 +512,10 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ StrictMock bind_errors; StrictMock delayed_close_timeouts; - Connection::ConnectionStats cs = { rx_total, rx_current, tx_total, tx_current, &bind_errors, &delayed_close_timeouts}; - EXPECT_CALL(*created_connections_[0], setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { - EXPECT_EQ(&s, &cs); - })); + Connection::ConnectionStats cs = {rx_total, rx_current, tx_total, + tx_current, &bind_errors, &delayed_close_timeouts}; + EXPECT_CALL(*created_connections_[0], setConnectionStats(_)) + .WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs); })); impl_->setConnectionStats(cs); EXPECT_CALL(*created_connections_[0], connect()); @@ -517,14 +524,14 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)).WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); // setConnectionStats() should be applied to the newly created connection. - EXPECT_CALL(*next_connections_.back(), setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { - EXPECT_EQ(&s, &cs); - })); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)).Times(1); + EXPECT_CALL(*next_connections_.back(), setConnectionStats(_)) + .WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs); })); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -534,10 +541,10 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats){ connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); // Verify that setConnectionStats calls are delegated to the remaining connection. - Connection::ConnectionStats cs2 = { rx_total, rx_current, tx_total, tx_current, &bind_errors, &delayed_close_timeouts}; - EXPECT_CALL(*created_connections_[1], setConnectionStats(_)).WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { - EXPECT_EQ(&s, &cs2); - })); + Connection::ConnectionStats cs2 = {rx_total, rx_current, tx_total, + tx_current, &bind_errors, &delayed_close_timeouts}; + EXPECT_CALL(*created_connections_[1], setConnectionStats(_)) + .WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs2); })); impl_->setConnectionStats(cs2); } @@ -643,7 +650,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddressProvider) { connectFirstAttempt(); const SocketAddressSetterImpl provider(std::make_shared(80), - std::make_shared(80)); + std::make_shared(80)); EXPECT_CALL(*created_connections_[0], addressProvider()).WillOnce(ReturnRef(provider)); impl_->addressProvider(); } @@ -651,8 +658,9 @@ TEST_F(HappyEyeballsConnectionImplTest, AddressProvider) { TEST_F(HappyEyeballsConnectionImplTest, AddressProviderSharedPtr) { connectFirstAttempt(); - SocketAddressProviderSharedPtr provider = std::make_shared(std::make_shared("127.0.0.2"), - std::make_shared("127.0.0.1")); + SocketAddressProviderSharedPtr provider = std::make_shared( + std::make_shared("127.0.0.2"), + std::make_shared("127.0.0.1")); EXPECT_CALL(*created_connections_[0], addressProviderSharedPtr()).WillOnce(Return(provider)); EXPECT_EQ(provider, impl_->addressProviderSharedPtr()); } @@ -660,7 +668,8 @@ TEST_F(HappyEyeballsConnectionImplTest, AddressProviderSharedPtr) { TEST_F(HappyEyeballsConnectionImplTest, UnixSocketPeerCredentials) { connectFirstAttempt(); - EXPECT_CALL(*created_connections_[0], unixSocketPeerCredentials()).WillOnce(Return(absl::optional())); + EXPECT_CALL(*created_connections_[0], unixSocketPeerCredentials()) + .WillOnce(Return(absl::optional())); EXPECT_FALSE(impl_->unixSocketPeerCredentials().has_value()); } @@ -676,7 +685,8 @@ TEST_F(HappyEyeballsConnectionImplTest, SocketOptions) { connectFirstAttempt(); ConnectionSocket::OptionsSharedPtr options = nullptr; - EXPECT_CALL(*created_connections_[0], socketOptions()).WillOnce(ReturnRef(options));; + EXPECT_CALL(*created_connections_[0], socketOptions()).WillOnce(ReturnRef(options)); + ; EXPECT_EQ(options, impl_->socketOptions()); } diff --git a/test/common/upstream/upstream_impl_test.cc b/test/common/upstream/upstream_impl_test.cc index f2a151c3ec38b..e44f29d4c7dd3 100644 --- a/test/common/upstream/upstream_impl_test.cc +++ b/test/common/upstream/upstream_impl_test.cc @@ -1317,11 +1317,13 @@ TEST_F(HostImplTest, CreateConnection) { locality.set_region("oceania"); locality.set_zone("hello"); locality.set_sub_zone("world"); - Network::Address::InstanceConstSharedPtr address = Network::Utility::resolveUrl("tcp://10.0.0.1:1234"); - auto host = std::make_shared(cluster.info_, "lyft.com", address, - std::make_shared(metadata), 1, locality, - envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 1, - envoy::config::core::v3::UNKNOWN, simTime()); + Network::Address::InstanceConstSharedPtr address = + Network::Utility::resolveUrl("tcp://10.0.0.1:1234"); + auto host = std::make_shared( + cluster.info_, "lyft.com", address, + std::make_shared(metadata), 1, locality, + envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 1, + envoy::config::core::v3::UNKNOWN, simTime()); testing::StrictMock dispatcher; Network::TransportSocketOptionsConstSharedPtr transport_socket_options; @@ -1332,7 +1334,8 @@ TEST_F(HostImplTest, CreateConnection) { EXPECT_CALL(*connection, setBufferLimits(0)); EXPECT_CALL(dispatcher, createClientConnection_(_, _, _, _)).WillOnce(Return(connection)); - Envoy::Upstream::Host::CreateConnectionData connection_data = host->createConnection(dispatcher, options, transport_socket_options); + Envoy::Upstream::Host::CreateConnectionData connection_data = + host->createConnection(dispatcher, options, transport_socket_options); EXPECT_EQ(connection, connection_data.connection_.get()); } @@ -1346,11 +1349,13 @@ TEST_F(HostImplTest, CreateConnectionHappyEyeballs) { locality.set_region("oceania"); locality.set_zone("hello"); locality.set_sub_zone("world"); - Network::Address::InstanceConstSharedPtr address = Network::Utility::resolveUrl("tcp://10.0.0.1:1234"); - auto host = std::make_shared(cluster.info_, "lyft.com", address, - std::make_shared(metadata), 1, locality, - envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 1, - envoy::config::core::v3::UNKNOWN, simTime()); + Network::Address::InstanceConstSharedPtr address = + Network::Utility::resolveUrl("tcp://10.0.0.1:1234"); + auto host = std::make_shared( + cluster.info_, "lyft.com", address, + std::make_shared(metadata), 1, locality, + envoy::config::endpoint::v3::Endpoint::HealthCheckConfig::default_instance(), 1, + envoy::config::core::v3::UNKNOWN, simTime()); testing::StrictMock dispatcher; Network::TransportSocketOptionsConstSharedPtr transport_socket_options; @@ -1358,18 +1363,20 @@ TEST_F(HostImplTest, CreateConnectionHappyEyeballs) { Network::MockTransportSocketFactory socket_factory; std::vector address_list = { - Network::Utility::resolveUrl("tcp://10.0.0.1:1235"), - address, + Network::Utility::resolveUrl("tcp://10.0.0.1:1235"), + address, }; host->setAddressList(address_list); auto connection = new testing::StrictMock(); EXPECT_CALL(*connection, setBufferLimits(0)); EXPECT_CALL(*connection, addConnectionCallbacks(_)); // The underlying connection should be created with the first address in the list. - EXPECT_CALL(dispatcher, createClientConnection_(address_list[0], _, _, _)).WillOnce(Return(connection)); + EXPECT_CALL(dispatcher, createClientConnection_(address_list[0], _, _, _)) + .WillOnce(Return(connection)); EXPECT_CALL(dispatcher, createTimer_(_)); - Envoy::Upstream::Host::CreateConnectionData connection_data = host->createConnection(dispatcher, options, transport_socket_options); + Envoy::Upstream::Host::CreateConnectionData connection_data = + host->createConnection(dispatcher, options, transport_socket_options); // The created connection will be wrapped in a HappyEyeballsConnectionImpl. EXPECT_NE(connection, connection_data.connection_.get()); } From 9a5acf336813c2b966d1da62fdc553ce8a49fecf Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 15 Jul 2021 19:36:45 +0000 Subject: [PATCH 20/49] tidy Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index e34c05bccc7c1..e4dfafad73bd8 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -379,7 +379,7 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, } } if (event == ConnectionEvent::Connected) { - for (auto filter : post_connect_state_.read_filters_) { + for (auto& filter : post_connect_state_.read_filters_) { connections_[0]->addReadFilter(filter); } } From afe4c6a5008f154e9c62b19e8e18b0b318c38618 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 16 Jul 2021 02:06:19 +0000 Subject: [PATCH 21/49] Comments from Renjie Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 2 +- source/common/network/happy_eyeballs_connection_impl.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index e4dfafad73bd8..1dcaa8396b3a0 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -303,7 +303,7 @@ void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) connections_[0]->dumpState(os, indent_level); } -std::unique_ptr HappyEyeballsConnectionImpl::createNextConnection() { +ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { ASSERT(next_address_ < address_list_.size()); auto connection = dispatcher_.createClientConnection( address_list_[next_address_++], source_address_, diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 886882a820732..439a216550661 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -104,7 +104,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { ClientConnection& connection_; }; - std::unique_ptr createNextConnection(); + ClientConnectionPtr createNextConnection(); void tryAnotherConnection(); void maybeScheduleNextAttempt(); @@ -144,7 +144,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { const ConnectionSocket::OptionsSharedPtr options_; // Set of active connections. - std::vector> connections_; + std::vector connections_; std::vector> callbacks_wrappers_; // True when connect() has finished, either success or failure. From 935ce6b5c1b89952be9bcf0f383d03e75b80d7eb Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 16 Jul 2021 17:14:12 +0000 Subject: [PATCH 22/49] OptRef Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 4 ++-- source/common/network/happy_eyeballs_connection_impl.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 1dcaa8396b3a0..62414ef9b1310 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -286,7 +286,7 @@ void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stats) { if (!connect_finished_) { - per_connection_state_.connection_stats_ = &stats; + per_connection_state_.connection_stats_ = stats; } for (auto& connection : connections_) { connection->setConnectionStats(stats); @@ -319,7 +319,7 @@ ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { connection->noDelay(per_connection_state_.no_delay_.value()); } if (per_connection_state_.connection_stats_.has_value()) { - connection->setConnectionStats(*per_connection_state_.connection_stats_.value()); + connection->setConnectionStats(*per_connection_state_.connection_stats_); } if (per_connection_state_.buffer_limits_.has_value()) { connection->setBufferLimits(per_connection_state_.buffer_limits_.value()); diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 439a216550661..ffd1679b81744 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -122,7 +122,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { struct PerConnectionState { absl::optional detect_early_close_when_read_disabled_; absl::optional no_delay_; - absl::optional connection_stats_; + OptRef connection_stats_; absl::optional buffer_limits_; }; From 12cf1104ded53c85402a7f0af82b6f56ae2b9098 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 16 Jul 2021 17:35:44 +0000 Subject: [PATCH 23/49] Spelling Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.h | 2 +- test/common/network/happy_eyeballs_connection_impl_test.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index ffd1679b81744..6df21c56ff06a 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -118,7 +118,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper); - // State which needs to be applie to every connection attempt. + // State which needs to be applied to every connection attempt. struct PerConnectionState { absl::optional detect_early_close_when_read_disabled_; absl::optional no_delay_; diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 6e38698166d34..64d66e29e2c53 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -106,7 +106,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { impl_->connect(); // When the first connection attempt fails, the next attempt will be immediately - // started and the timer will be armed for the third attempe. + // started and the timer will be armed for the third attempt. next_connections_.push_back(std::make_unique>()); EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) @@ -579,7 +579,7 @@ TEST_F(HappyEyeballsConnectionImplTest, Connecting) { } // Tests for HappyEyeballsConnectionImpl methods which must only be called after connect() -// has finised. +// has finished. TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilter) { connectFirstAttempt(); From 7f84039fd7850de79705c1d08ebaff553cbc9864 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Mon, 19 Jul 2021 20:38:22 +0000 Subject: [PATCH 24/49] Comments from alyssa Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 51 ++++++++++--------- .../network/happy_eyeballs_connection_impl.h | 7 ++- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 62414ef9b1310..9bbc045162c72 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -20,18 +20,18 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( HappyEyeballsConnectionImpl::~HappyEyeballsConnectionImpl() = default; void HappyEyeballsConnectionImpl::connect() { - ASSERT(!connect_finished_); + ENVOY_BUG(!connect_finished_, "connection already connected"); connections_[0]->connect(); maybeScheduleNextAttempt(); } void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->addWriteFilter(filter); } void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->addFilter(filter); } @@ -44,32 +44,32 @@ void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { } void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->removeReadFilter(filter); } bool HappyEyeballsConnectionImpl::initializeReadFilters() { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->initializeReadFilters(); } void HappyEyeballsConnectionImpl::addBytesSentCallback(Connection::BytesSentCb cb) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->addBytesSentCallback(cb); } void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->enableHalfClose(enabled); } bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->isHalfCloseEnabled(); } std::string HappyEyeballsConnectionImpl::nextProtocol() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->nextProtocol(); } @@ -83,7 +83,7 @@ void HappyEyeballsConnectionImpl::noDelay(bool enable) { } void HappyEyeballsConnectionImpl::readDisable(bool disable) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->readDisable(disable); } @@ -104,23 +104,23 @@ bool HappyEyeballsConnectionImpl::readEnabled() const { } const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->addressProvider(); } SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->addressProviderSharedPtr(); } absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->unixSocketPeerCredentials(); } Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->ssl(); } @@ -186,37 +186,37 @@ bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { } const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->socketOptions(); } absl::string_view HappyEyeballsConnectionImpl::requestedServerName() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->requestedServerName(); } StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->streamInfo(); } const StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->streamInfo(); } absl::string_view HappyEyeballsConnectionImpl::transportFailureReason() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->transportFailureReason(); } bool HappyEyeballsConnectionImpl::startSecureTransport() { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->startSecureTransport(); } absl::optional HappyEyeballsConnectionImpl::lastRoundTripTime() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->lastRoundTripTime(); } @@ -242,6 +242,7 @@ void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& } ASSERT(false); } + void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { if (connect_finished_) { connections_[0]->close(type); @@ -275,12 +276,12 @@ Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { } uint64_t HappyEyeballsConnectionImpl::id() const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); return connections_[0]->id(); } void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->hashKey(hash); } @@ -294,12 +295,12 @@ void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stat } void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::milliseconds timeout) { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->setDelayedCloseTimeout(timeout); } void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) const { - ASSERT(connect_finished_); + ENVOY_BUG(connect_finished_, "connect() has not finished"); connections_[0]->dumpState(os, indent_level); } diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 6df21c56ff06a..b68f64d8d6e15 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -18,8 +18,11 @@ namespace Envoy { namespace Network { /** - * Implementation of Network::Connection, Network::FilterManagerConnection and - * Envoy::ScopeTrackedObject. + * Implementation of ClientConnection which transparently attempts connections to + * multiple different IP addresses, and use the first connection that succeeds. + * See the Happy Eyeballs RFC at https://datatracker.ietf.org/doc/html/rfc6555 + * TODO(RyanTheOptimist): Implement the Happy Eyeballs address sorting algorithm + * either in the class or in the resolution code. */ class HappyEyeballsConnectionImpl : public ClientConnection { public: From 62f5d5085ce21e7ed4ec481419f79674f8742433 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 20 Jul 2021 21:34:08 +0000 Subject: [PATCH 25/49] All methods now valid before connect finished Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 160 +++++++++++++----- .../network/happy_eyeballs_connection_impl.h | 13 +- 2 files changed, 131 insertions(+), 42 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 9bbc045162c72..96a79d89027ed 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -10,7 +10,8 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( Address::InstanceConstSharedPtr source_address, TransportSocketFactory& socket_factory, TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) - : dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), + // XXX: get a real id. + : id_(42), dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), socket_factory_(socket_factory), transport_socket_options_(transport_socket_options), options_(options), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { @@ -26,13 +27,19 @@ void HappyEyeballsConnectionImpl::connect() { } void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->addWriteFilter(filter); + if (connect_finished_) { + connections_[0]->addWriteFilter(filter); + return; + } + post_connect_state_.write_filters_.push_back(filter); } void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->addFilter(filter); + if (connect_finished_) { + connections_[0]->addFilter(filter); + return; + } + post_connect_state_.filters_.push_back(filter); } void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { @@ -44,33 +51,61 @@ void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { } void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->removeReadFilter(filter); + if (connect_finished_) { + connections_[0]->removeReadFilter(filter); + return; + } + auto i = post_connect_state_.read_filters_.begin(); + while (i != post_connect_state_.read_filters_.end()) { + if (*i == filter) { + post_connect_state_.read_filters_.erase(i); + return; + } + } + ASSERT(false); } bool HappyEyeballsConnectionImpl::initializeReadFilters() { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - return connections_[0]->initializeReadFilters(); + if (connect_finished_) { + return connections_[0]->initializeReadFilters(); + } + if (!post_connect_state_.read_filters_.empty()) { + return false; + } + post_connect_state_.initialize_read_filters_.value() = true; + return true; + } void HappyEyeballsConnectionImpl::addBytesSentCallback(Connection::BytesSentCb cb) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->addBytesSentCallback(cb); + if (connect_finished_) { + connections_[0]->addBytesSentCallback(cb); + return; + } + post_connect_state_.bytes_sent_callbacks_.push_back(cb); } void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->enableHalfClose(enabled); + if (!connect_finished_) { + per_connection_state_.enable_half_close_ = enabled; + } + for (auto& connection : connections_) { + connection->enableHalfClose(enabled); + } } bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - return connections_[0]->isHalfCloseEnabled(); + if (connect_finished_) { + return connections_[0]->isHalfCloseEnabled(); + } + return per_connection_state_.enable_half_close_.has_value() && per_connection_state_.enable_half_close_.value(); } std::string HappyEyeballsConnectionImpl::nextProtocol() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - return connections_[0]->nextProtocol(); + if (connect_finished_) { + return connections_[0]->nextProtocol(); + } + return ""; } void HappyEyeballsConnectionImpl::noDelay(bool enable) { @@ -83,8 +118,13 @@ void HappyEyeballsConnectionImpl::noDelay(bool enable) { } void HappyEyeballsConnectionImpl::readDisable(bool disable) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->readDisable(disable); + if (connect_finished_) { + connections_[0]->readDisable(disable); + return; + } + // if (!per_connection_state_.read_disable_count_.has_value()) { + // } + per_connection_state_.read_disable_count_.value()++; } void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { @@ -98,29 +138,31 @@ void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { bool HappyEyeballsConnectionImpl::readEnabled() const { if (!connect_finished_) { - ASSERT(connections_[0]->readEnabled()); + return !per_connection_state_.read_disable_count_.has_value() || + per_connection_state_.read_disable_count_ == 0; + } return connections_[0]->readEnabled(); } const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->addressProvider(); } SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->addressProviderSharedPtr(); } absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->unixSocketPeerCredentials(); } Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->ssl(); } @@ -186,37 +228,43 @@ bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { } const ConnectionSocket::OptionsSharedPtr& HappyEyeballsConnectionImpl::socketOptions() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->socketOptions(); } absl::string_view HappyEyeballsConnectionImpl::requestedServerName() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->requestedServerName(); } StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->streamInfo(); } const StreamInfo::StreamInfo& HappyEyeballsConnectionImpl::streamInfo() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->streamInfo(); } absl::string_view HappyEyeballsConnectionImpl::transportFailureReason() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->transportFailureReason(); } bool HappyEyeballsConnectionImpl::startSecureTransport() { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - return connections_[0]->startSecureTransport(); + if (!connect_finished_) { + per_connection_state_.start_secure_transport_ = true; + } + bool ret = false; + for (auto& connection : connections_) { + ret = connection->startSecureTransport(); + } + return ret; } absl::optional HappyEyeballsConnectionImpl::lastRoundTripTime() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); + // Note, this might change before connect finishes. return connections_[0]->lastRoundTripTime(); } @@ -276,13 +324,15 @@ Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { } uint64_t HappyEyeballsConnectionImpl::id() const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - return connections_[0]->id(); + return id_; } -void HappyEyeballsConnectionImpl::hashKey(std::vector& hash) const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->hashKey(hash); +void HappyEyeballsConnectionImpl::hashKey(std::vector& hash_key) const { + // Pack the id into sizeof(id_) uint8_t entries in the hash_key vector. + hash_key.reserve(hash_key.size() + sizeof(id_)); + for (unsigned i = 0; i < sizeof(id_); ++i) { + hash_key.push_back(0xFF & (id_ >> (8 * i))); + } } void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stats) { @@ -295,13 +345,21 @@ void HappyEyeballsConnectionImpl::setConnectionStats(const ConnectionStats& stat } void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::milliseconds timeout) { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->setDelayedCloseTimeout(timeout); + if (!connect_finished_) { + per_connection_state_.delayed_close_timeout_ = timeout; + } + for (auto& connection : connections_) { + connection->setDelayedCloseTimeout(timeout); + } } void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) const { - ENVOY_BUG(connect_finished_, "connect() has not finished"); - connections_[0]->dumpState(os, indent_level); + const char* spaces = spacesForLevel(indent_level); + os << spaces << "HappyEyeballsConnectionImpl " << this << DUMP_MEMBER(id_) << DUMP_MEMBER(connect_finished_) << "\n"; + + for (auto& connection : connections_) { + DUMP_DETAILS(connection); + } } ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { @@ -325,6 +383,13 @@ ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { if (per_connection_state_.buffer_limits_.has_value()) { connection->setBufferLimits(per_connection_state_.buffer_limits_.value()); } + if (per_connection_state_.enable_half_close_.has_value()) { + connection->enableHalfClose(per_connection_state_.enable_half_close_.value()); + } + if (per_connection_state_.delayed_close_timeout_.has_value()) { + connection->setDelayedCloseTimeout(per_connection_state_.delayed_close_timeout_.value()); + } + return connection; } @@ -379,10 +444,23 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, connections_[0]->addConnectionCallbacks(*cb); } } + + for (auto cb : post_connect_state_.bytes_sent_callbacks_) { + connections_[0]->addBytesSentCallback(cb); + } + if (event == ConnectionEvent::Connected) { for (auto& filter : post_connect_state_.read_filters_) { connections_[0]->addReadFilter(filter); } + if (post_connect_state_.initialize_read_filters_.has_value() && + post_connect_state_.initialize_read_filters_.value()) { + bool initialized = connections_[0]->initializeReadFilters(); + ASSERT(initialized); + } + for (int i = 0; i < per_connection_state_.read_disable_count_.value(); ++i) { + connections_[0]->readDisable(true); + } } if (post_connect_state_.write_buffer_.has_value()) { diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index b68f64d8d6e15..95e4ee7404948 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -76,7 +76,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void close(ConnectionCloseType type) override; Event::Dispatcher& dispatcher() override; uint64_t id() const override; - void hashKey(std::vector& hash) const override; + void hashKey(std::vector& hash_key) const override; void setConnectionStats(const ConnectionStats& stats) override; void setDelayedCloseTimeout(std::chrono::milliseconds timeout) override; @@ -125,19 +125,30 @@ class HappyEyeballsConnectionImpl : public ClientConnection { struct PerConnectionState { absl::optional detect_early_close_when_read_disabled_; absl::optional no_delay_; + absl::optional enable_half_close_; OptRef connection_stats_; absl::optional buffer_limits_; + absl::optional read_disable_count_; + absl::optional start_secure_transport_; + absl::optional delayed_close_timeout_; }; // State which needs to be saved and applied only to the final connection // attempt. struct PostConnectState { std::vector connection_callbacks_; + std::vector bytes_sent_callbacks_; std::vector read_filters_; + std::vector write_filters_; + std::vector filters_; absl::optional write_buffer_; absl::optional end_stream_; + absl::optional initialize_read_filters_; }; + // ID for this connection which is distinct from the ID of the underlying connections. + const uint64_t id_; + Event::Dispatcher& dispatcher_; const std::vector& address_list_; size_t next_address_ = 0; From 3c988a9239064b789de78a451c9f7f2f7f28c682 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 20 Jul 2021 22:36:13 +0000 Subject: [PATCH 26/49] Tests for all methods Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 45 ++- .../network/happy_eyeballs_connection_impl.h | 2 +- .../happy_eyeballs_connection_impl_test.cc | 257 ++++++++++++++++-- 3 files changed, 271 insertions(+), 33 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 96a79d89027ed..6cb656dcdcad9 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -122,9 +122,16 @@ void HappyEyeballsConnectionImpl::readDisable(bool disable) { connections_[0]->readDisable(disable); return; } - // if (!per_connection_state_.read_disable_count_.has_value()) { - // } - per_connection_state_.read_disable_count_.value()++; + if (!post_connect_state_.read_disable_count_.has_value()) { + post_connect_state_.read_disable_count_ = 0; + } + + if (disable) { + post_connect_state_.read_disable_count_.value()++; + } else { + ASSERT(post_connect_state_.read_disable_count_ != 0); + post_connect_state_.read_disable_count_.value()--; + } } void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { @@ -138,8 +145,8 @@ void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { bool HappyEyeballsConnectionImpl::readEnabled() const { if (!connect_finished_) { - return !per_connection_state_.read_disable_count_.has_value() || - per_connection_state_.read_disable_count_ == 0; + return !post_connect_state_.read_disable_count_.has_value() || + post_connect_state_.read_disable_count_ == 0; } return connections_[0]->readEnabled(); @@ -389,6 +396,10 @@ ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { if (per_connection_state_.delayed_close_timeout_.has_value()) { connection->setDelayedCloseTimeout(per_connection_state_.delayed_close_timeout_.value()); } + if (per_connection_state_.start_secure_transport_.has_value()) { + ASSERT(per_connection_state_.start_secure_transport_); + connection->startSecureTransport(); + } return connection; } @@ -450,6 +461,12 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, } if (event == ConnectionEvent::Connected) { + for (auto& filter : post_connect_state_.filters_) { + connections_[0]->addFilter(filter); + } + for (auto& filter : post_connect_state_.write_filters_) { + connections_[0]->addWriteFilter(filter); + } for (auto& filter : post_connect_state_.read_filters_) { connections_[0]->addReadFilter(filter); } @@ -458,16 +475,18 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, bool initialized = connections_[0]->initializeReadFilters(); ASSERT(initialized); } - for (int i = 0; i < per_connection_state_.read_disable_count_.value(); ++i) { - connections_[0]->readDisable(true); + if (post_connect_state_.read_disable_count_.has_value()) { + for (int i = 0; i < post_connect_state_.read_disable_count_.value(); ++i) { + connections_[0]->readDisable(true); + } } - } - if (post_connect_state_.write_buffer_.has_value()) { - // write_buffer_ and end_stream_ are both set together in write(). - ASSERT(post_connect_state_.end_stream_.has_value()); - connections_[0]->write(*post_connect_state_.write_buffer_.value(), - post_connect_state_.end_stream_.value()); + if (post_connect_state_.write_buffer_.has_value()) { + // write_buffer_ and end_stream_ are both set together in write(). + ASSERT(post_connect_state_.end_stream_.has_value()); + connections_[0]->write(*post_connect_state_.write_buffer_.value(), + post_connect_state_.end_stream_.value()); + } } std::vector cbs; diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 95e4ee7404948..039d6cb46391f 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -128,7 +128,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { absl::optional enable_half_close_; OptRef connection_stats_; absl::optional buffer_limits_; - absl::optional read_disable_count_; absl::optional start_secure_transport_; absl::optional delayed_close_timeout_; }; @@ -142,6 +141,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { std::vector write_filters_; std::vector filters_; absl::optional write_buffer_; + absl::optional read_disable_count_; absl::optional end_stream_; absl::optional initialize_read_filters_; }; diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 64d66e29e2c53..fe09489984bf1 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -578,10 +578,42 @@ TEST_F(HappyEyeballsConnectionImplTest, Connecting) { EXPECT_FALSE(impl_->connecting()); } -// Tests for HappyEyeballsConnectionImpl methods which must only be called after connect() -// has finished. - TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilter) { + MockWriteFilterCallbacks callbacks; + WriteFilterSharedPtr filter = std::make_shared(); + filter->initializeWriteFilterCallbacks(callbacks); + // The filter will be captured by the impl and not passed to the connection until it completes. + impl_->addWriteFilter(filter); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + + // addWriteFilter() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], addWriteFilter(filter)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + WriteFilterSharedPtr filter2 = std::make_shared(); + filter2->initializeWriteFilterCallbacks(callbacks); + // Verify that addWriteFilter() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], addWriteFilter(filter2)); + impl_->addWriteFilter(filter2); +} + +TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilterAfterConnect) { connectFirstAttempt(); MockWriteFilterCallbacks callbacks; @@ -592,6 +624,44 @@ TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilter) { } TEST_F(HappyEyeballsConnectionImplTest, AddFilter) { + MockReadFilterCallbacks read_callbacks; + MockWriteFilterCallbacks write_callbacks; + FilterSharedPtr filter = std::make_shared(); + filter->initializeReadFilterCallbacks(read_callbacks); + filter->initializeWriteFilterCallbacks(write_callbacks); + // The filter will be captured by the impl and not passed to the connection until it completes. + impl_->addFilter(filter); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + + // addFilter() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], addFilter(filter)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + FilterSharedPtr filter2 = std::make_shared(); + filter2->initializeReadFilterCallbacks(read_callbacks); + filter2->initializeWriteFilterCallbacks(write_callbacks); + // Verify that addFilter() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], addFilter(filter2)); + impl_->addFilter(filter2); +} + +TEST_F(HappyEyeballsConnectionImplTest, AddFilterAfterConnect) { connectFirstAttempt(); MockReadFilterCallbacks read_callbacks; @@ -604,14 +674,76 @@ TEST_F(HappyEyeballsConnectionImplTest, AddFilter) { } TEST_F(HappyEyeballsConnectionImplTest, AddBytesSentCallback) { + Connection::BytesSentCb callback = [](uint64_t) { return true; }; + // The filter will be captured by the impl and not passed to the connection until it completes. + impl_->addBytesSentCallback(callback); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + + // addBytesSentCallback() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], addBytesSentCallback(_)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + Connection::BytesSentCb callback2 = [](uint64_t) { return true; }; + // Verify that addBytesSentCallback() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], addBytesSentCallback(_)); + impl_->addBytesSentCallback(callback2); +} + +TEST_F(HappyEyeballsConnectionImplTest, AddBytesSentCallbackAfterConnect) { connectFirstAttempt(); - std::function cb = [](uint64_t) { return true; }; + Connection::BytesSentCb cb = [](uint64_t) { return true; }; EXPECT_CALL(*created_connections_[0], addBytesSentCallback(_)); impl_->addBytesSentCallback(cb); } TEST_F(HappyEyeballsConnectionImplTest, EnableHalfClose) { + EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); + impl_->enableHalfClose(true); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + // enableHalfClose() should be applied to the newly created connection. + EXPECT_CALL(*next_connections_.back(), enableHalfClose(true)); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that enableHalfClose calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], enableHalfClose(false)); + impl_->enableHalfClose(false); +} + +TEST_F(HappyEyeballsConnectionImplTest, EnableHalfCloseAfterConnect) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); @@ -619,33 +751,128 @@ TEST_F(HappyEyeballsConnectionImplTest, EnableHalfClose) { } TEST_F(HappyEyeballsConnectionImplTest, IsHalfCloseEnabled) { - connectFirstAttempt(); + EXPECT_FALSE(impl_->isHalfCloseEnabled()); - EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(true)); + EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); + impl_->enableHalfClose(true); EXPECT_TRUE(impl_->isHalfCloseEnabled()); + + connectFirstAttempt(); } -TEST_F(HappyEyeballsConnectionImplTest, NextProtocol) { +TEST_F(HappyEyeballsConnectionImplTest, IsHalfCloseEnabledAfterConnect) { connectFirstAttempt(); - EXPECT_CALL(*created_connections_[0], nextProtocol()).WillOnce(Return("h3")); - EXPECT_EQ("h3", impl_->nextProtocol()); + EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(true)); + EXPECT_TRUE(impl_->isHalfCloseEnabled()); } TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { - connectFirstAttempt(); + // The disables will be captured by the impl and not passed to the connection until it completes. + impl_->readDisable(true); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); - EXPECT_CALL(*created_connections_[0], readDisable(true)); + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + + // The disables will be captured by the impl and not passed to the connection until it completes. + impl_->readDisable(true); impl_->readDisable(true); + // Read disable count should now be 2. + impl_->readDisable(false); + + + // readDisable() should be applied to the newly created connection. + EXPECT_CALL(*created_connections_[1], readDisable(true)).Times(2); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that addBytesSentCallback() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], readDisable(false)); + impl_->readDisable(false); } TEST_F(HappyEyeballsConnectionImplTest, ReadEnabled) { + EXPECT_TRUE(impl_->readEnabled()); + impl_->readDisable(true); // Disable count 1. + EXPECT_FALSE(impl_->readEnabled()); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + impl_->readDisable(true); // Disable count 2 + EXPECT_FALSE(impl_->readEnabled()); + impl_->readDisable(false); // Disable count 1 + EXPECT_FALSE(impl_->readEnabled()); + impl_->readDisable(false); // Disable count 0 + EXPECT_TRUE(impl_->readEnabled()); +} + +TEST_F(HappyEyeballsConnectionImplTest, ReadEnabledAfterConnect) { connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], readEnabled()).WillOnce(Return(true)); EXPECT_TRUE(impl_->readEnabled()); } +TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransport) { + EXPECT_CALL(*created_connections_[0], startSecureTransport()).WillOnce(Return(true)); + EXPECT_TRUE(impl_->startSecureTransport()); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + // Let the first attempt timeout to start the second attempt. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + // startSecureTransport() should be applied to the newly created connection. + EXPECT_CALL(*next_connections_.back(), startSecureTransport()).WillOnce(Return(true)); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + + // Verify that startSecureTransport calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], startSecureTransport()).WillOnce(Return(false)); + EXPECT_FALSE(impl_->startSecureTransport()); +} + +TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransportAfterConnect) { + connectFirstAttempt(); + + EXPECT_CALL(*created_connections_[0], startSecureTransport()); + impl_->startSecureTransport(); +} + +// Tests for HappyEyeballsConnectionImpl methods which simply delegate to the first connection. + +TEST_F(HappyEyeballsConnectionImplTest, NextProtocol) { + connectFirstAttempt(); + + EXPECT_CALL(*created_connections_[0], nextProtocol()).WillOnce(Return("h3")); + EXPECT_EQ("h3", impl_->nextProtocol()); +} + TEST_F(HappyEyeballsConnectionImplTest, AddressProvider) { connectFirstAttempt(); @@ -686,7 +913,6 @@ TEST_F(HappyEyeballsConnectionImplTest, SocketOptions) { ConnectionSocket::OptionsSharedPtr options = nullptr; EXPECT_CALL(*created_connections_[0], socketOptions()).WillOnce(ReturnRef(options)); - ; EXPECT_EQ(options, impl_->socketOptions()); } @@ -719,13 +945,6 @@ TEST_F(HappyEyeballsConnectionImplTest, TransportFailureReason) { EXPECT_EQ("reason", impl_->transportFailureReason()); } -TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransport) { - connectFirstAttempt(); - - EXPECT_CALL(*created_connections_[0], startSecureTransport()); - impl_->startSecureTransport(); -} - TEST_F(HappyEyeballsConnectionImplTest, LastRoundTripTime) { connectFirstAttempt(); From 9a656e57eeb114c0e44d9ee9e484878528124292 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 20 Jul 2021 22:51:44 +0000 Subject: [PATCH 27/49] id Signed-off-by: Ryan Hamilton --- source/common/network/connection_impl.h | 3 +++ .../common/network/happy_eyeballs_connection_impl.cc | 3 +-- .../network/happy_eyeballs_connection_impl_test.cc | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index 4e9f0044924c1..2cbd351674f93 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -22,6 +22,8 @@ class TestPauseFilter; namespace Network { +class HappyEyeballsConnectionImpl; + /** * Utility functions for the connection implementation. */ @@ -168,6 +170,7 @@ class ConnectionImpl : public ConnectionImplBase, public TransportSocketCallback bool bind_error_{false}; private: + friend class HappyEyeballsConnectionImpl; friend class Envoy::RandomPauseFilter; friend class Envoy::TestPauseFilter; diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 6cb656dcdcad9..85087c242d494 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -10,8 +10,7 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( Address::InstanceConstSharedPtr source_address, TransportSocketFactory& socket_factory, TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) - // XXX: get a real id. - : id_(42), dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), + : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), socket_factory_(socket_factory), transport_socket_options_(transport_socket_options), options_(options), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index fe09489984bf1..0fd4b87b21772 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -213,6 +213,16 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); } +TEST_F(HappyEyeballsConnectionImplTest, Id) { + uint64_t id = ConnectionImpl::nextGlobalIdForTest() - 1; + EXPECT_EQ(id, impl_->id()); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_EQ(id, impl_->id()); +} + TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { EXPECT_CALL(*created_connections_[0], noDelay(true)); impl_->noDelay(true); From 0a943163a209ec6228b69d6dc69b9f866cc9d6b9 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 20 Jul 2021 23:00:23 +0000 Subject: [PATCH 28/49] format Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 22 +++++++++---------- .../happy_eyeballs_connection_impl_test.cc | 9 ++++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 85087c242d494..0b3267cc060d5 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -10,9 +10,9 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( Address::InstanceConstSharedPtr source_address, TransportSocketFactory& socket_factory, TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) - : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), address_list_(address_list), source_address_(source_address), - socket_factory_(socket_factory), transport_socket_options_(transport_socket_options), - options_(options), + : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), address_list_(address_list), + source_address_(source_address), socket_factory_(socket_factory), + transport_socket_options_(transport_socket_options), options_(options), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { connections_.push_back(createNextConnection()); } @@ -73,7 +73,6 @@ bool HappyEyeballsConnectionImpl::initializeReadFilters() { } post_connect_state_.initialize_read_filters_.value() = true; return true; - } void HappyEyeballsConnectionImpl::addBytesSentCallback(Connection::BytesSentCb cb) { @@ -97,7 +96,8 @@ bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { if (connect_finished_) { return connections_[0]->isHalfCloseEnabled(); } - return per_connection_state_.enable_half_close_.has_value() && per_connection_state_.enable_half_close_.value(); + return per_connection_state_.enable_half_close_.has_value() && + per_connection_state_.enable_half_close_.value(); } std::string HappyEyeballsConnectionImpl::nextProtocol() const { @@ -145,8 +145,7 @@ void HappyEyeballsConnectionImpl::detectEarlyCloseWhenReadDisabled(bool value) { bool HappyEyeballsConnectionImpl::readEnabled() const { if (!connect_finished_) { return !post_connect_state_.read_disable_count_.has_value() || - post_connect_state_.read_disable_count_ == 0; - + post_connect_state_.read_disable_count_ == 0; } return connections_[0]->readEnabled(); } @@ -329,9 +328,7 @@ Event::Dispatcher& HappyEyeballsConnectionImpl::dispatcher() { return connections_[0]->dispatcher(); } -uint64_t HappyEyeballsConnectionImpl::id() const { - return id_; -} +uint64_t HappyEyeballsConnectionImpl::id() const { return id_; } void HappyEyeballsConnectionImpl::hashKey(std::vector& hash_key) const { // Pack the id into sizeof(id_) uint8_t entries in the hash_key vector. @@ -361,7 +358,8 @@ void HappyEyeballsConnectionImpl::setDelayedCloseTimeout(std::chrono::millisecon void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) const { const char* spaces = spacesForLevel(indent_level); - os << spaces << "HappyEyeballsConnectionImpl " << this << DUMP_MEMBER(id_) << DUMP_MEMBER(connect_finished_) << "\n"; + os << spaces << "HappyEyeballsConnectionImpl " << this << DUMP_MEMBER(id_) + << DUMP_MEMBER(connect_finished_) << "\n"; for (auto& connection : connections_) { DUMP_DETAILS(connection); @@ -469,7 +467,7 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, for (auto& filter : post_connect_state_.read_filters_) { connections_[0]->addReadFilter(filter); } - if (post_connect_state_.initialize_read_filters_.has_value() && + if (post_connect_state_.initialize_read_filters_.has_value() && post_connect_state_.initialize_read_filters_.value()) { bool initialized = connections_[0]->initializeReadFilters(); ASSERT(initialized); diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 0fd4b87b21772..f0c21b3ac27b5 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -800,7 +800,6 @@ TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { // Read disable count should now be 2. impl_->readDisable(false); - // readDisable() should be applied to the newly created connection. EXPECT_CALL(*created_connections_[1], readDisable(true)).Times(2); EXPECT_CALL(*failover_timer_, disableTimer()); @@ -816,17 +815,17 @@ TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { TEST_F(HappyEyeballsConnectionImplTest, ReadEnabled) { EXPECT_TRUE(impl_->readEnabled()); - impl_->readDisable(true); // Disable count 1. + impl_->readDisable(true); // Disable count 1. EXPECT_FALSE(impl_->readEnabled()); EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); - impl_->readDisable(true); // Disable count 2 + impl_->readDisable(true); // Disable count 2 EXPECT_FALSE(impl_->readEnabled()); - impl_->readDisable(false); // Disable count 1 + impl_->readDisable(false); // Disable count 1 EXPECT_FALSE(impl_->readEnabled()); - impl_->readDisable(false); // Disable count 0 + impl_->readDisable(false); // Disable count 0 EXPECT_TRUE(impl_->readEnabled()); } From 06900236b37319a87697a41bb0d4aa0694642cd6 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 17:43:08 +0000 Subject: [PATCH 29/49] Comments from Alyssa Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 40 +++++++-------- .../network/happy_eyeballs_connection_impl.h | 17 ++++--- .../happy_eyeballs_connection_impl_test.cc | 49 ++++++++++++------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 0b3267cc060d5..f64dceab746fa 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -93,18 +93,11 @@ void HappyEyeballsConnectionImpl::enableHalfClose(bool enabled) { } bool HappyEyeballsConnectionImpl::isHalfCloseEnabled() { - if (connect_finished_) { - return connections_[0]->isHalfCloseEnabled(); - } - return per_connection_state_.enable_half_close_.has_value() && - per_connection_state_.enable_half_close_.value(); + return connections_[0]->isHalfCloseEnabled(); } std::string HappyEyeballsConnectionImpl::nextProtocol() const { - if (connect_finished_) { - return connections_[0]->nextProtocol(); - } - return ""; + return connections_[0]->nextProtocol(); } void HappyEyeballsConnectionImpl::noDelay(bool enable) { @@ -189,14 +182,19 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) return; } - post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( - []() -> void { ASSERT(false); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, - []() -> void { ASSERT(false); }); - if (per_connection_state_.buffer_limits_.has_value()) { - post_connect_state_.write_buffer_.value()->setWatermarks( - per_connection_state_.buffer_limits_.value()); + if (!post_connect_state_.write_buffer_.has_value()) { + post_connect_state_.end_stream_ = false; + post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( + []() -> void { ASSERT(false); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, + []() -> void { ASSERT(false); }); + if (per_connection_state_.buffer_limits_.has_value()) { + post_connect_state_.write_buffer_.value()->setWatermarks( + per_connection_state_.buffer_limits_.value()); + } } + post_connect_state_.write_buffer_.value()->move(data); + ASSERT(!post_connect_state_.end_stream_.value()); // Don't write after end_stream. post_connect_state_.end_stream_ = end_stream; } @@ -215,12 +213,6 @@ void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { } uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { - if (!connect_finished_) { - if (per_connection_state_.buffer_limits_.has_value()) { - return per_connection_state_.buffer_limits_.value(); - } - return 0; - } return connections_[0]->bufferLimit(); } @@ -261,9 +253,11 @@ bool HappyEyeballsConnectionImpl::startSecureTransport() { if (!connect_finished_) { per_connection_state_.start_secure_transport_ = true; } - bool ret = false; + bool ret = true; for (auto& connection : connections_) { - ret = connection->startSecureTransport(); + if (!connection->startSecureTransport()) { + ret = false; + } } return ret; } diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 039d6cb46391f..3ea4633e2bda9 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -19,7 +19,7 @@ namespace Network { /** * Implementation of ClientConnection which transparently attempts connections to - * multiple different IP addresses, and use the first connection that succeeds. + * multiple different IP addresses, and uses the first connection that succeeds. * See the Happy Eyeballs RFC at https://datatracker.ietf.org/doc/html/rfc6555 * TODO(RyanTheOptimist): Implement the Happy Eyeballs address sorting algorithm * either in the class or in the resolution code. @@ -35,17 +35,21 @@ class HappyEyeballsConnectionImpl : public ClientConnection { ~HappyEyeballsConnectionImpl() override; + // After a connection is established, these methods simply delegate to the + // underlying connection. Before the connection is established, however + // their behavior depends on their semantics. For anything which can result + // in up-call (e.g. filter registration) or which must only happen once (e.g. + // writing data) the context is saved in until the connection completes, at + // which point they are replayed to the underlying connection. For simple methods + // they are applied to each open connection and applied when creating new ones. + // Network::ClientConnection void connect() override; - - // Network::FilterManager void addWriteFilter(WriteFilterSharedPtr filter) override; void addFilter(FilterSharedPtr filter) override; void addReadFilter(ReadFilterSharedPtr filter) override; void removeReadFilter(ReadFilterSharedPtr filter) override; bool initializeReadFilters() override; - - // Network::Connection void addBytesSentCallback(BytesSentCb cb) override; void enableHalfClose(bool enabled) override; bool isHalfCloseEnabled() override; @@ -79,8 +83,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void hashKey(std::vector& hash_key) const override; void setConnectionStats(const ConnectionStats& stats) override; void setDelayedCloseTimeout(std::chrono::milliseconds timeout) override; - - // ScopeTrackedObject void dumpState(std::ostream& os, int indent_level) const override; private: @@ -99,7 +101,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void onBelowWriteBufferLowWatermark() override { parent_.onBelowWriteBufferLowWatermark(this); } - // Not needed? interesting. ClientConnection& connection() { return connection_; } private: diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index f0c21b3ac27b5..6f1e2c758eb5a 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -428,6 +428,28 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } +TEST_F(HappyEyeballsConnectionImplTest, WriteTwiceBeforeConnect) { + Buffer::OwnedImpl data1("hello world"); + Buffer::OwnedImpl data2("goodbye"); + + impl_->write(data1, false); + impl_->write(data2, true); + + EXPECT_CALL(*created_connections_[0], connect()); + impl_->connect(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*created_connections_[0], write(_, _)) + .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_EQ("hello worldgoodbye", data.toString()); + EXPECT_TRUE(end_stream); + ; + })); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); +} + TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -482,22 +504,6 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } -TEST_F(HappyEyeballsConnectionImplTest, BufferLimit) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); - - // Always returns 0 until connected. - EXPECT_EQ(0, impl_->bufferLimit()); - - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); - - // Delegates to the connection once connected. - EXPECT_CALL(*created_connections_[0], bufferLimit()).WillOnce(Return(42)); - EXPECT_EQ(42, impl_->bufferLimit()); -} - TEST_F(HappyEyeballsConnectionImplTest, AboveHighWatermark) { EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); @@ -761,10 +767,10 @@ TEST_F(HappyEyeballsConnectionImplTest, EnableHalfCloseAfterConnect) { } TEST_F(HappyEyeballsConnectionImplTest, IsHalfCloseEnabled) { + EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(false)); EXPECT_FALSE(impl_->isHalfCloseEnabled()); - EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); - impl_->enableHalfClose(true); + EXPECT_CALL(*created_connections_[0], isHalfCloseEnabled()).WillOnce(Return(true)); EXPECT_TRUE(impl_->isHalfCloseEnabled()); connectFirstAttempt(); @@ -875,6 +881,13 @@ TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransportAfterConnect) { // Tests for HappyEyeballsConnectionImpl methods which simply delegate to the first connection. +TEST_F(HappyEyeballsConnectionImplTest, BufferLimit) { + connectFirstAttempt(); + + EXPECT_CALL(*created_connections_[0], bufferLimit()).WillOnce(Return(42)); + EXPECT_EQ(42, impl_->bufferLimit()); +} + TEST_F(HappyEyeballsConnectionImplTest, NextProtocol) { connectFirstAttempt(); From f07f63e5abef7f848516a8d27716ef30044bbf4f Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 17:50:03 +0000 Subject: [PATCH 30/49] Format Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index f64dceab746fa..8a1fe94253f38 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -212,9 +212,7 @@ void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { } } -uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { - return connections_[0]->bufferLimit(); -} +uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { return connections_[0]->bufferLimit(); } bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { if (!connect_finished_) { From 177ed7fd5abaa4847cbd501b8a0fcef42d55433d Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 21:22:47 +0000 Subject: [PATCH 31/49] More good comments from Alyssa Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 37 ++++++-- .../network/happy_eyeballs_connection_impl.h | 89 +++++++++++++------ .../happy_eyeballs_connection_impl_test.cc | 4 + 3 files changed, 94 insertions(+), 36 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 8a1fe94253f38..d3a2ab6124566 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -11,8 +11,7 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), address_list_(address_list), - source_address_(source_address), socket_factory_(socket_factory), - transport_socket_options_(transport_socket_options), options_(options), + connection_construction_state_({source_address, socket_factory, transport_socket_options, options}), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { connections_.push_back(createNextConnection()); } @@ -30,6 +29,8 @@ void HappyEyeballsConnectionImpl::addWriteFilter(WriteFilterSharedPtr filter) { connections_[0]->addWriteFilter(filter); return; } + // Filters should only be notified of events on the final connection, so defer adding + // filters until the final connection has been determined. post_connect_state_.write_filters_.push_back(filter); } @@ -38,6 +39,8 @@ void HappyEyeballsConnectionImpl::addFilter(FilterSharedPtr filter) { connections_[0]->addFilter(filter); return; } + // Filters should only be notified of events on the final connection, so defer adding + // filters until the final connection has been determined. post_connect_state_.filters_.push_back(filter); } @@ -46,6 +49,8 @@ void HappyEyeballsConnectionImpl::addReadFilter(ReadFilterSharedPtr filter) { connections_[0]->addReadFilter(filter); return; } + // Filters should only be notified of events on the final connection, so defer adding + // filters until the final connection has been determined. post_connect_state_.read_filters_.push_back(filter); } @@ -54,6 +59,8 @@ void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { connections_[0]->removeReadFilter(filter); return; } + // Filters should only be notified of events on the final connection, so remove + // the filters from the list of deferred filters. auto i = post_connect_state_.read_filters_.begin(); while (i != post_connect_state_.read_filters_.end()) { if (*i == filter) { @@ -68,6 +75,8 @@ bool HappyEyeballsConnectionImpl::initializeReadFilters() { if (connect_finished_) { return connections_[0]->initializeReadFilters(); } + // Filters should only be notified of events on the final connection, so defer + // initialization of the filters until the final connection has been determined. if (!post_connect_state_.read_filters_.empty()) { return false; } @@ -80,6 +89,8 @@ void HappyEyeballsConnectionImpl::addBytesSentCallback(Connection::BytesSentCb c connections_[0]->addBytesSentCallback(cb); return; } + // Callbacks should only be notified of events on the final connection, so defer adding + // callbacks until the final connection has been determined. post_connect_state_.bytes_sent_callbacks_.push_back(cb); } @@ -182,6 +193,8 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) return; } + // Data should only be written on the final connection, so defer actually writing + // until the final connection has been determined. if (!post_connect_state_.write_buffer_.has_value()) { post_connect_state_.end_stream_ = false; post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( @@ -270,6 +283,8 @@ void HappyEyeballsConnectionImpl::addConnectionCallbacks(ConnectionCallbacks& cb connections_[0]->addConnectionCallbacks(cb); return; } + // Callbacks should only be notified of events on the final connection, so defer adding + // callbacks until the final connection has been determined. post_connect_state_.connection_callbacks_.push_back(&cb); } @@ -278,6 +293,8 @@ void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& connections_[0]->removeConnectionCallbacks(cb); return; } + // Callbacks should only be notified of events on the final connection, so remove + // the callback from the list of deferred callbacks. auto i = post_connect_state_.connection_callbacks_.begin(); while (i != post_connect_state_.connection_callbacks_.end()) { if (*i == &cb) { @@ -361,8 +378,8 @@ void HappyEyeballsConnectionImpl::dumpState(std::ostream& os, int indent_level) ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { ASSERT(next_address_ < address_list_.size()); auto connection = dispatcher_.createClientConnection( - address_list_[next_address_++], source_address_, - socket_factory_.createTransportSocket(transport_socket_options_), options_); + address_list_[next_address_++], connection_construction_state_.source_address_, + connection_construction_state_.socket_factory_.createTransportSocket(connection_construction_state_.transport_socket_options_), connection_construction_state_.options_); callbacks_wrappers_.push_back(std::make_unique(*this, *connection)); connection->addConnectionCallbacks(*callbacks_wrappers_.back()); @@ -410,21 +427,27 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper) { wrapper->connection().removeConnectionCallbacks(*wrapper); if (event != ConnectionEvent::Connected) { + // This connection attempt has failed. If possible, start another connection attempt + // immediately, instead of waiting for the timer. if (next_address_ < address_list_.size()) { next_attempt_timer_->disableTimer(); tryAnotherConnection(); } + // If there is at least one more attempt running then the current attempt can be destroyed. if (connections_.size() > 1) { // Nuke this connection and associated callbacks and let a subsequent attempt proceed. cleanupWrapperAndConnection(wrapper); return; } + // This connection attempt failed but there are no more attempts to be made, so pass + // the failure up. + ASSERT(connections_.size() == 1); } connect_finished_ = true; next_attempt_timer_->disableTimer(); - // Close and delete up other connections. + // Close and delete any other connections. auto it = connections_.begin(); while (it != connections_.end()) { if (it->get() != &(wrapper->connection())) { @@ -450,6 +473,7 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, } if (event == ConnectionEvent::Connected) { + // Apply post-connect state which is only connections which have succeeded. for (auto& filter : post_connect_state_.filters_) { connections_[0]->addFilter(filter); } @@ -477,9 +501,6 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, post_connect_state_.end_stream_.value()); } } - - std::vector cbs; - cbs.swap(post_connect_state_.connection_callbacks_); } void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper) { diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 3ea4633e2bda9..5990ea7ba04d3 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -20,6 +20,14 @@ namespace Network { /** * Implementation of ClientConnection which transparently attempts connections to * multiple different IP addresses, and uses the first connection that succeeds. + * After a connection is established, all methods simply delegate to the + * underlying connection. Before the connection is established, however + * their behavior depends on their semantics. For anything which can result + * in up-call (e.g. filter registration) or which must only happen once (e.g. + * writing data) the context is saved in until the connection completes, at + * which point they are replayed to the underlying connection. For simple methods + * they are applied to each open connection and applied when creating new ones. + * * See the Happy Eyeballs RFC at https://datatracker.ietf.org/doc/html/rfc6555 * TODO(RyanTheOptimist): Implement the Happy Eyeballs address sorting algorithm * either in the class or in the resolution code. @@ -35,54 +43,54 @@ class HappyEyeballsConnectionImpl : public ClientConnection { ~HappyEyeballsConnectionImpl() override; - // After a connection is established, these methods simply delegate to the - // underlying connection. Before the connection is established, however - // their behavior depends on their semantics. For anything which can result - // in up-call (e.g. filter registration) or which must only happen once (e.g. - // writing data) the context is saved in until the connection completes, at - // which point they are replayed to the underlying connection. For simple methods - // they are applied to each open connection and applied when creating new ones. - // Network::ClientConnection void connect() override; + + // Methods which defer action until the final connection has been determined. void addWriteFilter(WriteFilterSharedPtr filter) override; void addFilter(FilterSharedPtr filter) override; void addReadFilter(ReadFilterSharedPtr filter) override; void removeReadFilter(ReadFilterSharedPtr filter) override; bool initializeReadFilters() override; void addBytesSentCallback(BytesSentCb cb) override; + void write(Buffer::Instance& data, bool end_stream) override; + void addConnectionCallbacks(ConnectionCallbacks& cb) override; + void removeConnectionCallbacks(ConnectionCallbacks& cb) override; + + // Methods which are applied to each connection attempt. void enableHalfClose(bool enabled) override; - bool isHalfCloseEnabled() override; - std::string nextProtocol() const override; void noDelay(bool enable) override; void readDisable(bool disable) override; void detectEarlyCloseWhenReadDisabled(bool value) override; - bool readEnabled() const override; + void setConnectionStats(const ConnectionStats& stats) override; + void setDelayedCloseTimeout(std::chrono::milliseconds timeout) override; + void setBufferLimits(uint32_t limit) override; + bool startSecureTransport() override; + absl::optional lastRoundTripTime() const override; + + // Simple getters which always delegate to the first connection. + bool isHalfCloseEnabled() override; + std::string nextProtocol() const override; const SocketAddressProvider& addressProvider() const override; SocketAddressProviderSharedPtr addressProviderSharedPtr() const override; absl::optional unixSocketPeerCredentials() const override; Ssl::ConnectionInfoConstSharedPtr ssl() const override; State state() const override; bool connecting() const override; - void write(Buffer::Instance& data, bool end_stream) override; - void setBufferLimits(uint32_t limit) override; uint32_t bufferLimit() const override; - bool aboveHighWatermark() const override; const ConnectionSocket::OptionsSharedPtr& socketOptions() const override; absl::string_view requestedServerName() const override; StreamInfo::StreamInfo& streamInfo() override; const StreamInfo::StreamInfo& streamInfo() const override; absl::string_view transportFailureReason() const override; - bool startSecureTransport() override; - absl::optional lastRoundTripTime() const override; - void addConnectionCallbacks(ConnectionCallbacks& cb) override; - void removeConnectionCallbacks(ConnectionCallbacks& cb) override; - void close(ConnectionCloseType type) override; - Event::Dispatcher& dispatcher() override; + + // Methods implemented largely but this class itself. uint64_t id() const override; + Event::Dispatcher& dispatcher() override; + void close(ConnectionCloseType type) override; + bool readEnabled() const override; + bool aboveHighWatermark() const override; void hashKey(std::vector& hash_key) const override; - void setConnectionStats(const ConnectionStats& stats) override; - void setDelayedCloseTimeout(std::chrono::milliseconds timeout) override; void dumpState(std::ostream& os, int indent_level) const override; private: @@ -108,18 +116,34 @@ class HappyEyeballsConnectionImpl : public ClientConnection { ClientConnection& connection_; }; + // Creates a connection to the next address in address_list_ and applies + // any settings from per_connection_state_ to the newly created connection. ClientConnectionPtr createNextConnection(); + + // Create a new connection, connects it and scheduled a timer to start another + // connection attempt if there are more addresses to connect to. void tryAnotherConnection(); + + // Schedules another connection attempt if there are mode address to connect to. void maybeScheduleNextAttempt(); + // Called by the wrapper when the wrapped connection raises the specified event. void onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper); + // Called by the wrapper when the wrapped connection is above the write buffer + // high water mark. void onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* wrapper); + // Called by the wrapper when the wrapped connection is above the write buffer + // high water mark. void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); + // Called by the write buffer containg pending writes if it goes above the + // high water mark. void onWriteBufferHighWatermark(); + // Cleans up all state for the connection associated with wrapper. Called when the + // connection is no longer needed. void cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper); // State which needs to be applied to every connection attempt. @@ -147,16 +171,27 @@ class HappyEyeballsConnectionImpl : public ClientConnection { absl::optional initialize_read_filters_; }; + // State which is needed to construct a new connection. + struct ConnectionConstructionState { + Address::InstanceConstSharedPtr source_address_; + TransportSocketFactory& socket_factory_; + TransportSocketOptionsConstSharedPtr transport_socket_options_; + const ConnectionSocket::OptionsSharedPtr options_; + }; + // ID for this connection which is distinct from the ID of the underlying connections. const uint64_t id_; Event::Dispatcher& dispatcher_; + + // List of addresses to attempt to connect to. const std::vector& address_list_; + // Index of the next address to use. size_t next_address_ = 0; - Address::InstanceConstSharedPtr source_address_; - TransportSocketFactory& socket_factory_; - TransportSocketOptionsConstSharedPtr transport_socket_options_; - const ConnectionSocket::OptionsSharedPtr options_; + + ConnectionConstructionState connection_construction_state_; + PerConnectionState per_connection_state_; + PostConnectState post_connect_state_; // Set of active connections. std::vector connections_; @@ -167,8 +202,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { Event::TimerPtr next_attempt_timer_; bool above_write_high_water_mark_ = false; - PerConnectionState per_connection_state_; - PostConnectState post_connect_state_; }; } // namespace Network diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 6f1e2c758eb5a..d05995334f415 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -861,6 +861,10 @@ TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransport) { EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); + EXPECT_CALL(*created_connections_[0], startSecureTransport()).WillOnce(Return(false)); + EXPECT_CALL(*created_connections_[1], startSecureTransport()).WillOnce(Return(true)); + EXPECT_FALSE(impl_->startSecureTransport()); + EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); From 58a9411e630c298b61ec6ac2c20837c5a53ff847 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 21:29:46 +0000 Subject: [PATCH 32/49] Spelling Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 5990ea7ba04d3..e4b7148c13941 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -138,7 +138,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // high water mark. void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); - // Called by the write buffer containg pending writes if it goes above the + // Called by the write buffer containing pending writes if it goes above the // high water mark. void onWriteBufferHighWatermark(); From 299d0594615e16c77054133cad0fca8b90b653de Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 21:46:47 +0000 Subject: [PATCH 33/49] Tidy Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index d3a2ab6124566..6e549ee5bc932 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -468,7 +468,7 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, } } - for (auto cb : post_connect_state_.bytes_sent_callbacks_) { + for (const auto& cb : post_connect_state_.bytes_sent_callbacks_) { connections_[0]->addBytesSentCallback(cb); } From 0c5e4bbb36133a771964a3f72cf5beb960f73f9e Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 23:05:32 +0000 Subject: [PATCH 34/49] Test cleanup Signed-off-by: Ryan Hamilton --- .../happy_eyeballs_connection_impl_test.cc | 395 +++++------------- 1 file changed, 110 insertions(+), 285 deletions(-) diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index d05995334f415..6b127d03497d4 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -29,16 +29,17 @@ class HappyEyeballsConnectionImplTest : public testing::Test { .WillOnce(testing::InvokeWithoutArgs( this, &HappyEyeballsConnectionImplTest::createNextConnection)); - // This timer will be returned and armed as the happy eyeballs connection creates the next - // connection timer. - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(true)); next_connections_.push_back(std::make_unique>()); impl_ = std::make_unique( dispatcher_, address_list_, Address::InstanceConstSharedPtr(), transport_socket_factory_, transport_socket_options_, options_); } + // Called by the disptacher to return a MockClientConnection. In order to allow expections to + // be set on this connection, the object must exist. So instead of allocating a new + // MockClientConnection in this method, it instead pops the first entry from + // next_connections_ and returns that. It also saves a pointer to that connection into + // created_connections_ so that it can be interacted with after it has been returned. MockClientConnection* createNextConnection() { created_connections_.push_back(next_connections_.front().release()); next_connections_.pop_front(); @@ -48,15 +49,45 @@ class HappyEyeballsConnectionImplTest : public testing::Test { return created_connections_.back(); } - void connectFirstAttempt() { + // Calls connect() on the impl and verifies that the timer is started. + void startConnect() { + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + EXPECT_CALL(*failover_timer_, enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(*created_connections_[0], connect()); impl_->connect(); + } + + // Connects the first (and only attempt). + void connectFirstAttempt() { + startConnect(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } + // First the failover timer and creates the next connection. + void timeoutAndStartNextAttempt() { + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); + failover_timer_->invokeCallback(); + } + + // Have the second connection attempt succeed which should disable the fallback timer, + // and close the first attempt. + void connectSecondAttempt() { + ASSERT_EQ(2, created_connections_.size()); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + } + protected: Event::MockDispatcher dispatcher_; testing::StrictMock* failover_timer_; @@ -71,23 +102,14 @@ class HappyEyeballsConnectionImplTest : public testing::Test { }; TEST_F(HappyEyeballsConnectionImplTest, Connect) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); } TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); // Let the second attempt timeout to start the third and final attempt. next_connections_.push_back(std::make_unique>()); @@ -102,8 +124,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { } TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); // When the first connection attempt fails, the next attempt will be immediately // started and the timer will be armed for the third attempt. @@ -121,8 +142,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFailed) { } TEST_F(HappyEyeballsConnectionImplTest, ConnectFirstSuccess) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -130,19 +150,12 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectFirstSuccess) { } TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); + // Connect the first attempt and verify that the second is closed. EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); @@ -155,19 +168,12 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { } TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); + // Connect the second attempt and verify that the first is closed. EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -180,18 +186,10 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { } TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSucceeds) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); // When the second attempt fails, the third and final attempt will be started. next_connections_.push_back(std::make_unique>()); @@ -200,10 +198,11 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc .WillOnce( testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, disableTimer()); // Since there are no more address to connect to, the fallback timer will not // be rescheduled. + EXPECT_CALL(*failover_timer_, disableTimer()); ASSERT_EQ(2, created_connections_.size()); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); connection_callbacks_[1]->onEvent(ConnectionEvent::RemoteClose); @@ -217,8 +216,7 @@ TEST_F(HappyEyeballsConnectionImplTest, Id) { uint64_t id = ConnectionImpl::nextGlobalIdForTest() - 1; EXPECT_EQ(id, impl_->id()); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_EQ(id, impl_->id()); } @@ -227,26 +225,14 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { EXPECT_CALL(*created_connections_[0], noDelay(true)); impl_->noDelay(true); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); // noDelay() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), noDelay(true)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that noDelay calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], noDelay(false)); @@ -257,26 +243,14 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled) { EXPECT_CALL(*created_connections_[0], detectEarlyCloseWhenReadDisabled(true)); impl_->detectEarlyCloseWhenReadDisabled(true); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); // detectEarlyCloseWhenReadDisabled() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), detectEarlyCloseWhenReadDisabled(true)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that detectEarlyCloseWhenReadDisabled() calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], detectEarlyCloseWhenReadDisabled(false)); @@ -284,25 +258,16 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled) { } TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttempt) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::FlushWrite)); EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); - impl_->close(ConnectionCloseType::FlushWrite); } @@ -320,26 +285,14 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter) { // The filter will be captured by the impl and not passed to the connection until it completes. impl_->addReadFilter(filter); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - // addReadFilter() should be applied to the newly created connection. + // addReadFilter() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addReadFilter(filter)); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); ReadFilterSharedPtr filter2 = std::make_shared(); filter2->initializeReadFilterCallbacks(callbacks); @@ -353,27 +306,15 @@ TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks) { // The filter will be captured by the impl and not passed to the connection until it completes. impl_->addConnectionCallbacks(callbacks); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - // addConnectionCallbacks() should be applied to the newly created connection. + // addConnectionCallbacks() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)) .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks); })); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); MockConnectionCallbacks callbacks2; // Verify that addConnectionCallbacks() calls are delegated to the remaining connection. @@ -389,17 +330,14 @@ TEST_F(HappyEyeballsConnectionImplTest, RemoveConnectionCallbacks) { impl_->addConnectionCallbacks(callbacks); impl_->addConnectionCallbacks(callbacks2); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); impl_->removeConnectionCallbacks(callbacks); - // addConnectionCallbacks() should be applied to the newly created connection. + // addConnectionCallbacks() should be applied to the now final connection. EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_)) .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks2); })); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); // Verify that removeConnectionCallbacks calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)) @@ -413,8 +351,7 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnect) { impl_->write(data, end_stream); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -435,8 +372,7 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteTwiceBeforeConnect) { impl_->write(data1, false); impl_->write(data2, true); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -451,29 +387,17 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteTwiceBeforeConnect) { } TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_CALL(*created_connections_[0], setBufferLimits(42)); impl_->setBufferLimits(42); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); // setBufferLimits() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), setBufferLimits(42)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that removeConnectionCallbacks calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], setBufferLimits(420)); @@ -481,8 +405,7 @@ TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { } TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); Buffer::OwnedImpl data("hello world"); bool end_stream = false; @@ -492,28 +415,21 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { impl_->write(data, end_stream); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); // The call to write() will be replayed on the underlying connection. EXPECT_CALL(*created_connections_[0], write(_, _)) .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { EXPECT_EQ("hello world", data.toString()); EXPECT_FALSE(end_stream); - ; })); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); } TEST_F(HappyEyeballsConnectionImplTest, AboveHighWatermark) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_FALSE(impl_->aboveHighWatermark()); - // The call to write() will be replayed on the underlying connection. - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); // Delegates to the connection once connected. EXPECT_CALL(*created_connections_[0], aboveHighWatermark()).WillOnce(Return(true)); @@ -534,27 +450,15 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats) { .WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs); })); impl_->setConnectionStats(cs); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); // setConnectionStats() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), setConnectionStats(_)) .WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs); })); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that setConnectionStats calls are delegated to the remaining connection. Connection::ConnectionStats cs2 = {rx_total, rx_current, tx_total, @@ -565,30 +469,24 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats) { } TEST_F(HappyEyeballsConnectionImplTest, State) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_CALL(*created_connections_[0], state()).WillRepeatedly(Return(Connection::State::Open)); EXPECT_EQ(Connection::State::Open, impl_->state()); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], state()).WillOnce(Return(Connection::State::Closing)); EXPECT_EQ(Connection::State::Closing, impl_->state()); } TEST_F(HappyEyeballsConnectionImplTest, Connecting) { - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); EXPECT_CALL(*created_connections_[0], connecting()).WillRepeatedly(Return(true)); EXPECT_TRUE(impl_->connecting()); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + connectFirstAttempt(); EXPECT_CALL(*created_connections_[0], connecting()).WillRepeatedly(Return(false)); EXPECT_FALSE(impl_->connecting()); @@ -601,26 +499,14 @@ TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilter) { // The filter will be captured by the impl and not passed to the connection until it completes. impl_->addWriteFilter(filter); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - // addWriteFilter() should be applied to the newly created connection. + // addWriteFilter() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addWriteFilter(filter)); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); WriteFilterSharedPtr filter2 = std::make_shared(); filter2->initializeWriteFilterCallbacks(callbacks); @@ -648,26 +534,14 @@ TEST_F(HappyEyeballsConnectionImplTest, AddFilter) { // The filter will be captured by the impl and not passed to the connection until it completes. impl_->addFilter(filter); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - // addFilter() should be applied to the newly created connection. + // addFilter() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addFilter(filter)); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); FilterSharedPtr filter2 = std::make_shared(); filter2->initializeReadFilterCallbacks(read_callbacks); @@ -694,26 +568,14 @@ TEST_F(HappyEyeballsConnectionImplTest, AddBytesSentCallback) { // The filter will be captured by the impl and not passed to the connection until it completes. impl_->addBytesSentCallback(callback); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - // addBytesSentCallback() should be applied to the newly created connection. + // addBytesSentCallback() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addBytesSentCallback(_)); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); Connection::BytesSentCb callback2 = [](uint64_t) { return true; }; // Verify that addBytesSentCallback() calls are delegated to the remaining connection. @@ -733,26 +595,14 @@ TEST_F(HappyEyeballsConnectionImplTest, EnableHalfClose) { EXPECT_CALL(*created_connections_[0], enableHalfClose(true)); impl_->enableHalfClose(true); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); // enableHalfClose() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), enableHalfClose(true)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that enableHalfClose calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], enableHalfClose(false)); @@ -787,18 +637,10 @@ TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { // The disables will be captured by the impl and not passed to the connection until it completes. impl_->readDisable(true); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); // The disables will be captured by the impl and not passed to the connection until it completes. impl_->readDisable(true); @@ -806,13 +648,9 @@ TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { // Read disable count should now be 2. impl_->readDisable(false); - // readDisable() should be applied to the newly created connection. + // readDisable() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], readDisable(true)).Times(2); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that addBytesSentCallback() calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], readDisable(false)); @@ -824,8 +662,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ReadEnabled) { impl_->readDisable(true); // Disable count 1. EXPECT_FALSE(impl_->readEnabled()); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); impl_->readDisable(true); // Disable count 2 EXPECT_FALSE(impl_->readEnabled()); @@ -846,30 +683,18 @@ TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransport) { EXPECT_CALL(*created_connections_[0], startSecureTransport()).WillOnce(Return(true)); EXPECT_TRUE(impl_->startSecureTransport()); - EXPECT_CALL(*created_connections_[0], connect()); - impl_->connect(); + startConnect(); - // Let the first attempt timeout to start the second attempt. next_connections_.push_back(std::make_unique>()); - EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); - EXPECT_CALL(dispatcher_, createClientConnection_(_, _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); - EXPECT_CALL(*next_connections_.back(), connect()); // startSecureTransport() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), startSecureTransport()).WillOnce(Return(true)); - EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); - failover_timer_->invokeCallback(); + timeoutAndStartNextAttempt(); EXPECT_CALL(*created_connections_[0], startSecureTransport()).WillOnce(Return(false)); EXPECT_CALL(*created_connections_[1], startSecureTransport()).WillOnce(Return(true)); EXPECT_FALSE(impl_->startSecureTransport()); - EXPECT_CALL(*failover_timer_, disableTimer()); - EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); - EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); - connection_callbacks_[1]->onEvent(ConnectionEvent::Connected); + connectSecondAttempt(); // Verify that startSecureTransport calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], startSecureTransport()).WillOnce(Return(false)); From 002b94928b86c66417d2906b8cf6c0a51d7e77b0 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Wed, 21 Jul 2021 23:20:44 +0000 Subject: [PATCH 35/49] Format Signed-off-by: Ryan Hamilton --- .../common/network/happy_eyeballs_connection_impl.cc | 7 +++++-- .../network/happy_eyeballs_connection_impl_test.cc | 10 ++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 6e549ee5bc932..85de68c22a1ba 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -11,7 +11,8 @@ HappyEyeballsConnectionImpl::HappyEyeballsConnectionImpl( TransportSocketOptionsConstSharedPtr transport_socket_options, const ConnectionSocket::OptionsSharedPtr options) : id_(ConnectionImpl::next_global_id_++), dispatcher_(dispatcher), address_list_(address_list), - connection_construction_state_({source_address, socket_factory, transport_socket_options, options}), + connection_construction_state_( + {source_address, socket_factory, transport_socket_options, options}), next_attempt_timer_(dispatcher_.createTimer([this]() -> void { tryAnotherConnection(); })) { connections_.push_back(createNextConnection()); } @@ -379,7 +380,9 @@ ClientConnectionPtr HappyEyeballsConnectionImpl::createNextConnection() { ASSERT(next_address_ < address_list_.size()); auto connection = dispatcher_.createClientConnection( address_list_[next_address_++], connection_construction_state_.source_address_, - connection_construction_state_.socket_factory_.createTransportSocket(connection_construction_state_.transport_socket_options_), connection_construction_state_.options_); + connection_construction_state_.socket_factory_.createTransportSocket( + connection_construction_state_.transport_socket_options_), + connection_construction_state_.options_); callbacks_wrappers_.push_back(std::make_unique(*this, *connection)); connection->addConnectionCallbacks(*callbacks_wrappers_.back()); diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 6b127d03497d4..6f44db74f74a6 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -35,7 +35,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { transport_socket_options_, options_); } - // Called by the disptacher to return a MockClientConnection. In order to allow expections to + // Called by the dispatcher to return a MockClientConnection. In order to allow expectations to // be set on this connection, the object must exist. So instead of allocating a new // MockClientConnection in this method, it instead pops the first entry from // next_connections_ and returns that. It also saves a pointer to that connection into @@ -70,8 +70,8 @@ class HappyEyeballsConnectionImplTest : public testing::Test { void timeoutAndStartNextAttempt() { EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) - .WillOnce( - testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + .WillOnce(testing::InvokeWithoutArgs( + this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); EXPECT_CALL(*failover_timer_, enableTimer(std::chrono::milliseconds(300), nullptr)); failover_timer_->invokeCallback(); @@ -101,9 +101,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { std::unique_ptr impl_; }; -TEST_F(HappyEyeballsConnectionImplTest, Connect) { - startConnect(); -} +TEST_F(HappyEyeballsConnectionImplTest, Connect) { startConnect(); } TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { startConnect(); From ada720e882df63e38c2ec1a46e83f54cfb457b68 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Thu, 22 Jul 2021 17:46:36 +0000 Subject: [PATCH 36/49] More test coverage Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 10 +- .../network/happy_eyeballs_connection_impl.h | 2 - .../happy_eyeballs_connection_impl_test.cc | 96 +++++++++++++++++-- 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 85de68c22a1ba..a126e2a671a68 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -78,10 +78,10 @@ bool HappyEyeballsConnectionImpl::initializeReadFilters() { } // Filters should only be notified of events on the final connection, so defer // initialization of the filters until the final connection has been determined. - if (!post_connect_state_.read_filters_.empty()) { + if (post_connect_state_.read_filters_.empty()) { return false; } - post_connect_state_.initialize_read_filters_.value() = true; + post_connect_state_.initialize_read_filters_ = true; return true; } @@ -230,7 +230,8 @@ uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { return connections_[ bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { if (!connect_finished_) { - return above_write_high_water_mark_; + return post_connect_state_.write_buffer_.has_value() && + post_connect_state_.write_buffer_.value()->highWatermarkTriggered(); } return connections_[0]->aboveHighWatermark(); @@ -536,8 +537,7 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( } void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { - ASSERT(!above_write_high_water_mark_); - above_write_high_water_mark_ = true; + ASSERT(!connect_finished_); } } // namespace Network diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index e4b7148c13941..5654ee02496e4 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -200,8 +200,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // True when connect() has finished, either success or failure. bool connect_finished_ = false; Event::TimerPtr next_attempt_timer_; - - bool above_write_high_water_mark_ = false; }; } // namespace Network diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 6f44db74f74a6..61f6b1e3f5d49 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -219,6 +219,23 @@ TEST_F(HappyEyeballsConnectionImplTest, Id) { EXPECT_EQ(id, impl_->id()); } +TEST_F(HappyEyeballsConnectionImplTest, HashKey) { + uint64_t id = ConnectionImpl::nextGlobalIdForTest() - 1; + + startConnect(); + + std::vector hash_key = {'A', 'B', 'C' }; + uint8_t* id_array = reinterpret_cast(&id); + impl_->hashKey(hash_key); + EXPECT_EQ(3 + sizeof(id), hash_key.size()); + EXPECT_EQ('A', hash_key[0]); + EXPECT_EQ('B', hash_key[1]); + EXPECT_EQ('C', hash_key[2]); + for (size_t i = 0; i < sizeof(id); ++i) { + EXPECT_EQ(id_array[i], hash_key[i+3]); + } +} + TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { EXPECT_CALL(*created_connections_[0], noDelay(true)); impl_->noDelay(true); @@ -255,6 +272,24 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled) { impl_->detectEarlyCloseWhenReadDisabled(false); } +TEST_F(HappyEyeballsConnectionImplTest, SetDelayedCloseTimeout) { + startConnect(); + + EXPECT_CALL(*created_connections_[0], setDelayedCloseTimeout(std::chrono::milliseconds(5))); + impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5)); + + next_connections_.push_back(std::make_unique>()); + // setDelayedCloseTimeout() should be applied to the newly created connection. + EXPECT_CALL(*next_connections_.back(), setDelayedCloseTimeout(std::chrono::milliseconds(5))); + timeoutAndStartNextAttempt(); + + connectSecondAttempt(); + + // Verify that setDelayedCloseTimeout() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], setDelayedCloseTimeout(std::chrono::milliseconds(10))); + impl_->setDelayedCloseTimeout(std::chrono::milliseconds(10)); +} + TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttempt) { startConnect(); @@ -299,6 +334,35 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter) { impl_->addReadFilter(filter2); } +TEST_F(HappyEyeballsConnectionImplTest, InitializeReadFilters) { + startConnect(); + + // No read filters have been added + EXPECT_FALSE(impl_->initializeReadFilters()); + + MockReadFilterCallbacks callbacks; + ReadFilterSharedPtr filter = std::make_shared(); + filter->initializeReadFilterCallbacks(callbacks); + impl_->addReadFilter(filter); + + // initializeReadFilters() will be captured by the impl and not passed to the connection until it completes. + EXPECT_TRUE(impl_->initializeReadFilters()); + + next_connections_.push_back(std::make_unique>()); + timeoutAndStartNextAttempt(); + + // initializeReadFilters() should be applied to the now final connection. + EXPECT_CALL(*created_connections_[1], addReadFilter(_)); + EXPECT_CALL(*created_connections_[1], initializeReadFilters()).WillOnce(Return(true)); + connectSecondAttempt(); + + ReadFilterSharedPtr filter2 = std::make_shared(); + filter2->initializeReadFilterCallbacks(callbacks); + // Verify that addReadFilter() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], addReadFilter(filter2)); + impl_->addReadFilter(filter2); +} + TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks) { MockConnectionCallbacks callbacks; // The filter will be captured by the impl and not passed to the connection until it completes. @@ -384,6 +448,22 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteTwiceBeforeConnect) { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } +TEST_F(HappyEyeballsConnectionImplTest, Write) { + startConnect(); + + connectFirstAttempt(); + + Buffer::OwnedImpl data("hello world"); + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*created_connections_[0], write(_, _)) + .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_EQ("hello world", data.toString()); + EXPECT_TRUE(end_stream); + ; + })); + impl_->write(data, true); +} + TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { startConnect(); @@ -708,6 +788,13 @@ TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransportAfterConnect) { // Tests for HappyEyeballsConnectionImpl methods which simply delegate to the first connection. +TEST_F(HappyEyeballsConnectionImplTest, Dispatcher) { + connectFirstAttempt(); + + EXPECT_CALL(*created_connections_[0], dispatcher()).WillRepeatedly(ReturnRef(dispatcher_)); + EXPECT_EQ(&dispatcher_, &(impl_->dispatcher())); +} + TEST_F(HappyEyeballsConnectionImplTest, BufferLimit) { connectFirstAttempt(); @@ -777,14 +864,7 @@ TEST_F(HappyEyeballsConnectionImplTest, StreamInfo) { StreamInfo::MockStreamInfo info; EXPECT_CALL(*created_connections_[0], streamInfo()).WillOnce(ReturnRef(info)); - EXPECT_EQ(&impl_->streamInfo(), &info); -} - -TEST_F(HappyEyeballsConnectionImplTest, SetDelayedCloseTimeout) { - connectFirstAttempt(); - - EXPECT_CALL(*created_connections_[0], setDelayedCloseTimeout(std::chrono::milliseconds(5))); - impl_->setDelayedCloseTimeout(std::chrono::milliseconds(5)); + EXPECT_EQ(&info, &impl_->streamInfo()); } TEST_F(HappyEyeballsConnectionImplTest, TransportFailureReason) { From 11334e58b29073265f5556072e324c890b9a6d1d Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 23 Jul 2021 15:28:47 +0000 Subject: [PATCH 37/49] format Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 4 +--- test/common/network/happy_eyeballs_connection_impl_test.cc | 7 ++++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index a126e2a671a68..a54364da994d8 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -536,9 +536,7 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( ASSERT(false); } -void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { - ASSERT(!connect_finished_); -} +void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { ASSERT(!connect_finished_); } } // namespace Network } // namespace Envoy diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 61f6b1e3f5d49..1d3f10d8a99f7 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -224,7 +224,7 @@ TEST_F(HappyEyeballsConnectionImplTest, HashKey) { startConnect(); - std::vector hash_key = {'A', 'B', 'C' }; + std::vector hash_key = {'A', 'B', 'C'}; uint8_t* id_array = reinterpret_cast(&id); impl_->hashKey(hash_key); EXPECT_EQ(3 + sizeof(id), hash_key.size()); @@ -232,7 +232,7 @@ TEST_F(HappyEyeballsConnectionImplTest, HashKey) { EXPECT_EQ('B', hash_key[1]); EXPECT_EQ('C', hash_key[2]); for (size_t i = 0; i < sizeof(id); ++i) { - EXPECT_EQ(id_array[i], hash_key[i+3]); + EXPECT_EQ(id_array[i], hash_key[i + 3]); } } @@ -345,7 +345,8 @@ TEST_F(HappyEyeballsConnectionImplTest, InitializeReadFilters) { filter->initializeReadFilterCallbacks(callbacks); impl_->addReadFilter(filter); - // initializeReadFilters() will be captured by the impl and not passed to the connection until it completes. + // initializeReadFilters() will be captured by the impl and not passed to the connection until it + // completes. EXPECT_TRUE(impl_->initializeReadFilters()); next_connections_.push_back(std::make_unique>()); From db0ff5380a9008003e03ba82dda68e3469874036 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 23 Jul 2021 17:45:34 +0000 Subject: [PATCH 38/49] All tests Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 8 +- .../happy_eyeballs_connection_impl_test.cc | 109 ++++++++++++++++++ 2 files changed, 113 insertions(+), 4 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index a54364da994d8..7f0ba781794b2 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -69,7 +69,7 @@ void HappyEyeballsConnectionImpl::removeReadFilter(ReadFilterSharedPtr filter) { return; } } - ASSERT(false); + NOT_REACHED_GCOVR_EXCL_LINE; } bool HappyEyeballsConnectionImpl::initializeReadFilters() { @@ -304,7 +304,7 @@ void HappyEyeballsConnectionImpl::removeConnectionCallbacks(ConnectionCallbacks& return; } } - ASSERT(false); + NOT_REACHED_GCOVR_EXCL_LINE; } void HappyEyeballsConnectionImpl::close(ConnectionCloseType type) { @@ -528,12 +528,12 @@ void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallback void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark( ConnectionCallbacksWrapper* /*wrapper*/) { - ASSERT(false); + NOT_REACHED_GCOVR_EXCL_LINE; } void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( ConnectionCallbacksWrapper* /*wrapper*/) { - ASSERT(false); + NOT_REACHED_GCOVR_EXCL_LINE; } void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { ASSERT(!connect_finished_); } diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 1d3f10d8a99f7..2acadfb9fac0b 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -210,6 +210,35 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); } +TEST_F(HappyEyeballsConnectionImplTest, ConnectThenAllTimeoutAndFail) { + startConnect(); + + next_connections_.push_back(std::make_unique>()); + timeoutAndStartNextAttempt(); + + // After the second timeout the third and final attempt will be started. + next_connections_.push_back(std::make_unique>()); + EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); + EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[2], _, _, _)) + .WillOnce( + testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); + EXPECT_CALL(*next_connections_.back(), connect()); + ASSERT_EQ(2, created_connections_.size()); + failover_timer_->invokeCallback(); + + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[1]->onEvent(ConnectionEvent::RemoteClose); + + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::NoFlush)); + connection_callbacks_[0]->onEvent(ConnectionEvent::RemoteClose); + + EXPECT_CALL(*created_connections_[2], removeConnectionCallbacks(_)); + EXPECT_CALL(*failover_timer_, disableTimer()); + connection_callbacks_[2]->onEvent(ConnectionEvent::RemoteClose); +} + TEST_F(HappyEyeballsConnectionImplTest, Id) { uint64_t id = ConnectionImpl::nextGlobalIdForTest() - 1; EXPECT_EQ(id, impl_->id()); @@ -304,6 +333,27 @@ TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttempt) { impl_->close(ConnectionCloseType::FlushWrite); } +TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttemptWithCallbacks) { + startConnect(); + + MockConnectionCallbacks callbacks; + // The filter will be captured by the impl and not passed to the connection until it is closed. + impl_->addConnectionCallbacks(callbacks); + + next_connections_.push_back(std::make_unique>()); + timeoutAndStartNextAttempt(); + + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], removeConnectionCallbacks(_)); + EXPECT_CALL(*created_connections_[1], close(ConnectionCloseType::NoFlush)); + // addConnectionCallbacks() should be applied to the now final connection. + EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_)) + .WillOnce(Invoke([&](ConnectionCallbacks& c) -> void { EXPECT_EQ(&c, &callbacks); })); + EXPECT_CALL(*created_connections_[0], close(ConnectionCloseType::FlushWrite)); + impl_->close(ConnectionCloseType::FlushWrite); +} + TEST_F(HappyEyeballsConnectionImplTest, CloseAfterAttemptComplete) { connectFirstAttempt(); @@ -334,6 +384,42 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter) { impl_->addReadFilter(filter2); } +TEST_F(HappyEyeballsConnectionImplTest, RemoveReadFilter) { + startConnect(); + + connectFirstAttempt(); + + MockReadFilterCallbacks callbacks; + ReadFilterSharedPtr filter = std::make_shared(); + filter->initializeReadFilterCallbacks(callbacks); + // Verify that removeReadFilter() calls are delegated to the final connection. + EXPECT_CALL(*created_connections_[0], removeReadFilter(filter)); + impl_->removeReadFilter(filter); +} + +TEST_F(HappyEyeballsConnectionImplTest, RemoveReadFilterBeforeConnectFinished) { + startConnect(); + + MockReadFilterCallbacks callbacks; + ReadFilterSharedPtr filter = std::make_shared(); + filter->initializeReadFilterCallbacks(callbacks); + ReadFilterSharedPtr filter2 = std::make_shared(); + filter2->initializeReadFilterCallbacks(callbacks); + // The filters will be captured by the impl and not passed to the connection until it completes. + impl_->addReadFilter(filter); + impl_->addReadFilter(filter2); + + // The removal will be captured by the impl and not passed to the connection until it completes. + impl_->removeReadFilter(filter); + + next_connections_.push_back(std::make_unique>()); + timeoutAndStartNextAttempt(); + + // Verify that addReadFilter() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[1], addReadFilter(filter2)); + connectSecondAttempt(); +} + TEST_F(HappyEyeballsConnectionImplTest, InitializeReadFilters) { startConnect(); @@ -483,6 +569,29 @@ TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { impl_->setBufferLimits(420); } +TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeLimit) { + startConnect(); + + Buffer::OwnedImpl data("hello world"); + size_t length = data.length(); + bool end_stream = false; + + impl_->write(data, end_stream); + EXPECT_FALSE(impl_->aboveHighWatermark()); + + EXPECT_CALL(*created_connections_[0], setBufferLimits(length - 1)); + impl_->setBufferLimits(length - 1); + EXPECT_TRUE(impl_->aboveHighWatermark()); + + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*created_connections_[0], write(_, _)) + .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { + EXPECT_EQ("hello world", data.toString()); + EXPECT_FALSE(end_stream); + })); + connectFirstAttempt(); +} + TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { startConnect(); From 91cd11aac88644bb0694d78ccbcb4e5e3051e68d Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 23 Jul 2021 18:04:59 +0000 Subject: [PATCH 39/49] Two more Signed-off-by: Ryan Hamilton --- .../happy_eyeballs_connection_impl_test.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 2acadfb9fac0b..2e1c3b873822d 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -450,6 +450,16 @@ TEST_F(HappyEyeballsConnectionImplTest, InitializeReadFilters) { impl_->addReadFilter(filter2); } +TEST_F(HappyEyeballsConnectionImplTest, InitializeReadFiltersAfterConnect) { + startConnect(); + + connectFirstAttempt(); + + // Verify that initializeReadFilters() calls are delegated to the remaining connection. + EXPECT_CALL(*created_connections_[0], initializeReadFilters()).WillOnce(Return(false)); + EXPECT_FALSE(impl_->initializeReadFilters()); +} + TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks) { MockConnectionCallbacks callbacks; // The filter will be captured by the impl and not passed to the connection until it completes. @@ -977,6 +987,15 @@ TEST_F(HappyEyeballsConnectionImplTest, StreamInfo) { EXPECT_EQ(&info, &impl_->streamInfo()); } +TEST_F(HappyEyeballsConnectionImplTest, ConstStreamInfo) { + connectFirstAttempt(); + + StreamInfo::MockStreamInfo info; + EXPECT_CALL(*created_connections_[0], streamInfo()).WillOnce(ReturnRef(info)); + const HappyEyeballsConnectionImpl* impl = impl_.get(); + EXPECT_EQ(&info, &impl->streamInfo()); +} + TEST_F(HappyEyeballsConnectionImplTest, TransportFailureReason) { connectFirstAttempt(); From f63ad489bee610f010a8058d825b7ad6e78765ae Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Mon, 26 Jul 2021 20:12:08 +0000 Subject: [PATCH 40/49] Address comments from Alyssa Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 18 +++++++++++++--- .../network/happy_eyeballs_connection_impl.h | 4 ++++ .../happy_eyeballs_connection_impl_test.cc | 21 +++++++++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 7f0ba781794b2..6293f68c8507b 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -217,8 +217,7 @@ void HappyEyeballsConnectionImpl::setBufferLimits(uint32_t limit) { ASSERT(!per_connection_state_.buffer_limits_.has_value()); per_connection_state_.buffer_limits_ = limit; if (post_connect_state_.write_buffer_.has_value()) { - post_connect_state_.write_buffer_.value()->setWatermarks( - per_connection_state_.buffer_limits_.value()); + post_connect_state_.write_buffer_.value()->setWatermarks(limit); } } for (auto& connection : connections_) { @@ -448,6 +447,12 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ASSERT(connections_.size() == 1); } + // Close all other connections and configure the final connection. + setUpFinalConnection(event, wrapper); +} + +void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, + ConnectionCallbacksWrapper* wrapper) { connect_finished_ = true; next_attempt_timer_->disableTimer(); @@ -536,7 +541,14 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( NOT_REACHED_GCOVR_EXCL_LINE; } -void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { ASSERT(!connect_finished_); } +void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { + ASSERT(!connect_finished_); + for (auto callback : post_connect_state_.connection_callbacks_) { + if (callback) { + callback->onAboveWriteBufferHighWatermark(); + } + } +} } // namespace Network } // namespace Envoy diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 5654ee02496e4..02cf8919a4d9f 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -130,6 +130,10 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // Called by the wrapper when the wrapped connection raises the specified event. void onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper); + // Called to bind the final connection. All other connections will be closed, and + // and deferred operations will be replayed. + void setUpFinalConnection(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper); + // Called by the wrapper when the wrapped connection is above the write buffer // high water mark. void onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* wrapper); diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 2e1c3b873822d..d1f67e9c0d829 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -66,7 +66,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); } - // First the failover timer and creates the next connection. + // Fires the failover timer and creates the next connection. void timeoutAndStartNextAttempt() { EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) @@ -116,7 +116,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { .WillOnce( testing::InvokeWithoutArgs(this, &HappyEyeballsConnectionImplTest::createNextConnection)); EXPECT_CALL(*next_connections_.back(), connect()); - // Since there are no more address to connect to, the fallback timer will not + // Since there are no more addresses to connect to, the fallback timer will not // be rescheduled. failover_timer_->invokeCallback(); } @@ -622,6 +622,23 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { connectFirstAttempt(); } +TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimitWithCallbacks) { + startConnect(); + + MockConnectionCallbacks callbacks; + // The filter will be captured by the impl and not passed to the connection until it is closed. + impl_->addConnectionCallbacks(callbacks); + + Buffer::OwnedImpl data("hello world"); + bool end_stream = false; + + EXPECT_CALL(*created_connections_[0], setBufferLimits(data.length() - 1)); + impl_->setBufferLimits(data.length() - 1); + + EXPECT_CALL(callbacks, onAboveWriteBufferHighWatermark()); + impl_->write(data, end_stream); +} + TEST_F(HappyEyeballsConnectionImplTest, AboveHighWatermark) { startConnect(); From 016be672936211fa064324bd751000100f209153 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 27 Jul 2021 17:20:56 +0000 Subject: [PATCH 41/49] More tweaks Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 1 + .../happy_eyeballs_connection_impl_test.cc | 46 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 6293f68c8507b..d1bc4c9e1c130 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -229,6 +229,7 @@ uint32_t HappyEyeballsConnectionImpl::bufferLimit() const { return connections_[ bool HappyEyeballsConnectionImpl::aboveHighWatermark() const { if (!connect_finished_) { + // Writes are deferred, so return the watermark status from the deferred write buffer. return post_connect_state_.write_buffer_.has_value() && post_connect_state_.write_buffer_.value()->highWatermarkTriggered(); } diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index d1f67e9c0d829..843db1176fc85 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -67,7 +67,7 @@ class HappyEyeballsConnectionImplTest : public testing::Test { } // Fires the failover timer and creates the next connection. - void timeoutAndStartNextAttempt() { + void timeOutAndStartNextAttempt() { EXPECT_CALL(transport_socket_factory_, createTransportSocket(_)); EXPECT_CALL(dispatcher_, createClientConnection_(address_list_[1], _, _, _)) .WillOnce(testing::InvokeWithoutArgs( @@ -107,7 +107,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeout) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // Let the second attempt timeout to start the third and final attempt. next_connections_.push_back(std::make_unique>()); @@ -151,7 +151,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenFirstSuccess) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // Connect the first attempt and verify that the second is closed. EXPECT_CALL(*failover_timer_, disableTimer()); @@ -169,7 +169,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondSuccess) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // Connect the second attempt and verify that the first is closed. EXPECT_CALL(*failover_timer_, disableTimer()); @@ -187,7 +187,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectTimeoutThenSecondFailsAndFirstSuc startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // When the second attempt fails, the third and final attempt will be started. next_connections_.push_back(std::make_unique>()); @@ -214,7 +214,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ConnectThenAllTimeoutAndFail) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // After the second timeout the third and final attempt will be started. next_connections_.push_back(std::make_unique>()); @@ -274,7 +274,7 @@ TEST_F(HappyEyeballsConnectionImplTest, NoDelay) { next_connections_.push_back(std::make_unique>()); // noDelay() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), noDelay(true)); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); connectSecondAttempt(); @@ -292,7 +292,7 @@ TEST_F(HappyEyeballsConnectionImplTest, DetectEarlyCloseWhenReadDisabled) { next_connections_.push_back(std::make_unique>()); // detectEarlyCloseWhenReadDisabled() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), detectEarlyCloseWhenReadDisabled(true)); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); connectSecondAttempt(); @@ -310,7 +310,7 @@ TEST_F(HappyEyeballsConnectionImplTest, SetDelayedCloseTimeout) { next_connections_.push_back(std::make_unique>()); // setDelayedCloseTimeout() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), setDelayedCloseTimeout(std::chrono::milliseconds(5))); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); connectSecondAttempt(); @@ -323,7 +323,7 @@ TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttempt) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -341,7 +341,7 @@ TEST_F(HappyEyeballsConnectionImplTest, CloseDuringAttemptWithCallbacks) { impl_->addConnectionCallbacks(callbacks); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); @@ -371,7 +371,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddReadFilter) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // addReadFilter() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addReadFilter(filter)); @@ -413,7 +413,7 @@ TEST_F(HappyEyeballsConnectionImplTest, RemoveReadFilterBeforeConnectFinished) { impl_->removeReadFilter(filter); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // Verify that addReadFilter() calls are delegated to the remaining connection. EXPECT_CALL(*created_connections_[1], addReadFilter(filter2)); @@ -436,7 +436,7 @@ TEST_F(HappyEyeballsConnectionImplTest, InitializeReadFilters) { EXPECT_TRUE(impl_->initializeReadFilters()); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // initializeReadFilters() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addReadFilter(_)); @@ -468,7 +468,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddConnectionCallbacks) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // addConnectionCallbacks() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addConnectionCallbacks(_)) @@ -570,7 +570,7 @@ TEST_F(HappyEyeballsConnectionImplTest, SetBufferLimits) { next_connections_.push_back(std::make_unique>()); // setBufferLimits() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), setBufferLimits(42)); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); connectSecondAttempt(); @@ -671,7 +671,7 @@ TEST_F(HappyEyeballsConnectionImplTest, SetConnectionStats) { // setConnectionStats() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), setConnectionStats(_)) .WillOnce(Invoke([&](const Connection::ConnectionStats& s) -> void { EXPECT_EQ(&s, &cs); })); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); connectSecondAttempt(); @@ -717,7 +717,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddWriteFilter) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // addWriteFilter() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addWriteFilter(filter)); @@ -752,7 +752,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddFilter) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // addFilter() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addFilter(filter)); @@ -786,7 +786,7 @@ TEST_F(HappyEyeballsConnectionImplTest, AddBytesSentCallback) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // addBytesSentCallback() should be applied to the now final connection. EXPECT_CALL(*created_connections_[1], addBytesSentCallback(_)); @@ -815,7 +815,7 @@ TEST_F(HappyEyeballsConnectionImplTest, EnableHalfClose) { next_connections_.push_back(std::make_unique>()); // enableHalfClose() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), enableHalfClose(true)); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); connectSecondAttempt(); @@ -855,7 +855,7 @@ TEST_F(HappyEyeballsConnectionImplTest, ReadDisable) { startConnect(); next_connections_.push_back(std::make_unique>()); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); // The disables will be captured by the impl and not passed to the connection until it completes. impl_->readDisable(true); @@ -903,7 +903,7 @@ TEST_F(HappyEyeballsConnectionImplTest, StartSecureTransport) { next_connections_.push_back(std::make_unique>()); // startSecureTransport() should be applied to the newly created connection. EXPECT_CALL(*next_connections_.back(), startSecureTransport()).WillOnce(Return(true)); - timeoutAndStartNextAttempt(); + timeOutAndStartNextAttempt(); EXPECT_CALL(*created_connections_[0], startSecureTransport()).WillOnce(Return(false)); EXPECT_CALL(*created_connections_[1], startSecureTransport()).WillOnce(Return(true)); From 9dfc6789951c19b3da5a81d857c2cceac7b596b1 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 27 Jul 2021 19:56:55 +0000 Subject: [PATCH 42/49] Correctly handle watermark signaling when moving the write data to the final connection Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 30 ++++++++++++++----- .../network/happy_eyeballs_connection_impl.h | 4 +++ .../happy_eyeballs_connection_impl_test.cc | 19 ++++++++++-- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index d1bc4c9e1c130..c964396e7fa88 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -199,8 +199,11 @@ void HappyEyeballsConnectionImpl::write(Buffer::Instance& data, bool end_stream) if (!post_connect_state_.write_buffer_.has_value()) { post_connect_state_.end_stream_ = false; post_connect_state_.write_buffer_ = dispatcher_.getWatermarkFactory().createBuffer( - []() -> void { ASSERT(false); }, [this]() -> void { this->onWriteBufferHighWatermark(); }, - []() -> void { ASSERT(false); }); + [this]() -> void { this->onWriteBufferLowWatermark(); }, + [this]() -> void { this->onWriteBufferHighWatermark(); }, + // ConnectionCallbacks do not have a method to receive overflow watermark + // notification. So this class, like ConnectionImpl, has a no-op handler. + []() -> void { /* TODO(adisuissa): Handle overflow watermark */ }); if (per_connection_state_.buffer_limits_.has_value()) { post_connect_state_.write_buffer_.value()->setWatermarks( per_connection_state_.buffer_limits_.value()); @@ -472,12 +475,6 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, callbacks_wrappers_.clear(); // Apply post-connect state to the final socket. - for (auto cb : post_connect_state_.connection_callbacks_) { - if (cb) { - connections_[0]->addConnectionCallbacks(*cb); - } - } - for (const auto& cb : post_connect_state_.bytes_sent_callbacks_) { connections_[0]->addBytesSentCallback(cb); } @@ -507,10 +504,21 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, if (post_connect_state_.write_buffer_.has_value()) { // write_buffer_ and end_stream_ are both set together in write(). ASSERT(post_connect_state_.end_stream_.has_value()); + // If a buffer limit was set, ensure that it was applied to the connection. + if (per_connection_state_.buffer_limits_.has_value()) { + ASSERT(connections_[0]->bufferLimit() == + per_connection_state_.buffer_limits_.value()); + } connections_[0]->write(*post_connect_state_.write_buffer_.value(), post_connect_state_.end_stream_.value()); } } + + for (auto cb : post_connect_state_.connection_callbacks_) { + if (cb) { + connections_[0]->addConnectionCallbacks(*cb); + } + } } void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper) { @@ -542,6 +550,12 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( NOT_REACHED_GCOVR_EXCL_LINE; } +void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { + // Oonly be called went moving write data from the deferred write buffer + // to the underlying connection. In this case, the connection callbacks must + // not be notified since this should be transparent to the callbacks. +} + void HappyEyeballsConnectionImpl::onWriteBufferHighWatermark() { ASSERT(!connect_finished_); for (auto callback : post_connect_state_.connection_callbacks_) { diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 02cf8919a4d9f..a303952178c1e 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -142,6 +142,10 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // high water mark. void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); + // Called by the write buffer containing pending writes if it goes beloq the + // low water mark. + void onWriteBufferLowWatermark(); + // Called by the write buffer containing pending writes if it goes above the // high water mark. void onWriteBufferHighWatermark(); diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 843db1176fc85..539f01d624e25 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -599,6 +599,7 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeLimit) { EXPECT_EQ("hello world", data.toString()); EXPECT_FALSE(end_stream); })); + EXPECT_CALL(*created_connections_[0], bufferLimit()).WillRepeatedly(Return(length - 1)); connectFirstAttempt(); } @@ -606,13 +607,15 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimit) { startConnect(); Buffer::OwnedImpl data("hello world"); + size_t length = data.length(); bool end_stream = false; - EXPECT_CALL(*created_connections_[0], setBufferLimits(data.length() - 1)); + EXPECT_CALL(*created_connections_[0], setBufferLimits(length - 1)); impl_->setBufferLimits(data.length() - 1); impl_->write(data, end_stream); + EXPECT_CALL(*created_connections_[0], bufferLimit()).WillRepeatedly(Return(length - 1)); // The call to write() will be replayed on the underlying connection. EXPECT_CALL(*created_connections_[0], write(_, _)) .WillOnce(Invoke([](Buffer::Instance& data, bool end_stream) -> void { @@ -630,13 +633,25 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimitWithCallbacks impl_->addConnectionCallbacks(callbacks); Buffer::OwnedImpl data("hello world"); + size_t length = data.length(); bool end_stream = false; - EXPECT_CALL(*created_connections_[0], setBufferLimits(data.length() - 1)); + EXPECT_CALL(*created_connections_[0], setBufferLimits(length - 1)); impl_->setBufferLimits(data.length() - 1); EXPECT_CALL(callbacks, onAboveWriteBufferHighWatermark()); impl_->write(data, end_stream); + + { + testing::InSequence s; + // The call to write() will be replayed on the underlying connection. + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); + EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], bufferLimit()).WillRepeatedly(Return(length - 1)); + EXPECT_CALL(*created_connections_[0], write(_, _)); + EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_)); + connection_callbacks_[0]->onEvent(ConnectionEvent::Connected); + } } TEST_F(HappyEyeballsConnectionImplTest, AboveHighWatermark) { From ae43032f59234fd36aadf68ba98cb5d08d9fcfb6 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 27 Jul 2021 20:22:15 +0000 Subject: [PATCH 43/49] Format Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 5 ++--- source/common/network/happy_eyeballs_connection_impl.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index c964396e7fa88..a879d0db6712c 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -506,8 +506,7 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, ASSERT(post_connect_state_.end_stream_.has_value()); // If a buffer limit was set, ensure that it was applied to the connection. if (per_connection_state_.buffer_limits_.has_value()) { - ASSERT(connections_[0]->bufferLimit() == - per_connection_state_.buffer_limits_.value()); + ASSERT(connections_[0]->bufferLimit() == per_connection_state_.buffer_limits_.value()); } connections_[0]->write(*post_connect_state_.write_buffer_.value(), post_connect_state_.end_stream_.value()); @@ -551,7 +550,7 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( } void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { - // Oonly be called went moving write data from the deferred write buffer + // Only be called went moving write data from the deferred write buffer // to the underlying connection. In this case, the connection callbacks must // not be notified since this should be transparent to the callbacks. } diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index a303952178c1e..c15f6c413d440 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -142,7 +142,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // high water mark. void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); - // Called by the write buffer containing pending writes if it goes beloq the + // Called by the write buffer containing pending writes if it goes below the // low water mark. void onWriteBufferLowWatermark(); From 47e34e49a9ceba25b88e831c02cb1f3d87874c08 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 27 Jul 2021 20:29:33 +0000 Subject: [PATCH 44/49] went -> when Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index a879d0db6712c..1671f5348015d 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -550,7 +550,7 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( } void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { - // Only be called went moving write data from the deferred write buffer + // Only be called when moving write data from the deferred write buffer // to the underlying connection. In this case, the connection callbacks must // not be notified since this should be transparent to the callbacks. } From 73cce3b399c4960afb8941c46cfec73e6724b1c6 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 27 Jul 2021 20:43:19 +0000 Subject: [PATCH 45/49] Better comments Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 3 +++ test/common/network/happy_eyeballs_connection_impl_test.cc | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 1671f5348015d..237a1a898a09a 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -513,6 +513,9 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, } } + // Add connection callbacks after moving data from the deferred write buffer so that + // any high watermark notification is swallowed and not conveyed to the callbacks, since + // that was already delivered to the callbacks when the data was written to the buffer. for (auto cb : post_connect_state_.connection_callbacks_) { if (cb) { connections_[0]->addConnectionCallbacks(*cb); diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 539f01d624e25..80e68b87879c7 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -643,8 +643,10 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimitWithCallbacks impl_->write(data, end_stream); { + // The call to write() will be replayed on the underlying connection, but it will be done + // after the temporary callbacks are removed and before the final callbacks are added. + // This causes the underlying connection's high watermark notification to be swallowed. testing::InSequence s; - // The call to write() will be replayed on the underlying connection. EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*failover_timer_, disableTimer()); EXPECT_CALL(*created_connections_[0], bufferLimit()).WillRepeatedly(Return(length - 1)); From 9b0ad9e110be560349c50ce91f987a2c78d29403 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Tue, 27 Jul 2021 20:49:00 +0000 Subject: [PATCH 46/49] Grammar! Yarrrrrr! Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index 237a1a898a09a..dfc7eda29ffab 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -553,8 +553,8 @@ void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( } void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { - // Only be called when moving write data from the deferred write buffer - // to the underlying connection. In this case, the connection callbacks must + // Only called when moving write data from the deferred write buffer to + // the underlying connection. In this case, the connection callbacks must // not be notified since this should be transparent to the callbacks. } From 88ba9960bf2eba0537f8d57262a7429ef2cb5268 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 30 Jul 2021 17:20:07 +0000 Subject: [PATCH 47/49] Fix comments from snow Signed-off-by: Ryan Hamilton --- .../network/happy_eyeballs_connection_impl.cc | 23 +++++---------- .../network/happy_eyeballs_connection_impl.h | 28 ++++++++++--------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index dfc7eda29ffab..adfb6a3246213 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -156,23 +156,19 @@ bool HappyEyeballsConnectionImpl::readEnabled() const { } const SocketAddressProvider& HappyEyeballsConnectionImpl::addressProvider() const { - // Note, this might change before connect finishes. return connections_[0]->addressProvider(); } SocketAddressProviderSharedPtr HappyEyeballsConnectionImpl::addressProviderSharedPtr() const { - // Note, this might change before connect finishes. return connections_[0]->addressProviderSharedPtr(); } absl::optional HappyEyeballsConnectionImpl::unixSocketPeerCredentials() const { - // Note, this might change before connect finishes. return connections_[0]->unixSocketPeerCredentials(); } Ssl::ConnectionInfoConstSharedPtr HappyEyeballsConnectionImpl::ssl() const { - // Note, this might change before connect finishes. return connections_[0]->ssl(); } @@ -446,9 +442,9 @@ void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, cleanupWrapperAndConnection(wrapper); return; } - // This connection attempt failed but there are no more attempts to be made, so pass - // the failure up. ASSERT(connections_.size() == 1); + // This connection attempt failed but there are no more attempts to be made, so pass + // the failure up by setting up this connection as the final one. } // Close all other connections and configure the final connection. @@ -492,6 +488,11 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, } if (post_connect_state_.initialize_read_filters_.has_value() && post_connect_state_.initialize_read_filters_.value()) { + // initialize_read_filters_ is set to true in initializeReadFilters() only when + // there are read filters installed. The underlying connection's initializeReadFilters() + // will always return true when read filters are installed so this should always + // return true. + ASSERT(!post_connect_state_.read_filters_.empty()); bool initialized = connections_[0]->initializeReadFilters(); ASSERT(initialized); } @@ -542,16 +543,6 @@ void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallback } } -void HappyEyeballsConnectionImpl::onAboveWriteBufferHighWatermark( - ConnectionCallbacksWrapper* /*wrapper*/) { - NOT_REACHED_GCOVR_EXCL_LINE; -} - -void HappyEyeballsConnectionImpl::onBelowWriteBufferLowWatermark( - ConnectionCallbacksWrapper* /*wrapper*/) { - NOT_REACHED_GCOVR_EXCL_LINE; -} - void HappyEyeballsConnectionImpl::onWriteBufferLowWatermark() { // Only called when moving write data from the deferred write buffer to // the underlying connection. In this case, the connection callbacks must diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index c15f6c413d440..2c79f69268ebe 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -21,7 +21,7 @@ namespace Network { * Implementation of ClientConnection which transparently attempts connections to * multiple different IP addresses, and uses the first connection that succeeds. * After a connection is established, all methods simply delegate to the - * underlying connection. Before the connection is established, however + * underlying connection. However, before the connection is established * their behavior depends on their semantics. For anything which can result * in up-call (e.g. filter registration) or which must only happen once (e.g. * writing data) the context is saved in until the connection completes, at @@ -68,12 +68,16 @@ class HappyEyeballsConnectionImpl : public ClientConnection { bool startSecureTransport() override; absl::optional lastRoundTripTime() const override; - // Simple getters which always delegate to the first connection. + // Simple getters which always delegate to the first connection in connections_. bool isHalfCloseEnabled() override; std::string nextProtocol() const override; + // Note, this might change before connect finishes. const SocketAddressProvider& addressProvider() const override; + // Note, this might change before connect finishes. SocketAddressProviderSharedPtr addressProviderSharedPtr() const override; + // Note, this might change before connect finishes. absl::optional unixSocketPeerCredentials() const override; + // Note, this might change before connect finishes. Ssl::ConnectionInfoConstSharedPtr ssl() const override; State state() const override; bool connecting() const override; @@ -84,7 +88,7 @@ class HappyEyeballsConnectionImpl : public ClientConnection { const StreamInfo::StreamInfo& streamInfo() const override; absl::string_view transportFailureReason() const override; - // Methods implemented largely but this class itself. + // Methods implemented largely by this class itself. uint64_t id() const override; Event::Dispatcher& dispatcher() override; void close(ConnectionCloseType type) override; @@ -104,10 +108,16 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void onEvent(ConnectionEvent event) override { parent_.onEvent(event, this); } void onAboveWriteBufferHighWatermark() override { - parent_.onAboveWriteBufferHighWatermark(this); + // No data will be written to the connection while the wrapper is assocaited with it, + // so the write buffer should never hit the high watermark. + NOT_REACHED_GCOVR_EXCL_LINE; } - void onBelowWriteBufferLowWatermark() override { parent_.onBelowWriteBufferLowWatermark(this); } + void onBelowWriteBufferLowWatermark() override { + // No data will be written to the connection while the wrapper is assocaited with it, + // so the write buffer should never hit the high watermark. + NOT_REACHED_GCOVR_EXCL_LINE; + } ClientConnection& connection() { return connection_; } @@ -134,14 +144,6 @@ class HappyEyeballsConnectionImpl : public ClientConnection { // and deferred operations will be replayed. void setUpFinalConnection(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper); - // Called by the wrapper when the wrapped connection is above the write buffer - // high water mark. - void onAboveWriteBufferHighWatermark(ConnectionCallbacksWrapper* wrapper); - - // Called by the wrapper when the wrapped connection is above the write buffer - // high water mark. - void onBelowWriteBufferLowWatermark(ConnectionCallbacksWrapper* wrapper); - // Called by the write buffer containing pending writes if it goes below the // low water mark. void onWriteBufferLowWatermark(); From 52aee63657244d809e903f05bca9b6a4746d8bb2 Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Fri, 30 Jul 2021 17:21:54 +0000 Subject: [PATCH 48/49] Spelling Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.h b/source/common/network/happy_eyeballs_connection_impl.h index 2c79f69268ebe..9417db2de44d5 100644 --- a/source/common/network/happy_eyeballs_connection_impl.h +++ b/source/common/network/happy_eyeballs_connection_impl.h @@ -108,13 +108,13 @@ class HappyEyeballsConnectionImpl : public ClientConnection { void onEvent(ConnectionEvent event) override { parent_.onEvent(event, this); } void onAboveWriteBufferHighWatermark() override { - // No data will be written to the connection while the wrapper is assocaited with it, + // No data will be written to the connection while the wrapper is associated with it, // so the write buffer should never hit the high watermark. NOT_REACHED_GCOVR_EXCL_LINE; } void onBelowWriteBufferLowWatermark() override { - // No data will be written to the connection while the wrapper is assocaited with it, + // No data will be written to the connection while the wrapper is associated with it, // so the write buffer should never hit the high watermark. NOT_REACHED_GCOVR_EXCL_LINE; } From 811309ad1158517dfe860e15b7f210dada7da1de Mon Sep 17 00:00:00 2001 From: Ryan Hamilton Date: Mon, 2 Aug 2021 22:14:21 +0000 Subject: [PATCH 49/49] Better location for removeConnectionCallbacks Signed-off-by: Ryan Hamilton --- source/common/network/happy_eyeballs_connection_impl.cc | 8 ++++++-- .../common/network/happy_eyeballs_connection_impl_test.cc | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/common/network/happy_eyeballs_connection_impl.cc b/source/common/network/happy_eyeballs_connection_impl.cc index adfb6a3246213..dbe68e170d059 100644 --- a/source/common/network/happy_eyeballs_connection_impl.cc +++ b/source/common/network/happy_eyeballs_connection_impl.cc @@ -428,7 +428,6 @@ void HappyEyeballsConnectionImpl::maybeScheduleNextAttempt() { void HappyEyeballsConnectionImpl::onEvent(ConnectionEvent event, ConnectionCallbacksWrapper* wrapper) { - wrapper->connection().removeConnectionCallbacks(*wrapper); if (event != ConnectionEvent::Connected) { // This connection attempt has failed. If possible, start another connection attempt // immediately, instead of waiting for the timer. @@ -456,11 +455,15 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, connect_finished_ = true; next_attempt_timer_->disableTimer(); + // Remove the proxied connection callbacks from all connections. + for (auto& w : callbacks_wrappers_) { + w->connection().removeConnectionCallbacks(*w); + } + // Close and delete any other connections. auto it = connections_.begin(); while (it != connections_.end()) { if (it->get() != &(wrapper->connection())) { - (*it)->removeConnectionCallbacks(*wrapper); (*it)->close(ConnectionCloseType::NoFlush); it = connections_.erase(it); } else { @@ -525,6 +528,7 @@ void HappyEyeballsConnectionImpl::setUpFinalConnection(ConnectionEvent event, } void HappyEyeballsConnectionImpl::cleanupWrapperAndConnection(ConnectionCallbacksWrapper* wrapper) { + wrapper->connection().removeConnectionCallbacks(*wrapper); for (auto it = connections_.begin(); it != connections_.end();) { if (it->get() == &(wrapper->connection())) { (*it)->close(ConnectionCloseType::NoFlush); diff --git a/test/common/network/happy_eyeballs_connection_impl_test.cc b/test/common/network/happy_eyeballs_connection_impl_test.cc index 80e68b87879c7..f3c7106bee5ac 100644 --- a/test/common/network/happy_eyeballs_connection_impl_test.cc +++ b/test/common/network/happy_eyeballs_connection_impl_test.cc @@ -647,8 +647,8 @@ TEST_F(HappyEyeballsConnectionImplTest, WriteBeforeConnectOverLimitWithCallbacks // after the temporary callbacks are removed and before the final callbacks are added. // This causes the underlying connection's high watermark notification to be swallowed. testing::InSequence s; - EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*failover_timer_, disableTimer()); + EXPECT_CALL(*created_connections_[0], removeConnectionCallbacks(_)); EXPECT_CALL(*created_connections_[0], bufferLimit()).WillRepeatedly(Return(length - 1)); EXPECT_CALL(*created_connections_[0], write(_, _)); EXPECT_CALL(*created_connections_[0], addConnectionCallbacks(_));