diff --git a/ci/build_setup.sh b/ci/build_setup.sh index 24095717f54a5..6c108539db3fa 100755 --- a/ci/build_setup.sh +++ b/ci/build_setup.sh @@ -81,8 +81,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. -# TODO(hennna): Point to updated hash. -(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/address_impl.cc b/source/common/network/address_impl.cc index cc3a5f61b108a..f8067d857dd9e 100644 --- a/source/common/network/address_impl.cc +++ b/source/common/network/address_impl.cc @@ -185,7 +185,12 @@ int Ipv6Instance::connect(int fd) const { } int Ipv6Instance::socket(SocketType type) const { - return ::socket(AF_INET6, flagsFromSocketType(type), 0); + const int fd = ::socket(AF_INET6, flagsFromSocketType(type), 0); + RELEASE_ASSERT(fd != -1); + // Setting IPV6_V6ONLY resticts the IPv6 socket to IPv6 connections only. + const int v6only = 1; + RELEASE_ASSERT(::setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != -1); + return fd; } PipeInstance::PipeInstance(const sockaddr_un* address) : InstanceBase(Type::Pipe) { diff --git a/source/common/network/utility.cc b/source/common/network/utility.cc index d932d3439fc55..622eccd61f5ed 100644 --- a/source/common/network/utility.cc +++ b/source/common/network/utility.cc @@ -87,12 +87,8 @@ const std::string Utility::TCP_SCHEME = "tcp://"; const std::string Utility::UNIX_SCHEME = "unix://"; Address::InstanceConstSharedPtr Utility::resolveUrl(const std::string& url) { - // TODO(mattklein123): IPv6 support. - // TODO(mattklein123): We still support the legacy tcp:// and unix:// names. We should - // support/parse ip:// and pipe:// as better names. if (url.find(TCP_SCHEME) == 0) { - return Address::InstanceConstSharedPtr{ - new Address::Ipv4Instance(hostFromTcpUrl(url), portFromTcpUrl(url))}; + return Address::parseInternetAddressAndPort(url.substr(TCP_SCHEME.size())); } else if (url.find(UNIX_SCHEME) == 0) { return Address::InstanceConstSharedPtr{ new Address::PipeInstance(url.substr(UNIX_SCHEME.size()))}; diff --git a/test/common/network/address_impl_test.cc b/test/common/network/address_impl_test.cc index 7225d18d3858e..bfe8cd9588a0e 100644 --- a/test/common/network/address_impl_test.cc +++ b/test/common/network/address_impl_test.cc @@ -46,6 +46,14 @@ void testSocketBindAndConnect(const std::string& addr_port_str) { ASSERT_GE(listen_fd, 0) << addr_port->asString(); ScopedFdCloser closer1(listen_fd); + // Check that IPv6 sockets accept IPv6 connections only. + if (addr_port->ip()->ipv6() != nullptr) { + int v6only = 0; + socklen_t size_int = sizeof(v6only); + ASSERT_GE(::getsockopt(listen_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, &size_int), 0); + EXPECT_EQ(v6only, 1); + } + // Bind the socket to the desired address and port. int rc = addr_port->bind(listen_fd); int err = errno; diff --git a/test/common/network/utility_test.cc b/test/common/network/utility_test.cc index 2ddecf1c1057e..ca174de6bd4e2 100644 --- a/test/common/network/utility_test.cc +++ b/test/common/network/utility_test.cc @@ -124,7 +124,27 @@ TEST(NetworkUtility, Url) { EXPECT_THROW(Utility::portFromTcpUrl("tcp://foo"), EnvoyException); EXPECT_THROW(Utility::portFromTcpUrl("tcp://foo:bar"), EnvoyException); EXPECT_THROW(Utility::hostFromTcpUrl(""), EnvoyException); +} + +TEST(NetworkUtility, resolveUrl) { EXPECT_THROW(Utility::resolveUrl("foo"), EnvoyException); + EXPECT_THROW(Utility::resolveUrl("abc://foo"), EnvoyException); + EXPECT_EQ("", Utility::resolveUrl("unix://")->asString()); + EXPECT_EQ("foo", Utility::resolveUrl("unix://foo")->asString()); + EXPECT_EQ("tmp", Utility::resolveUrl("unix://tmp")->asString()); + EXPECT_EQ("tmp/server", Utility::resolveUrl("unix://tmp/server")->asString()); + EXPECT_EQ(nullptr, Utility::resolveUrl("tcp://192.168.3.3")); + EXPECT_EQ(nullptr, Utility::resolveUrl("tcp://192.168.3.3.3:0")); + EXPECT_EQ(nullptr, Utility::resolveUrl("tcp://192.168.3:0")); + EXPECT_EQ(nullptr, Utility::resolveUrl("tcp://[::1]")); + EXPECT_EQ(nullptr, Utility::resolveUrl("tcp://[:::1]:1")); + EXPECT_EQ(nullptr, Utility::resolveUrl("tcp://foo:0")); + EXPECT_EQ("1.2.3.4:1234", Utility::resolveUrl("tcp://1.2.3.4:1234")->asString()); + EXPECT_EQ("0.0.0.0:0", Utility::resolveUrl("tcp://0.0.0.0:0")->asString()); + EXPECT_EQ("[::1]:1", Utility::resolveUrl("tcp://[::1]:1")->asString()); + EXPECT_EQ("[1::2:3]:4", Utility::resolveUrl("tcp://[1::2:3]:4")->asString()); + EXPECT_EQ("[a::1]:0", Utility::resolveUrl("tcp://[a::1]:0")->asString()); + EXPECT_EQ("[a:b:c:d::]:0", Utility::resolveUrl("tcp://[a:b:c:d::]:0")->asString()); } TEST(NetworkUtility, getLocalAddress) { EXPECT_NE(nullptr, Utility::getLocalAddress()); } diff --git a/test/config/integration/server.json b/test/config/integration/server.json index c4c9da02d09bd..5d6873ce1799c 100644 --- a/test/config/integration/server.json +++ b/test/config/integration/server.json @@ -1,7 +1,7 @@ { "listeners": [ { - "address": "tcp://127.0.0.1:0", + "address": "tcp://{{ ip_loopback_address }}:0", "use_original_dst": true, "filters": [ { "type": "read", "name": "ratelimit", diff --git a/test/integration/BUILD b/test/integration/BUILD index f2d3249ee7a27..fe50cea934445 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", ], ) 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..22a0606ee3d3a 100644 --- a/test/integration/http2_integration_test.h +++ b/test/integration/http2_integration_test.h @@ -9,7 +9,7 @@ class Http2IntegrationTest : public BaseIntegrationTest, public testing::Test { /** * Global initializer for all integration tests. */ - 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)); @@ -20,7 +20,7 @@ class Http2IntegrationTest : public BaseIntegrationTest, public testing::Test { /** * Global destructor for all integration tests. */ - 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..bf27e291ab2f2 100644 --- a/test/integration/http2_upstream_integration_test.h +++ b/test/integration/http2_upstream_integration_test.h @@ -9,7 +9,7 @@ class Http2UpstreamIntegrationTest : public BaseIntegrationTest, public testing: /** * Global initializer for all integration tests. */ - 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)); @@ -21,7 +21,7 @@ class Http2UpstreamIntegrationTest : public BaseIntegrationTest, public testing: /** * Global destructor for all integration tests. */ - 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..6485a5e79ce0a 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_)); 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_admin_test.cc b/test/integration/integration_admin_test.cc index 372e5dac21f18..fbbf039107267 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -4,11 +4,15 @@ #include "test/integration/integration_test.h" #include "test/integration/utility.h" +#include "test/test_common/environment.h" #include "gtest/gtest.h" #include "spdlog/spdlog.h" -TEST_F(IntegrationTest, HealthCheck) { +INSTANTIATE_TEST_CASE_P(AdminIntegrationTestIpVersions, IntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); + +TEST_P(IntegrationTest, HealthCheck) { BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( lookupPort("http"), "GET", "/healthcheck", "", Http::CodecClient::Type::HTTP1); EXPECT_TRUE(response->complete()); @@ -40,7 +44,7 @@ TEST_F(IntegrationTest, HealthCheck) { EXPECT_STREQ("200", response->headers().Status()->value().c_str()); } -TEST_F(IntegrationTest, AdminLogging) { +TEST_P(IntegrationTest, AdminLogging) { BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( lookupPort("admin"), "GET", "/logging", "", Http::CodecClient::Type::HTTP1); EXPECT_TRUE(response->complete()); @@ -84,7 +88,7 @@ TEST_F(IntegrationTest, AdminLogging) { } } -TEST_F(IntegrationTest, Admin) { +TEST_P(IntegrationTest, Admin) { BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( lookupPort("admin"), "GET", "/", "", Http::CodecClient::Type::HTTP1); EXPECT_TRUE(response->complete()); @@ -141,7 +145,7 @@ TEST_F(IntegrationTest, Admin) { // Successful call to startProfiler requires tcmalloc. #ifdef TCMALLOC -TEST_F(IntegrationTest, AdminCpuProfilerStart) { +TEST_P(IntegrationTest, AdminCpuProfilerStart) { BufferingStreamDecoderPtr response = IntegrationUtil::makeSingleRequest( lookupPort("admin"), "GET", "/cpuprofiler?enable=y", "", Http::CodecClient::Type::HTTP1); EXPECT_TRUE(response->complete()); diff --git a/test/integration/integration_test.cc b/test/integration/integration_test.cc index db2e3132b1469..432d8292558bd 100644 --- a/test/integration/integration_test.cc +++ b/test/integration/integration_test.cc @@ -5,15 +5,19 @@ #include "common/http/header_map_impl.h" #include "test/integration/utility.h" +#include "test/test_common/environment.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" #include "gtest/gtest.h" -TEST_F(IntegrationTest, Echo) { +INSTANTIATE_TEST_CASE_P(IntegrationTestIpVersions, IntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest())); + +TEST_P(IntegrationTest, Echo) { Buffer::OwnedImpl buffer("hello"); std::string response; - RawConnectionDriver connection(lookupPort("echo"), buffer, + RawConnectionDriver connection(lookupPort("echo"), GetParam(), buffer, [&](Network::ClientConnection&, const Buffer::Instance& data) -> void { response.append(TestUtility::bufferToString(data)); @@ -24,21 +28,21 @@ TEST_F(IntegrationTest, Echo) { EXPECT_EQ("hello", response); } -TEST_F(IntegrationTest, RouterNotFound) { testRouterNotFound(Http::CodecClient::Type::HTTP1); } +TEST_P(IntegrationTest, RouterNotFound) { testRouterNotFound(Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterNotFoundBodyNoBuffer) { +TEST_P(IntegrationTest, RouterNotFoundBodyNoBuffer) { testRouterNotFoundWithBody(lookupPort("http"), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterNotFoundBodyBuffer) { +TEST_P(IntegrationTest, RouterNotFoundBodyBuffer) { testRouterNotFoundWithBody(lookupPort("http_buffer"), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterRedirect) { testRouterRedirect(Http::CodecClient::Type::HTTP1); } +TEST_P(IntegrationTest, RouterRedirect) { testRouterRedirect(Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, DrainClose) { testDrainClose(Http::CodecClient::Type::HTTP1); } +TEST_P(IntegrationTest, DrainClose) { testDrainClose(Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, ConnectionClose) { +TEST_P(IntegrationTest, ConnectionClose) { IntegrationCodecClientPtr codec_client; IntegrationStreamDecoderPtr response(new IntegrationStreamDecoder(*dispatcher_)); executeActions({[&]() -> void { @@ -59,77 +63,77 @@ TEST_F(IntegrationTest, ConnectionClose) { EXPECT_STREQ("200", response->headers().Status()->value().c_str()); } -TEST_F(IntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { +TEST_P(IntegrationTest, RouterRequestAndResponseWithBodyNoBuffer) { testRouterRequestAndResponseWithBody(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1, 1024, 512, false); } -TEST_F(IntegrationTest, RouterRequestAndResponseWithBodyBuffer) { +TEST_P(IntegrationTest, RouterRequestAndResponseWithBodyBuffer) { testRouterRequestAndResponseWithBody(makeClientConnection(lookupPort("http_buffer")), Http::CodecClient::Type::HTTP1, 1024, 512, false); } -TEST_F(IntegrationTest, RouterRequestAndResponseWithGiantBodyBuffer) { +TEST_P(IntegrationTest, RouterRequestAndResponseWithGiantBodyBuffer) { testRouterRequestAndResponseWithBody(makeClientConnection(lookupPort("http_buffer")), Http::CodecClient::Type::HTTP1, 4 * 1024 * 1024, 4 * 1024 * 1024, false); } -TEST_F(IntegrationTest, RouterRequestAndResponseLargeHeaderNoBuffer) { +TEST_P(IntegrationTest, RouterRequestAndResponseLargeHeaderNoBuffer) { testRouterRequestAndResponseWithBody(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1, 1024, 512, true); } -TEST_F(IntegrationTest, RouterHeaderOnlyRequestAndResponseNoBuffer) { +TEST_P(IntegrationTest, RouterHeaderOnlyRequestAndResponseNoBuffer) { testRouterHeaderOnlyRequestAndResponse(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterHeaderOnlyRequestAndResponseBuffer) { +TEST_P(IntegrationTest, RouterHeaderOnlyRequestAndResponseBuffer) { testRouterHeaderOnlyRequestAndResponse(makeClientConnection(lookupPort("http_buffer")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterUpstreamDisconnectBeforeRequestcomplete) { +TEST_P(IntegrationTest, RouterUpstreamDisconnectBeforeRequestcomplete) { testRouterUpstreamDisconnectBeforeRequestComplete(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { +TEST_P(IntegrationTest, RouterUpstreamDisconnectBeforeResponseComplete) { testRouterUpstreamDisconnectBeforeResponseComplete(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterDownstreamDisconnectBeforeRequestComplete) { +TEST_P(IntegrationTest, RouterDownstreamDisconnectBeforeRequestComplete) { testRouterDownstreamDisconnectBeforeRequestComplete(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterDownstreamDisconnectBeforeResponseComplete) { +TEST_P(IntegrationTest, RouterDownstreamDisconnectBeforeResponseComplete) { testRouterDownstreamDisconnectBeforeResponseComplete(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { +TEST_P(IntegrationTest, RouterUpstreamResponseBeforeRequestComplete) { testRouterUpstreamResponseBeforeRequestComplete(makeClientConnection(lookupPort("http")), Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, Retry) { testRetry(Http::CodecClient::Type::HTTP1); } +TEST_P(IntegrationTest, Retry) { testRetry(Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, TwoRequests) { testTwoRequests(Http::CodecClient::Type::HTTP1); } +TEST_P(IntegrationTest, TwoRequests) { testTwoRequests(Http::CodecClient::Type::HTTP1); } -TEST_F(IntegrationTest, BadHttpRequest) { testBadHttpRequest(); } +TEST_P(IntegrationTest, BadHttpRequest) { testBadHttpRequest(); } -TEST_F(IntegrationTest, Http10Request) { testHttp10Request(); } +TEST_P(IntegrationTest, Http10Request) { testHttp10Request(); } -TEST_F(IntegrationTest, NoHost) { testNoHost(); } +TEST_P(IntegrationTest, NoHost) { testNoHost(); } -TEST_F(IntegrationTest, BadPath) { testBadPath(); } +TEST_P(IntegrationTest, BadPath) { testBadPath(); } -TEST_F(IntegrationTest, UpstreamProtocolError) { testUpstreamProtocolError(); } +TEST_P(IntegrationTest, UpstreamProtocolError) { testUpstreamProtocolError(); } -TEST_F(IntegrationTest, TcpProxyUpstreamDisconnect) { +TEST_P(IntegrationTest, TcpProxyUpstreamDisconnect) { IntegrationTcpClientPtr tcp_client; FakeRawConnectionPtr fake_upstream_connection; executeActions( @@ -145,7 +149,7 @@ TEST_F(IntegrationTest, TcpProxyUpstreamDisconnect) { EXPECT_EQ("world", tcp_client->data()); } -TEST_F(IntegrationTest, TcpProxyDownstreamDisconnect) { +TEST_P(IntegrationTest, TcpProxyDownstreamDisconnect) { IntegrationTcpClientPtr tcp_client; FakeRawConnectionPtr fake_upstream_connection; executeActions( diff --git a/test/integration/integration_test.h b/test/integration/integration_test.h index 9384fe6ac0473..bf0cfb2ec3428 100644 --- a/test/integration/integration_test.h +++ b/test/integration/integration_test.h @@ -4,24 +4,26 @@ #include "gtest/gtest.h" -class IntegrationTest : public BaseIntegrationTest, public testing::Test { +class IntegrationTest : public BaseIntegrationTest, + public testing::TestWithParam { public: /** - * Global initializer for all integration tests. + * Initializer for individual integration tests. */ - static void SetUpTestCase() { + void SetUp() override { + // TODO(hennna): Upstream IPv6 support. 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", + createTestServer("test/config/integration/server.json", GetParam(), {"echo", "http", "http_buffer", "tcp_proxy", "rds"}); } /** - * Global destructor for all integration tests. + * Destructor for individual integration tests. */ - 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..c339d85e69809 100644 --- a/test/integration/proxy_proto_integration_test.h +++ b/test/integration/proxy_proto_integration_test.h @@ -14,7 +14,7 @@ class ProxyProtoIntegrationTest : public BaseIntegrationTest, public testing::Te /** * Global initializer for all integration tests. */ - 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"}); @@ -23,7 +23,7 @@ class ProxyProtoIntegrationTest : public BaseIntegrationTest, public testing::Te /** * Global destructor for all integration tests. */ - 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..1e2c6947aae43 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -53,9 +53,9 @@ 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()->port(), "GET", "/quitquitquit", "", + Http::CodecClient::Type::HTTP1); EXPECT_TRUE(response->complete()); EXPECT_STREQ("200", response->headers().Status()->value().c_str()); diff --git a/test/integration/ssl_integration_test.cc b/test/integration/ssl_integration_test.cc index 0bda6a6694e25..a056ecc2b7e00 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(); @@ -36,7 +28,7 @@ void SslIntegrationTest::SetUpTestCase() { 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())); + "test/config/integration/server_ssl.json", port_map_)); registerTestServerPorts({"http"}); client_ssl_ctx_plain_ = createClientSslContext(false, false); client_ssl_ctx_alpn_ = createClientSslContext(true, false); @@ -44,7 +36,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..37bd72464bc8a 100644 --- a/test/integration/ssl_integration_test.h +++ b/test/integration/ssl_integration_test.h @@ -40,26 +40,26 @@ class SslIntegrationTest : public BaseIntegrationTest, public testing::Test { /** * Global initializer for all integration tests. */ - static void SetUpTestCase(); + void SetUp() override; /** * Global destructor for all integration tests. */ - 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..308108481e329 100644 --- a/test/integration/uds_integration_test.h +++ b/test/integration/uds_integration_test.h @@ -15,7 +15,7 @@ class UdsIntegrationTest : public BaseIntegrationTest, public testing::Test { /** * Global initializer for all integration tests. */ - 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( @@ -26,7 +26,7 @@ class UdsIntegrationTest : public BaseIntegrationTest, public testing::Test { /** * Global destructor for all integration tests. */ - 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..209d1ad2d86f1 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" @@ -81,17 +82,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, 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..ea09daae71973 100644 --- a/test/integration/utility.h +++ b/test/integration/utility.h @@ -51,7 +51,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, Network::Address::IpVersion version, + Buffer::Instance& initial_data, ReadCallback data_callback); ~RawConnectionDriver(); void run(); void close(); diff --git a/test/test_common/BUILD b/test/test_common/BUILD index dd9d3a99a1a75..c28731ba1ba23 100644 --- a/test/test_common/BUILD +++ b/test/test_common/BUILD @@ -19,6 +19,7 @@ envoy_cc_test_library( srcs = ["environment.cc"], hdrs = ["environment.h"], deps = [ + ":network_utility_lib", "//include/envoy/server:options_interface", "//source/common/common:assert_lib", "//source/common/common:compiler_requirements_lib", diff --git a/test/test_common/environment.cc b/test/test_common/environment.cc index 64f79d18f6afd..22f4f0e01043c 100644 --- a/test/test_common/environment.cc +++ b/test/test_common/environment.cc @@ -15,6 +15,8 @@ #include "server/options_impl.h" +#include "test/test_common/network_utility.h" + #include "spdlog/spdlog.h" namespace { @@ -109,8 +111,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); @@ -131,6 +140,12 @@ 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 32861f3ab342d..313c804845942 100644 --- a/test/test_common/network_utility.cc +++ b/test/test_common/network_utility.cc @@ -80,6 +80,13 @@ Address::InstanceConstSharedPtr findOrCheckFreePort(const std::string& addr_port return instance; } +const std::string getLoopbackAddressUrlString(const Address::IpVersion version) { + if (version == Address::IpVersion::v6) { + return std::string("[::1]"); + } + return std::string("127.0.0.1"); +} + Address::InstanceConstSharedPtr getSomeLoopbackAddress(Address::IpVersion version) { if (version == Address::IpVersion::v4) { // Pick a random address in 127.0.0.0/8. diff --git a/test/test_common/network_utility.h b/test/test_common/network_utility.h index 2da5b4dc01e2a..29ed3c1817e21 100644 --- a/test/test_common/network_utility.h +++ b/test/test_common/network_utility.h @@ -32,6 +32,13 @@ Address::InstanceConstSharedPtr findOrCheckFreePort(Address::InstanceConstShared Address::InstanceConstSharedPtr findOrCheckFreePort(const std::string& addr_port, Address::SocketType type); +/** + * Get a URL ready IP loopback address as a string. + * @param version IP address version of loopback address. + * @return std::string URL ready loopback address as a string. + */ +const std::string getLoopbackAddressUrlString(const Address::IpVersion version); + /** * Returns a loopback address for the specified IP version. For IPv6 this is always the same, * but for IPv4 it is anywhere in the range 127.0.0.0/8.