diff --git a/ci/build_setup.sh b/ci/build_setup.sh index 8afeb6e795f18..b8559da8beab5 100755 --- a/ci/build_setup.sh +++ b/ci/build_setup.sh @@ -78,7 +78,7 @@ fi cp -f "${ENVOY_SRCDIR}"/ci/WORKSPACE.filter.example "${ENVOY_FILTER_EXAMPLE_SRCDIR}"/WORKSPACE # This is the hash on https://github.com/lyft/envoy-filter-example.git we pin to. -(cd "${ENVOY_FILTER_EXAMPLE_SRCDIR}" && git checkout 9f006f6be519007e9be3b29ad531b8e8be5a18d6) +(cd "${ENVOY_FILTER_EXAMPLE_SRCDIR}" && git fetch origin && git checkout 03f5353c939b0d796925c67d94db52e8055ee732) # Also setup some space for building Envoy standalone. export ENVOY_BUILD_DIR="${BUILD_DIR}"/envoy diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index 622eccd61f5ed..4aba77f5d04d4 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -129,7 +129,11 @@ uint32_t Utility::portFromTcpUrl(const std::string& url) { } } -Address::InstanceConstSharedPtr Utility::getLocalAddress() { +// TODO(hennna): Currently getLocalAddress does not support choosing between +// multiple interfaces and addresses not returned by getifaddrs. In additon, +// the default is to return a loopback address of type version. This function may +// need to be updated in the future. Discussion can be found at Github issue #939. +Address::InstanceConstSharedPtr Utility::getLocalAddress(const Address::IpVersion version) { struct ifaddrs* ifaddr; struct ifaddrs* ifa; Address::InstanceConstSharedPtr ret; @@ -144,10 +148,13 @@ Address::InstanceConstSharedPtr Utility::getLocalAddress() { continue; } - if (ifa->ifa_addr->sa_family == AF_INET) { - sockaddr_in* addr = reinterpret_cast(ifa->ifa_addr); - if (htonl(INADDR_LOOPBACK) != addr->sin_addr.s_addr) { - ret.reset(new Address::Ipv4Instance(addr)); + if ((ifa->ifa_addr->sa_family == AF_INET && version == Address::IpVersion::v4) || + (ifa->ifa_addr->sa_family == AF_INET6 && version == Address::IpVersion::v6)) { + const struct sockaddr_storage* addr = + reinterpret_cast(ifa->ifa_addr); + ret = Address::addressFromSockAddr( + *addr, (version == Address::IpVersion::v4) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)); + if (!isLoopbackAddress(*ret)) { break; } } @@ -157,6 +164,14 @@ Address::InstanceConstSharedPtr Utility::getLocalAddress() { freeifaddrs(ifaddr); } + // If the local address is not found above, then return the loopback addresss by default. + if (ret == nullptr) { + if (version == Address::IpVersion::v4) { + ret.reset(new Address::Ipv4Instance("127.0.0.1")); + } else if (version == Address::IpVersion::v6) { + ret.reset(new Address::Ipv6Instance("::1")); + } + } return ret; } @@ -183,7 +198,14 @@ bool Utility::isLoopbackAddress(const Address::Instance& address) { return false; } - return address.ip()->ipv4()->address() == htonl(INADDR_LOOPBACK); + if (address.ip()->version() == Address::IpVersion::v4) { + // Compare to the canonical v4 loopback address: 127.0.0.1. + return address.ip()->ipv4()->address() == htonl(INADDR_LOOPBACK); + } else if (address.ip()->version() == Address::IpVersion::v6) { + std::array addr = address.ip()->ipv6()->address(); + return 0 == memcmp(&addr, &in6addr_loopback, sizeof(in6addr_loopback)); + } + NOT_IMPLEMENTED; } Address::InstanceConstSharedPtr Utility::getCanonicalIpv4LoopbackAddress() { diff --git a/source/common/network/utility.h b/source/common/network/utility.h index 6c7ed9f400490..6a5dbb04ac83a 100644 --- a/source/common/network/utility.h +++ b/source/common/network/utility.h @@ -79,9 +79,13 @@ class Utility { static uint32_t portFromTcpUrl(const std::string& url); /** + * Get the local address of the first interface address that is of type + * version and is not a loopback address. If no matches are found, return the + * loopback address of type version. + * @param the local address IP version. * @return the local IP address of the server */ - static Address::InstanceConstSharedPtr getLocalAddress(); + static Address::InstanceConstSharedPtr getLocalAddress(const Address::IpVersion version); /** * Determine whether this is an internal (RFC1918) address. @@ -91,6 +95,7 @@ class Utility { /** * Check if address is loopback address. + * @param address IP address to check. * @return true if so, otherwise false */ static bool isLoopbackAddress(const Address::Instance& address); diff --git a/source/exe/main.cc b/source/exe/main.cc index 158225c690682..387c7230597bc 100644 --- a/source/exe/main.cc +++ b/source/exe/main.cc @@ -59,8 +59,10 @@ int main(int argc, char** argv) { DefaultTestHooks default_test_hooks; Stats::ThreadLocalStoreImpl stats_store(*restarter); Server::ProdComponentFactory component_factory; - LocalInfo::LocalInfoImpl local_info(Network::Utility::getLocalAddress(), options.serviceZone(), - options.serviceClusterName(), options.serviceNodeName()); + // TODO(henna): Add CLI option for local address IP version. + LocalInfo::LocalInfoImpl local_info( + Network::Utility::getLocalAddress(Network::Address::IpVersion::v4), options.serviceZone(), + options.serviceClusterName(), options.serviceNodeName()); Server::InstanceImpl server(options, default_test_hooks, *restarter, stats_store, restarter->accessLogLock(), component_factory, local_info); server.run(); diff --git a/source/server/server.cc b/source/server/server.cc index 15e40dcc65163..81963df3f0a31 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -77,10 +77,6 @@ InstanceImpl::InstanceImpl(Options& options, TestHooks& hooks, HotRestart& resta } server_stats_.version_.set(version_int); - if (!local_info_.address()) { - throw EnvoyException("could not resolve local address"); - } - restarter_.initialize(handler_.dispatcher(), *this); drain_manager_ = component_factory.createDrainManager(*this); diff --git a/test/common/network/BUILD b/test/common/network/BUILD index bbfa82273c8ce..612f6f040d0c9 100644 --- a/test/common/network/BUILD +++ b/test/common/network/BUILD @@ -138,5 +138,6 @@ envoy_cc_test( "//source/common/json:json_loader_lib", "//source/common/network:address_lib", "//source/common/network:utility_lib", + "//test/test_common:environment_lib", ], ) diff --git a/test/common/network/address_impl_test.cc b/test/common/network/address_impl_test.cc index 6e96a03e0a108..1636c6ae140d8 100644 --- a/test/common/network/address_impl_test.cc +++ b/test/common/network/address_impl_test.cc @@ -50,7 +50,7 @@ void testSocketBindAndConnect(const std::string& addr_port_str) { ScopedFdCloser closer1(listen_fd); // Check that IPv6 sockets accept IPv6 connections only. - if (addr_port->ip()->ipv6() != nullptr) { + if (addr_port->ip()->version() == IpVersion::v6) { int v6only = 0; socklen_t size_int = sizeof(v6only); ASSERT_GE(::getsockopt(listen_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, &size_int), 0); diff --git a/test/common/network/utility_test.cc b/test/common/network/utility_test.cc index ca174de6bd4e2..91d6694e77e15 100644 --- a/test/common/network/utility_test.cc +++ b/test/common/network/utility_test.cc @@ -8,6 +8,8 @@ #include "common/network/address_impl.h" #include "common/network/utility.h" +#include "test/test_common/environment.h" + #include "gtest/gtest.h" namespace Network { @@ -147,7 +149,14 @@ TEST(NetworkUtility, resolveUrl) { EXPECT_EQ("[a:b:c:d::]:0", Utility::resolveUrl("tcp://[a:b:c:d::]:0")->asString()); } -TEST(NetworkUtility, getLocalAddress) { EXPECT_NE(nullptr, Utility::getLocalAddress()); } +class NetworkUtilityGetLocalAddress : public testing::TestWithParam {}; + +INSTANTIATE_TEST_CASE_P(IpVersions, NetworkUtilityGetLocalAddress, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); + +TEST_P(NetworkUtilityGetLocalAddress, getLocalAddress) { + EXPECT_NE(nullptr, Utility::getLocalAddress(GetParam())); +} TEST(NetworkUtility, getOriginalDst) { EXPECT_EQ(nullptr, Utility::getOriginalDst(-1)); } @@ -164,6 +173,14 @@ TEST(NetworkUtility, loopbackAddress) { Address::PipeInstance address("/foo"); EXPECT_FALSE(Utility::isLoopbackAddress(address)); } + { + Address::Ipv6Instance address("::1"); + EXPECT_TRUE(Utility::isLoopbackAddress(address)); + } + { + Address::Ipv6Instance address("::"); + EXPECT_FALSE(Utility::isLoopbackAddress(address)); + } EXPECT_EQ("127.0.0.1:0", Utility::getCanonicalIpv4LoopbackAddress()->asString()); EXPECT_EQ("[::1]:0", Utility::getIpv6LoopbackAddress()->asString()); } diff --git a/test/config/integration/BUILD b/test/config/integration/BUILD index 22c94a7596edc..f2053ad13df52 100644 --- a/test/config/integration/BUILD +++ b/test/config/integration/BUILD @@ -8,6 +8,7 @@ load( envoy_package() exports_files([ + "echo_server.json", "server.json", "server_http2.json", "server_http2_upstream.json", diff --git a/test/config/integration/echo_server.json b/test/config/integration/echo_server.json new file mode 100644 index 0000000000000..404e43a3c5493 --- /dev/null +++ b/test/config/integration/echo_server.json @@ -0,0 +1,27 @@ +{ + "listeners": [ + { + "address": "tcp://{{ ip_loopback_address }}:0", + "use_original_dst": true, + "filters": [ + { "type": "read", "name": "ratelimit", + "config": { + "domain": "foo", + "descriptors": [[{"key": "foo", "value": "bar"}]], + "stat_prefix": "name" + } + }, + { "type": "read", "name": "echo", "config": {} } + ] + }], + + "admin": { + "access_log_path": "/dev/null", + "profile_path": "{{ test_tmpdir }}/envoy.prof", + "address": "tcp://{{ ip_loopback_address }}:0" + }, + + "cluster_manager": { + "clusters": [] + } +} diff --git a/test/config/integration/server.json b/test/config/integration/server.json index c4c9da02d09bd..d9b9b332e4e43 100644 --- a/test/config/integration/server.json +++ b/test/config/integration/server.json @@ -1,19 +1,5 @@ { "listeners": [ - { - "address": "tcp://127.0.0.1:0", - "use_original_dst": true, - "filters": [ - { "type": "read", "name": "ratelimit", - "config": { - "domain": "foo", - "descriptors": [[{"key": "foo", "value": "bar"}]], - "stat_prefix": "name" - } - }, - { "type": "read", "name": "echo", "config": {} } - ] - }, { "address": "tcp://127.0.0.1:0", "filters": [ diff --git a/test/integration/BUILD b/test/integration/BUILD index f2d3249ee7a27..d33c5e2510d86 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -140,6 +140,7 @@ envoy_cc_test_library( "//source/server/http:health_check_lib", "//test/mocks/upstream:upstream_mocks", "//test/test_common:environment_lib", + "//test/test_common:network_utility_lib", "//test/test_common:utility_lib", ], ) @@ -161,6 +162,19 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "echo_integration_test", + srcs = [ + "echo_integration_test.cc", + ], + data = [ + "//test/config/integration:echo_server.json", + ], + deps = [ + ":integration_lib", + ], +) + envoy_cc_test( name = "proxy_proto_integration_test", srcs = [ diff --git a/test/integration/echo_integration_test.cc b/test/integration/echo_integration_test.cc new file mode 100644 index 0000000000000..ef48b64ebc45d --- /dev/null +++ b/test/integration/echo_integration_test.cc @@ -0,0 +1,42 @@ +#include "test/integration/integration.h" +#include "test/integration/utility.h" + +class EchoIntegrationTest : public BaseIntegrationTest, + public testing::TestWithParam { +public: + /** + * Initializer for an individual test. + */ + void SetUp() override { + fake_upstreams_.emplace_back(new FakeUpstream(GetParam(), 0, FakeHttpConnection::Type::HTTP1)); + registerPort("upstream_0", fake_upstreams_.back()->localAddress()->ip()->port()); + fake_upstreams_.emplace_back(new FakeUpstream(GetParam(), 0, FakeHttpConnection::Type::HTTP1)); + registerPort("upstream_1", fake_upstreams_.back()->localAddress()->ip()->port()); + createTestServer("test/config/integration/echo_server.json", GetParam(), {"echo"}); + } + + /** + * Destructor for an individual test. + */ + void TearDown() override { + test_server_.reset(); + fake_upstreams_.clear(); + } +}; + +INSTANTIATE_TEST_CASE_P(IpVersions, EchoIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); + +TEST_P(EchoIntegrationTest, Hello) { + Buffer::OwnedImpl buffer("hello"); + std::string response; + RawConnectionDriver connection(lookupPort("echo"), GetParam(), buffer, + [&](Network::ClientConnection&, const Buffer::Instance& data) + -> void { + response.append(TestUtility::bufferToString(data)); + connection.close(); + }); + + connection.run(); + EXPECT_EQ("hello", response); +} diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 0f8f363995934..2c3ff34fba4e4 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -15,7 +15,9 @@ #include "common/http/http2/codec_impl.h" #include "common/network/address_impl.h" #include "common/network/listen_socket_impl.h" +#include "common/network/utility.h" +#include "test/test_common/network_utility.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -195,17 +197,34 @@ FakeUpstream::FakeUpstream(const std::string& uds_path, FakeHttpConnection::Type log().info("starting fake server on unix domain socket {}", uds_path); } +// TODO(henna): Deprecate when IPv6 test support is finished. static Network::ListenSocketPtr makeTcpListenSocket(uint32_t port) { auto addr = Network::Address::InstanceConstSharedPtr{new Network::Address::Ipv4Instance("0.0.0.0", port)}; return Network::ListenSocketPtr{new Network::TcpListenSocket(addr, true)}; } +static Network::ListenSocketPtr makeTcpListenSocket(const Network::Address::IpVersion version, + uint32_t port) { + return Network::ListenSocketPtr{new Network::TcpListenSocket( + Network::Address::parseInternetAddressAndPort( + fmt::format("{}:{}", Network::Test::getAnyAddressUrlString(version), port)), + true)}; +} + +// TODO(henna): Deprecate when IPv6 test support is finished. FakeUpstream::FakeUpstream(uint32_t port, FakeHttpConnection::Type type) : FakeUpstream(nullptr, makeTcpListenSocket(port), type) { log().info("starting fake server on port {}", this->localAddress()->ip()->port()); } +FakeUpstream::FakeUpstream(Network::Address::IpVersion version, uint32_t port, + FakeHttpConnection::Type type) + : FakeUpstream(nullptr, makeTcpListenSocket(version, port), type) { + log().info("starting fake server on port {}. Address version is {}", + this->localAddress()->ip()->port(), Network::Test::addressVersionAsString(version)); +} + FakeUpstream::FakeUpstream(Ssl::ServerContext* ssl_ctx, uint32_t port, FakeHttpConnection::Type type) : FakeUpstream(ssl_ctx, makeTcpListenSocket(port), type) { diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index b5ccad450cf57..0aff79cabf1db 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -200,6 +200,8 @@ class FakeUpstream : Logger::Loggable, public Network::Filt public: FakeUpstream(const std::string& uds_path, FakeHttpConnection::Type type); FakeUpstream(uint32_t port, FakeHttpConnection::Type type); + FakeUpstream(const Network::Address::IpVersion version, uint32_t port, + FakeHttpConnection::Type type); FakeUpstream(Ssl::ServerContext* ssl_ctx, uint32_t port, FakeHttpConnection::Type type); ~FakeUpstream(); diff --git a/test/integration/hotrestart_test.sh b/test/integration/hotrestart_test.sh index da912fa76b90a..f723277e4f68e 100755 --- a/test/integration/hotrestart_test.sh +++ b/test/integration/hotrestart_test.sh @@ -4,13 +4,15 @@ set -e [[ -z "${ENVOY_BIN}" ]] && ENVOY_BIN="${TEST_RUNDIR}"/source/exe/envoy-static -# TODO(htuch): Clean this up when Bazelifying the hot restart test below. At the same time, restore -# some test behavior lost in #650, when we switched to 0 port binding - the hot restart tests no -# longer check socket passing. See #654. +# TODO(htuch): In this test script, we are duplicating work done in test_environment.cc via sed. +# Instead, we can add a simple C++ binary that links against test_environment.cc and uses the +# substitution methods provided there. +# TODO(henna): Parameterize IPv4 and IPv6 testing. HOT_RESTART_JSON="${TEST_TMPDIR}"/hot_restart.json cat "${TEST_RUNDIR}"/test/config/integration/server.json | sed -e "s#{{ upstream_. }}#0#g" | \ sed -e "s#{{ test_rundir }}#$TEST_RUNDIR#" | \ + sed -e "s#{{ ip_loopback_address }}#127.0.0.1#" | \ cat > "${HOT_RESTART_JSON}" # Now start the real server, hot restart it twice, and shut it all down as a basic hot restart diff --git a/test/integration/http2_integration_test.h b/test/integration/http2_integration_test.h index 44d62cc22d125..d288daea26c6f 100644 --- a/test/integration/http2_integration_test.h +++ b/test/integration/http2_integration_test.h @@ -7,9 +7,9 @@ class Http2IntegrationTest : public BaseIntegrationTest, public testing::Test { public: /** - * Global initializer for all integration tests. + * Initializer for an individual test. */ - static void SetUpTestCase() { + void SetUp() override { fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP1)); registerPort("upstream_0", fake_upstreams_.back()->localAddress()->ip()->port()); fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP1)); @@ -18,9 +18,9 @@ class Http2IntegrationTest : public BaseIntegrationTest, public testing::Test { } /** - * Global destructor for all integration tests. + * Destructor for an individual test test. */ - static void TearDownTestCase() { + void TearDown() override { test_server_.reset(); fake_upstreams_.clear(); } diff --git a/test/integration/http2_upstream_integration_test.h b/test/integration/http2_upstream_integration_test.h index 497382c886842..6240429976ef4 100644 --- a/test/integration/http2_upstream_integration_test.h +++ b/test/integration/http2_upstream_integration_test.h @@ -7,9 +7,9 @@ class Http2UpstreamIntegrationTest : public BaseIntegrationTest, public testing::Test { public: /** - * Global initializer for all integration tests. + * Initializer for an individual test. */ - static void SetUpTestCase() { + void SetUp() override { fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2)); registerPort("upstream_0", fake_upstreams_.back()->localAddress()->ip()->port()); fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP2)); @@ -19,9 +19,9 @@ class Http2UpstreamIntegrationTest : public BaseIntegrationTest, public testing: } /** - * Global destructor for all integration tests. + * Destructor for an individual test. */ - static void TearDownTestCase() { + void TearDown() override { test_server_.reset(); fake_upstreams_.clear(); } diff --git a/test/integration/integration.cc b/test/integration/integration.cc index 6ff75f093d1b4..16ba9bc975211 100644 --- a/test/integration/integration.cc +++ b/test/integration/integration.cc @@ -27,9 +27,6 @@ #include "gtest/gtest.h" #include "spdlog/spdlog.h" -IntegrationTestServerPtr BaseIntegrationTest::test_server_; -std::vector> BaseIntegrationTest::fake_upstreams_; - IntegrationStreamDecoder::IntegrationStreamDecoder(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {} @@ -261,12 +258,12 @@ IntegrationTcpClientPtr BaseIntegrationTest::makeTcpConnection(uint32_t port) { } void BaseIntegrationTest::registerPort(const std::string& key, uint32_t port) { - port_map()[key] = port; + port_map_[key] = port; } uint32_t BaseIntegrationTest::lookupPort(const std::string& key) { - auto it = port_map().find(key); - if (it != port_map().end()) { + auto it = port_map_.find(key); + if (it != port_map_.end()) { return it->second; } RELEASE_ASSERT(false); @@ -283,12 +280,19 @@ void BaseIntegrationTest::registerTestServerPorts(const std::vector } void BaseIntegrationTest::createTestServer(const std::string& json_path, + const Network::Address::IpVersion version, const std::vector& port_names) { test_server_ = IntegrationTestServer::create( - TestEnvironment::temporaryFileSubstitute(json_path, port_map())); + TestEnvironment::temporaryFileSubstitute(json_path, version, port_map_), version); registerTestServerPorts(port_names); } +// TODO(hennna): Deprecate when IPv6 test support is finished. +void BaseIntegrationTest::createTestServer(const std::string& json_path, + const std::vector& port_names) { + BaseIntegrationTest::createTestServer(json_path, Network::Address::IpVersion::v4, port_names); +} + void BaseIntegrationTest::testRouterRequestAndResponseWithBody(Network::ClientConnectionPtr&& conn, Http::CodecClient::Type type, uint64_t request_size, diff --git a/test/integration/integration.h b/test/integration/integration.h index 8f0e62f61b96f..2e6adabc88db8 100644 --- a/test/integration/integration.h +++ b/test/integration/integration.h @@ -166,16 +166,14 @@ class BaseIntegrationTest : Logger::Loggable { IntegrationTcpClientPtr makeTcpConnection(uint32_t port); // Test-wide port map. - static void registerPort(const std::string& key, uint32_t port); - static uint32_t lookupPort(const std::string& key); - static std::string substitutePorts(const std::string& json_path); + void registerPort(const std::string& key, uint32_t port); + uint32_t lookupPort(const std::string& key); - static void registerTestServerPorts(const std::vector& port_names); - static void createTestServer(const std::string& json_path, - const std::vector& port_names); - - static IntegrationTestServerPtr test_server_; - static std::vector> fake_upstreams_; + void registerTestServerPorts(const std::vector& port_names); + // TODO(hennna): Deprecate when IPv6 test support is finished. + void createTestServer(const std::string& json_path, const std::vector& port_names); + void createTestServer(const std::string& json_path, const Network::Address::IpVersion version, + const std::vector& port_names); Api::ApiPtr api_; Event::DispatcherPtr dispatcher_; @@ -212,10 +210,8 @@ class BaseIntegrationTest : Logger::Loggable { void testDownstreamResetBeforeResponseComplete(); void testTrailers(uint64_t request_size, uint64_t response_size); - static TestEnvironment::PortMap& port_map() { - static auto* port_map = new TestEnvironment::PortMap(); - return *port_map; - } - + std::vector> fake_upstreams_; spdlog::level::level_enum default_log_level_; + IntegrationTestServerPtr test_server_; + TestEnvironment::PortMap port_map_; }; diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index db2e3132b1469..2dcb90e5c8013 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -10,20 +10,6 @@ #include "gtest/gtest.h" -TEST_F(IntegrationTest, Echo) { - Buffer::OwnedImpl buffer("hello"); - std::string response; - RawConnectionDriver connection(lookupPort("echo"), buffer, - [&](Network::ClientConnection&, const Buffer::Instance& data) - -> void { - response.append(TestUtility::bufferToString(data)); - connection.close(); - }); - - connection.run(); - EXPECT_EQ("hello", response); -} - TEST_F(IntegrationTest, RouterNotFound) { testRouterNotFound(Http::CodecClient::Type::HTTP1); } TEST_F(IntegrationTest, RouterNotFoundBodyNoBuffer) { diff --git a/test/integration/integration_test.h b/test/integration/integration_test.h index 9384fe6ac0473..19db10547df59 100644 --- a/test/integration/integration_test.h +++ b/test/integration/integration_test.h @@ -7,21 +7,21 @@ class IntegrationTest : public BaseIntegrationTest, public testing::Test { public: /** - * Global initializer for all integration tests. + * Initializer for an individual test. */ - static void SetUpTestCase() { + void SetUp() override { fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP1)); registerPort("upstream_0", fake_upstreams_.back()->localAddress()->ip()->port()); fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP1)); registerPort("upstream_1", fake_upstreams_.back()->localAddress()->ip()->port()); createTestServer("test/config/integration/server.json", - {"echo", "http", "http_buffer", "tcp_proxy", "rds"}); + {"http", "http_buffer", "tcp_proxy", "rds"}); } /** - * Global destructor for all integration tests. + * Destructor for an individual test. */ - static void TearDownTestCase() { + void TearDown() override { test_server_.reset(); fake_upstreams_.clear(); } diff --git a/test/integration/proxy_proto_integration_test.h b/test/integration/proxy_proto_integration_test.h index 579da91d9fe1c..f23f4d319e7f5 100644 --- a/test/integration/proxy_proto_integration_test.h +++ b/test/integration/proxy_proto_integration_test.h @@ -12,18 +12,18 @@ class ProxyProtoIntegrationTest : public BaseIntegrationTest, public testing::Test { public: /** - * Global initializer for all integration tests. + * Initializer for an individual test. */ - static void SetUpTestCase() { + void SetUp() override { fake_upstreams_.emplace_back(new FakeUpstream(0, FakeHttpConnection::Type::HTTP1)); registerPort("upstream_0", fake_upstreams_.back()->localAddress()->ip()->port()); createTestServer("test/config/integration/server_proxy_proto.json", {"http"}); } /** - * Global destructor for all integration tests. + * Destructor for an individual test. */ - static void TearDownTestCase() { + void TearDown() override { test_server_.reset(); fake_upstreams_.clear(); } diff --git a/test/integration/server.cc b/test/integration/server.cc index 76cbe7fa828e3..1a74548eeb864 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -31,16 +31,17 @@ class TestHotRestart : public HotRestart { } // Server -IntegrationTestServerPtr IntegrationTestServer::create(const std::string& config_path) { +IntegrationTestServerPtr IntegrationTestServer::create(const std::string& config_path, + const Network::Address::IpVersion version) { IntegrationTestServerPtr server{new IntegrationTestServer(config_path)}; - server->start(); + server->start(version); return server; } -void IntegrationTestServer::start() { +void IntegrationTestServer::start(const Network::Address::IpVersion version) { log().info("starting integration test server"); ASSERT(!thread_); - thread_.reset(new Thread::Thread([this]() -> void { threadRoutine(); })); + thread_.reset(new Thread::Thread([version, this]() -> void { threadRoutine(version); })); // First, we want to wait until we know the server's worker threads are all // started. server_initialized_.waitReady(); @@ -53,20 +54,21 @@ void IntegrationTestServer::start() { IntegrationTestServer::~IntegrationTestServer() { log().info("stopping integration test server"); - BufferingStreamDecoderPtr response = - IntegrationUtil::makeSingleRequest(BaseIntegrationTest::lookupPort("admin"), "GET", - "/quitquitquit", "", Http::CodecClient::Type::HTTP1); + BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( + server_->admin().socket().localAddress()->ip()->version(), + server_->admin().socket().localAddress()->ip()->port(), "GET", "/quitquitquit", "", + Http::CodecClient::Type::HTTP1); EXPECT_TRUE(response->complete()); EXPECT_STREQ("200", response->headers().Status()->value().c_str()); thread_->join(); } -void IntegrationTestServer::threadRoutine() { +void IntegrationTestServer::threadRoutine(const Network::Address::IpVersion version) { Server::TestOptionsImpl options(config_path_); Server::TestHotRestart restarter; Thread::MutexBasicLockable lock; - LocalInfo::LocalInfoImpl local_info(Network::Utility::getLocalAddress(), "zone_name", + LocalInfo::LocalInfoImpl local_info(Network::Utility::getLocalAddress(version), "zone_name", "cluster_name", "node_name"); server_.reset( new Server::InstanceImpl(options, *this, restarter, stats_store_, lock, *this, local_info)); diff --git a/test/integration/server.h b/test/integration/server.h index d8b4a135f1d2b..17941835e3979 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -121,7 +121,8 @@ class IntegrationTestServer : Logger::Loggable, public TestHooks, public Server::ComponentFactory { public: - static IntegrationTestServerPtr create(const std::string& config_path); + static IntegrationTestServerPtr create(const std::string& config_path, + const Network::Address::IpVersion version); ~IntegrationTestServer(); Server::TestDrainManager& drainManager() { return *drain_manager_; } @@ -129,6 +130,7 @@ class IntegrationTestServer : Logger::Loggable, RELEASE_ASSERT(server_ != nullptr); return *server_; } + void start(const Network::Address::IpVersion version); void start(); Stats::Store& store() { return stats_store_; } @@ -152,7 +154,7 @@ class IntegrationTestServer : Logger::Loggable, /** * Runs the real server on a thread. */ - void threadRoutine(); + void threadRoutine(const Network::Address::IpVersion version); const std::string config_path_; Thread::ThreadPtr thread_; diff --git a/test/integration/ssl_integration_test.cc b/test/integration/ssl_integration_test.cc index 0bda6a6694e25..2096ac5a7b89c 100644 --- a/test/integration/ssl_integration_test.cc +++ b/test/integration/ssl_integration_test.cc @@ -17,15 +17,7 @@ using testing::Return; namespace Ssl { -std::unique_ptr SslIntegrationTest::runtime_; -std::unique_ptr SslIntegrationTest::context_manager_; -ServerContextPtr SslIntegrationTest::upstream_ssl_ctx_; -ClientContextPtr SslIntegrationTest::client_ssl_ctx_plain_; -ClientContextPtr SslIntegrationTest::client_ssl_ctx_alpn_; -ClientContextPtr SslIntegrationTest::client_ssl_ctx_san_; -ClientContextPtr SslIntegrationTest::client_ssl_ctx_alpn_san_; - -void SslIntegrationTest::SetUpTestCase() { +void SslIntegrationTest::SetUp() { runtime_.reset(new NiceMock()); context_manager_.reset(new ContextManagerImpl(*runtime_)); upstream_ssl_ctx_ = createUpstreamSslContext(); @@ -35,8 +27,11 @@ void SslIntegrationTest::SetUpTestCase() { fake_upstreams_.emplace_back( new FakeUpstream(upstream_ssl_ctx_.get(), 0, FakeHttpConnection::Type::HTTP1)); registerPort("upstream_1", fake_upstreams_.back()->localAddress()->ip()->port()); - test_server_ = MockRuntimeIntegrationTestServer::create(TestEnvironment::temporaryFileSubstitute( - "test/config/integration/server_ssl.json", port_map())); + // TODO(hennna): Add IPv6 support. + test_server_ = MockRuntimeIntegrationTestServer::create( + TestEnvironment::temporaryFileSubstitute("test/config/integration/server_ssl.json", + port_map_), + Network::Address::IpVersion::v4); registerTestServerPorts({"http"}); client_ssl_ctx_plain_ = createClientSslContext(false, false); client_ssl_ctx_alpn_ = createClientSslContext(true, false); @@ -44,7 +39,7 @@ void SslIntegrationTest::SetUpTestCase() { client_ssl_ctx_alpn_san_ = createClientSslContext(true, true); } -void SslIntegrationTest::TearDownTestCase() { +void SslIntegrationTest::TearDown() { test_server_.reset(); fake_upstreams_.clear(); upstream_ssl_ctx_.reset(); diff --git a/test/integration/ssl_integration_test.h b/test/integration/ssl_integration_test.h index aea89823ef919..cb997d08f4fb5 100644 --- a/test/integration/ssl_integration_test.h +++ b/test/integration/ssl_integration_test.h @@ -16,9 +16,10 @@ namespace Ssl { class MockRuntimeIntegrationTestServer : public IntegrationTestServer { public: - static IntegrationTestServerPtr create(const std::string& config_path) { + static IntegrationTestServerPtr create(const std::string& config_path, + Network::Address::IpVersion version) { IntegrationTestServerPtr server{new MockRuntimeIntegrationTestServer(config_path)}; - server->start(); + server->start(version); return server; } @@ -38,28 +39,28 @@ class MockRuntimeIntegrationTestServer : public IntegrationTestServer { class SslIntegrationTest : public BaseIntegrationTest, public testing::Test { public: /** - * Global initializer for all integration tests. + * Initializer for an individual test. */ - static void SetUpTestCase(); + void SetUp() override; /** - * Global destructor for all integration tests. + * Destructor for an individual test. */ - static void TearDownTestCase(); + void TearDown() override; Network::ClientConnectionPtr makeSslClientConnection(bool alpn, bool san); - static ServerContextPtr createUpstreamSslContext(); - static ClientContextPtr createClientSslContext(bool alpn, bool san); + ServerContextPtr createUpstreamSslContext(); + ClientContextPtr createClientSslContext(bool alpn, bool san); void checkStats(); private: - static std::unique_ptr runtime_; - static std::unique_ptr context_manager_; - static ServerContextPtr upstream_ssl_ctx_; - static ClientContextPtr client_ssl_ctx_plain_; - static ClientContextPtr client_ssl_ctx_alpn_; - static ClientContextPtr client_ssl_ctx_san_; - static ClientContextPtr client_ssl_ctx_alpn_san_; + std::unique_ptr runtime_; + std::unique_ptr context_manager_; + ServerContextPtr upstream_ssl_ctx_; + ClientContextPtr client_ssl_ctx_plain_; + ClientContextPtr client_ssl_ctx_alpn_; + ClientContextPtr client_ssl_ctx_san_; + ClientContextPtr client_ssl_ctx_alpn_san_; }; } // Ssl diff --git a/test/integration/uds_integration_test.h b/test/integration/uds_integration_test.h index fb75829c0ff3d..51440eb64aacd 100644 --- a/test/integration/uds_integration_test.h +++ b/test/integration/uds_integration_test.h @@ -13,9 +13,9 @@ class UdsIntegrationTest : public BaseIntegrationTest, public testing::Test { public: /** - * Global initializer for all integration tests. + * Initializer for an individual test. */ - static void SetUpTestCase() { + void SetUp() override { fake_upstreams_.emplace_back(new FakeUpstream( TestEnvironment::unixDomainSocketPath("udstest.1.sock"), FakeHttpConnection::Type::HTTP1)); fake_upstreams_.emplace_back(new FakeUpstream( @@ -24,9 +24,9 @@ class UdsIntegrationTest : public BaseIntegrationTest, public testing::Test { } /** - * Global destructor for all integration tests. + * Destructor for an individual test. */ - static void TearDownTestCase() { + void TearDown() override { test_server_.reset(); fake_upstreams_.clear(); } diff --git a/test/integration/utility.cc b/test/integration/utility.cc index 171f30e55f4c8..8ac4f59708c03 100644 --- a/test/integration/utility.cc +++ b/test/integration/utility.cc @@ -17,6 +17,7 @@ #include "common/upstream/upstream_impl.h" #include "test/mocks/upstream/mocks.h" +#include "test/test_common/network_utility.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -49,19 +50,30 @@ void BufferingStreamDecoder::onComplete() { void BufferingStreamDecoder::onResetStream(Http::StreamResetReason) { ADD_FAILURE(); } +// TODO(hennna): Deprecate when Ipv6 test support is finished. BufferingStreamDecoderPtr IntegrationUtil::makeSingleRequest(uint32_t port, const std::string& method, const std::string& url, const std::string& body, Http::CodecClient::Type type, const std::string& host) { + return makeSingleRequest(Network::Address::IpVersion::v4, port, method, url, body, type, host); +} + +BufferingStreamDecoderPtr +IntegrationUtil::makeSingleRequest(const Network::Address::IpVersion version, uint32_t port, + const std::string& method, const std::string& url, + const std::string& body, Http::CodecClient::Type type, + const std::string& host) { Api::Impl api(std::chrono::milliseconds(9000)); Event::DispatcherPtr dispatcher(api.allocateDispatcher()); std::shared_ptr cluster{new NiceMock()}; Upstream::HostDescriptionConstSharedPtr host_description{new Upstream::HostDescriptionImpl( - cluster, "", Network::Utility::resolveUrl("tcp://127.0.0.1:80"), false, "")}; - Http::CodecClientProd client(type, - dispatcher->createClientConnection(Network::Utility::resolveUrl( - fmt::format("tcp://127.0.0.1:{}", port))), - host_description); + cluster, "", Network::Utility::resolveUrl(fmt::format( + "tcp://{}:80", Network::Test::getLoopbackAddressUrlString(version))), + false, "")}; + Http::CodecClientProd client( + type, dispatcher->createClientConnection(Network::Utility::resolveUrl(fmt::format( + "tcp://{}:{}", Network::Test::getLoopbackAddressUrlString(version), port))), + host_description); BufferingStreamDecoderPtr response(new BufferingStreamDecoder([&]() -> void { client.close(); })); Http::StreamEncoder& encoder = client.newStream(*response); encoder.getStream().addCallbacks(*response); @@ -81,17 +93,23 @@ IntegrationUtil::makeSingleRequest(uint32_t port, const std::string& method, con return response; } -RawConnectionDriver::RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, +RawConnectionDriver::RawConnectionDriver(uint32_t port, const Network::Address::IpVersion version, + Buffer::Instance& initial_data, ReadCallback data_callback) { api_.reset(new Api::Impl(std::chrono::milliseconds(10000))); dispatcher_ = api_->allocateDispatcher(); - client_ = dispatcher_->createClientConnection( - Network::Utility::resolveUrl(fmt::format("tcp://127.0.0.1:{}", port))); + client_ = dispatcher_->createClientConnection(Network::Utility::resolveUrl( + fmt::format("tcp://{}:{}", Network::Test::getLoopbackAddressUrlString(version), port))); client_->addReadFilter(Network::ReadFilterSharedPtr{new ForwardingFilter(*this, data_callback)}); client_->write(initial_data); client_->connect(); } +// TODO(hennna): Deprecate when IPv6 test support is finished. +RawConnectionDriver::RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, + ReadCallback data_callback) + : RawConnectionDriver(port, Network::Address::IpVersion::v4, initial_data, data_callback) {} + RawConnectionDriver::~RawConnectionDriver() {} void RawConnectionDriver::run() { dispatcher_->run(Event::Dispatcher::RunType::Block); } diff --git a/test/integration/utility.h b/test/integration/utility.h index 7b5fd49c9c103..a518b830cd92d 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -10,6 +10,7 @@ #include "envoy/http/header_map.h" #include "envoy/network/filter.h" +#include "common/common/assert.h" #include "common/http/codec_client.h" #include "test/test_common/printers.h" @@ -51,7 +52,10 @@ class RawConnectionDriver { public: typedef std::function ReadCallback; + // TODO(hennna): Deprecate when IPv6 test support is finished. RawConnectionDriver(uint32_t port, Buffer::Instance& initial_data, ReadCallback data_callback); + RawConnectionDriver(uint32_t port, const Network::Address::IpVersion version, + Buffer::Instance& initial_data, ReadCallback data_callback); ~RawConnectionDriver(); void run(); void close(); @@ -84,6 +88,7 @@ class IntegrationUtil { public: /** * Make a new connection, issues a request, and then disconnect when the request is complete. + * @param version the IP addess version of the client and server. * @param port supplies the port to connect to on localhost. * @param method supplies the request method. * @param url supplies the request url. @@ -93,6 +98,11 @@ class IntegrationUtil { * @return BufferingStreamDecoderPtr the complete request or a partial request if there was * remote easly disconnection. */ + static BufferingStreamDecoderPtr + makeSingleRequest(const Network::Address::IpVersion version, uint32_t port, + const std::string& method, const std::string& url, const std::string& body, + Http::CodecClient::Type type, const std::string& host = "host"); + // TODO(henna): Deprecate when IPv6 test support is finished. static BufferingStreamDecoderPtr makeSingleRequest(uint32_t port, const std::string& method, const std::string& url, const std::string& body, diff --git a/test/test_common/environment.cc b/test/test_common/environment.cc index 1279762676bab..707e9aa0632a3 100644 --- a/test/test_common/environment.cc +++ b/test/test_common/environment.cc @@ -117,8 +117,15 @@ std::string TestEnvironment::substitute(const std::string str) { return out_json_string; } +// TODO(hennna): Deprecate when IPv6 test support is finished. std::string TestEnvironment::temporaryFileSubstitute(const std::string& path, const PortMap& port_map) { + return TestEnvironment::temporaryFileSubstitute(path, Network::Address::IpVersion::v4, port_map); +} + +std::string TestEnvironment::temporaryFileSubstitute(const std::string& path, + const Network::Address::IpVersion& version, + const PortMap& port_map) { // Load the entire file as a string, regex replace one at a time and write it back out. Proper // templating might be better one day, but this works for now. const std::string json_path = TestEnvironment::runfilesPath(path); @@ -139,6 +146,11 @@ std::string TestEnvironment::temporaryFileSubstitute(const std::string& path, const std::regex port_regex("\\{\\{ " + it.first + " \\}\\}"); out_json_string = std::regex_replace(out_json_string, port_regex, std::to_string(it.second)); } + + // Substitute IP loopback addresses. + const std::regex loopback_address_regex("\\{\\{ ip_loopback_address \\}\\}"); + out_json_string = std::regex_replace(out_json_string, loopback_address_regex, + Network::Test::getLoopbackAddressUrlString(version)); // Substitute paths. out_json_string = substitute(out_json_string); const std::string out_json_path = TestEnvironment::temporaryPath(path + ".with.ports.json"); diff --git a/test/test_common/environment.h b/test/test_common/environment.h index dac84605245df..918b494825a65 100644 --- a/test/test_common/environment.h +++ b/test/test_common/environment.h @@ -103,6 +103,7 @@ class TestEnvironment { */ static std::string substitute(const std::string str); + // TODO(hennna): Deprecate after IPv6 test support is finished. /** * Substitue ports and paths in a JSON file in the private writable test temporary directory. * @param path path prefix for the input file with port and path templates. @@ -111,6 +112,18 @@ class TestEnvironment { */ static std::string temporaryFileSubstitute(const std::string& path, const PortMap& port_map); + /** + * Substitue ports, paths, and IP loopback addressses in a JSON file in the + * private writable test temporary directory. + * @param path path prefix for the input file with port and path templates. + * @param version IP address version to substitute. + * @param port_map map from port name to port number. + * @return std::string path for the generated file. + */ + static std::string temporaryFileSubstitute(const std::string& path, + const Network::Address::IpVersion& version, + const PortMap& port_map); + /** * Build JSON object from a string subject to environment path substitution. * @param json JSON with template patterns including {{ test_certs }}. diff --git a/test/test_common/network_utility.cc b/test/test_common/network_utility.cc index 75fdad6028216..50abd1e93e0ad 100644 --- a/test/test_common/network_utility.cc +++ b/test/test_common/network_utility.cc @@ -87,6 +87,13 @@ const std::string getLoopbackAddressUrlString(const Address::IpVersion version) return std::string("127.0.0.1"); } +const std::string getAnyAddressUrlString(const Address::IpVersion version) { + if (version == Address::IpVersion::v6) { + return std::string("[::]"); + } + return std::string("0.0.0.0"); +} + const std::string addressVersionAsString(const Address::IpVersion version) { if (version == Address::IpVersion::v4) { return std::string("v4"); diff --git a/test/test_common/network_utility.h b/test/test_common/network_utility.h index b748e4ce1ed43..827449ae6242c 100644 --- a/test/test_common/network_utility.h +++ b/test/test_common/network_utility.h @@ -39,6 +39,13 @@ Address::InstanceConstSharedPtr findOrCheckFreePort(const std::string& addr_port */ const std::string getLoopbackAddressUrlString(const Address::IpVersion version); +/** + * Get a URL ready IP any address as a string. + * @param version IP address version of any address. + * @return std::string URL ready any address as a string. + */ +const std::string getAnyAddressUrlString(const Address::IpVersion version); + /** * Return a string version of enum IpVersion version. * @param version IP address version.