face: support IPv6-only WebSocket channels
Change-Id: Ib9ba27d04611c13882e50995d701fc1b707cea5f
Refs: #4710
diff --git a/tests/daemon/face/websocket-channel-fixture.hpp b/tests/daemon/face/websocket-channel-fixture.hpp
new file mode 100644
index 0000000..5500281
--- /dev/null
+++ b/tests/daemon/face/websocket-channel-fixture.hpp
@@ -0,0 +1,163 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TESTS_DAEMON_FACE_WEBSOCKET_CHANNEL_FIXTURE_HPP
+#define NFD_TESTS_DAEMON_FACE_WEBSOCKET_CHANNEL_FIXTURE_HPP
+
+#include "face/websocket-channel.hpp"
+
+#include "channel-fixture.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+class WebSocketChannelFixture : public ChannelFixture<WebSocketChannel, websocket::Endpoint>
+{
+protected:
+ unique_ptr<WebSocketChannel>
+ makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
+ {
+ if (port == 0)
+ port = getNextPort();
+
+ return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
+ }
+
+ void
+ listen(const boost::asio::ip::address& addr,
+ const time::milliseconds& pingInterval = 10_s,
+ const time::milliseconds& pongTimeout = 1_s)
+ {
+ listenerEp = websocket::Endpoint(addr, 20030);
+ listenerChannel = makeChannel(addr, 20030);
+ listenerChannel->setPingInterval(pingInterval);
+ listenerChannel->setPongTimeout(pongTimeout);
+ listenerChannel->listen(bind(&WebSocketChannelFixture::listenerOnFaceCreated, this, _1));
+ }
+
+ void
+ clientConnect(websocket::Client& client)
+ {
+ client.clear_access_channels(websocketpp::log::alevel::all);
+ client.clear_error_channels(websocketpp::log::elevel::all);
+
+ client.init_asio(&g_io);
+ client.set_open_handler(bind(&WebSocketChannelFixture::clientHandleOpen, this, _1));
+ client.set_message_handler(bind(&WebSocketChannelFixture::clientHandleMessage, this, _1, _2));
+ client.set_ping_handler(bind(&WebSocketChannelFixture::clientHandlePing, this, _1, _2));
+
+ websocketpp::lib::error_code ec;
+ auto con = client.get_connection(FaceUri(listenerEp, "ws").toString(), ec);
+ BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
+
+ client.connect(con);
+ }
+
+ void
+ initialize(const boost::asio::ip::address& addr,
+ const time::milliseconds& pingInterval = 10_s,
+ const time::milliseconds& pongTimeout = 1_s)
+ {
+ listen(addr, pingInterval, pongTimeout);
+ clientConnect(client);
+ BOOST_REQUIRE_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
+ 1_s), LimitedIo::EXCEED_OPS);
+ BOOST_REQUIRE_EQUAL(listenerChannel->size(), 1);
+ }
+
+ void
+ clientSendInterest(const Interest& interest)
+ {
+ const Block& payload = interest.wireEncode();
+ client.send(clientHandle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
+ }
+
+private:
+ void
+ listenerOnFaceCreated(const shared_ptr<Face>& newFace)
+ {
+ BOOST_REQUIRE(newFace != nullptr);
+ newFace->afterReceiveInterest.connect(bind(&WebSocketChannelFixture::faceAfterReceiveInterest, this, _1));
+ connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
+ listenerFaces.push_back(newFace);
+ limitedIo.afterOp();
+ }
+
+ void
+ faceAfterReceiveInterest(const Interest& interest)
+ {
+ faceReceivedInterests.push_back(interest);
+ limitedIo.afterOp();
+ }
+
+ void
+ clientHandleOpen(websocketpp::connection_hdl hdl)
+ {
+ clientHandle = hdl;
+ limitedIo.afterOp();
+ }
+
+ void
+ clientHandleMessage(websocketpp::connection_hdl, websocket::Client::message_ptr msg)
+ {
+ clientReceivedMessages.push_back(msg->get_payload());
+ limitedIo.afterOp();
+ }
+
+ bool
+ clientHandlePing(websocketpp::connection_hdl, std::string)
+ {
+ auto now = time::steady_clock::now();
+ if (m_prevPingRecvTime != time::steady_clock::TimePoint()) {
+ measuredPingInterval = now - m_prevPingRecvTime;
+ }
+ m_prevPingRecvTime = now;
+
+ limitedIo.afterOp();
+ return clientShouldPong;
+ }
+
+protected:
+ std::vector<Interest> faceReceivedInterests;
+
+ websocket::Client client;
+ websocketpp::connection_hdl clientHandle;
+ std::vector<std::string> clientReceivedMessages;
+
+ time::steady_clock::Duration measuredPingInterval;
+ // set clientShouldPong to false to disable the pong response,
+ // which will eventually cause a timeout in listenerChannel
+ bool clientShouldPong = true;
+
+private:
+ time::steady_clock::TimePoint m_prevPingRecvTime;
+};
+
+} // namespace tests
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_WEBSOCKET_CHANNEL_FIXTURE_HPP
diff --git a/tests/daemon/face/websocket-channel.t.cpp b/tests/daemon/face/websocket-channel.t.cpp
index 5c4f433..d4d9c74 100644
--- a/tests/daemon/face/websocket-channel.t.cpp
+++ b/tests/daemon/face/websocket-channel.t.cpp
@@ -23,154 +23,35 @@
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "face/websocket-channel.hpp"
+#include "websocket-channel-fixture.hpp"
#include "face/websocket-transport.hpp"
-#include "channel-fixture.hpp"
#include "test-ip.hpp"
+#include <boost/mpl/vector.hpp>
namespace nfd {
namespace face {
namespace tests {
-namespace ip = boost::asio::ip;
-
-class WebSocketChannelFixture : public ChannelFixture<WebSocketChannel, websocket::Endpoint>
-{
-protected:
- unique_ptr<WebSocketChannel>
- makeChannel(const ip::address& addr, uint16_t port = 0) final
- {
- if (port == 0)
- port = getNextPort();
-
- return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
- }
-
- void
- listen(const ip::address& addr,
- const time::milliseconds& pingInterval = 10_s,
- const time::milliseconds& pongTimeout = 1_s)
- {
- listenerEp = websocket::Endpoint(addr, 20030);
- listenerChannel = makeChannel(addr, 20030);
- listenerChannel->setPingInterval(pingInterval);
- listenerChannel->setPongTimeout(pongTimeout);
- listenerChannel->listen(bind(&WebSocketChannelFixture::listenerOnFaceCreated, this, _1));
- }
-
- void
- clientConnect(websocket::Client& client)
- {
- client.clear_access_channels(websocketpp::log::alevel::all);
- client.clear_error_channels(websocketpp::log::elevel::all);
-
- client.init_asio(&g_io);
- client.set_open_handler(bind(&WebSocketChannelFixture::clientHandleOpen, this, _1));
- client.set_message_handler(bind(&WebSocketChannelFixture::clientHandleMessage, this, _1, _2));
- client.set_ping_handler(bind(&WebSocketChannelFixture::clientHandlePing, this, _1, _2));
-
- std::string uri = "ws://" + listenerEp.address().to_string() + ":" + to_string(listenerEp.port());
- websocketpp::lib::error_code ec;
- auto con = client.get_connection(uri, ec);
- BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
-
- client.connect(con);
- }
-
- void
- initialize(const ip::address& addr,
- const time::milliseconds& pingInterval = 10_s,
- const time::milliseconds& pongTimeout = 1_s)
- {
- listen(addr, pingInterval, pongTimeout);
- clientConnect(client);
- BOOST_REQUIRE_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
- 1_s), LimitedIo::EXCEED_OPS);
- BOOST_REQUIRE_EQUAL(listenerChannel->size(), 1);
- }
-
- void
- clientSendInterest(const Interest& interest)
- {
- const Block& payload = interest.wireEncode();
- client.send(clientHandle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
- }
-
-private:
- void
- listenerOnFaceCreated(const shared_ptr<Face>& newFace)
- {
- BOOST_REQUIRE(newFace != nullptr);
- newFace->afterReceiveInterest.connect(bind(&WebSocketChannelFixture::faceAfterReceiveInterest, this, _1));
- connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
- listenerFaces.push_back(newFace);
- limitedIo.afterOp();
- }
-
- void
- faceAfterReceiveInterest(const Interest& interest)
- {
- faceReceivedInterests.push_back(interest);
- limitedIo.afterOp();
- }
-
- void
- clientHandleOpen(websocketpp::connection_hdl hdl)
- {
- clientHandle = hdl;
- limitedIo.afterOp();
- }
-
- void
- clientHandleMessage(websocketpp::connection_hdl, websocket::Client::message_ptr msg)
- {
- clientReceivedMessages.push_back(msg->get_payload());
- limitedIo.afterOp();
- }
-
- bool
- clientHandlePing(websocketpp::connection_hdl, std::string)
- {
- auto now = time::steady_clock::now();
- if (m_prevPingRecvTime != time::steady_clock::TimePoint()) {
- measuredPingInterval = now - m_prevPingRecvTime;
- }
- m_prevPingRecvTime = now;
-
- limitedIo.afterOp();
- return clientShouldPong;
- }
-
-protected:
- std::vector<Interest> faceReceivedInterests;
-
- websocket::Client client;
- websocketpp::connection_hdl clientHandle;
- std::vector<std::string> clientReceivedMessages;
-
- time::steady_clock::Duration measuredPingInterval;
- // set clientShouldPong to false to disable the pong response,
- // which will eventually cause a timeout in listenerChannel
- bool clientShouldPong = true;
-
-private:
- time::steady_clock::TimePoint m_prevPingRecvTime;
-};
-
BOOST_AUTO_TEST_SUITE(Face)
BOOST_FIXTURE_TEST_SUITE(TestWebSocketChannel, WebSocketChannelFixture)
-BOOST_AUTO_TEST_CASE(Uri)
+using AddressFamilies = boost::mpl::vector<
+ std::integral_constant<AddressFamily, AddressFamily::V4>,
+ std::integral_constant<AddressFamily, AddressFamily::V6>>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Uri, F, AddressFamilies)
{
- websocket::Endpoint ep(ip::address_v4::loopback(), 20070);
- auto channel = makeChannel(ep.address(), ep.port());
+ using Address = typename IpAddressFromFamily<F::value>::type;
+ websocket::Endpoint ep(Address::loopback(), 20070);
+ auto channel = this->makeChannel(ep.address(), ep.port());
BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep, "ws"));
}
-BOOST_AUTO_TEST_CASE(Listen)
+BOOST_AUTO_TEST_CASE_TEMPLATE(Listen, F, AddressFamilies)
{
- auto channel = makeChannel(ip::address_v4());
+ using Address = typename IpAddressFromFamily<F::value>::type;
+ auto channel = this->makeChannel(Address());
BOOST_CHECK_EQUAL(channel->isListening(), false);
channel->listen(nullptr);
@@ -181,9 +62,9 @@
BOOST_CHECK_EQUAL(channel->isListening(), true);
}
-BOOST_AUTO_TEST_CASE(MultipleAccepts)
+BOOST_AUTO_TEST_CASE_TEMPLATE(MultipleAccepts, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
this->listen(address);
@@ -212,12 +93,12 @@
}
}
-BOOST_AUTO_TEST_CASE(Send)
+BOOST_AUTO_TEST_CASE_TEMPLATE(Send, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
this->initialize(address);
- auto transport = listenerFaces.front()->getTransport();
+ auto transport = listenerFaces.at(0)->getTransport();
Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
transport->send(Transport::Packet(Block(pkt1)));
@@ -240,9 +121,9 @@
pkt2.begin(), pkt2.end());
}
-BOOST_AUTO_TEST_CASE(Receive)
+BOOST_AUTO_TEST_CASE_TEMPLATE(Receive, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
this->initialize(address);
@@ -251,11 +132,11 @@
auto interest1 = makeInterest("ndn:/TpnzGvW9R");
auto interest2 = makeInterest("ndn:/QWiIMfj5sL");
- clientSendInterest(*interest1);
+ this->clientSendInterest(*interest1);
BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
1_s), LimitedIo::EXCEED_OPS);
- clientSendInterest(*interest2);
+ this->clientSendInterest(*interest2);
BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
1_s), LimitedIo::EXCEED_OPS);
@@ -264,19 +145,19 @@
BOOST_CHECK_EQUAL(faceReceivedInterests[1].getName(), interest2->getName());
}
-BOOST_AUTO_TEST_CASE(FaceClosure)
+BOOST_AUTO_TEST_CASE_TEMPLATE(FaceClosure, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
this->initialize(address);
- listenerFaces.front()->close();
+ listenerFaces.at(0)->close();
BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
}
-BOOST_AUTO_TEST_CASE(RemoteClose)
+BOOST_AUTO_TEST_CASE_TEMPLATE(RemoteClose, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
this->initialize(address);
@@ -286,9 +167,9 @@
BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
}
-BOOST_AUTO_TEST_CASE(SetPingInterval)
+BOOST_AUTO_TEST_CASE_TEMPLATE(SetPingInterval, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
const auto pingInterval = 1200_ms;
this->initialize(address, pingInterval);
@@ -299,18 +180,18 @@
BOOST_CHECK_GE(measuredPingInterval, pingInterval * 0.9);
}
-BOOST_AUTO_TEST_CASE(SetPongTimeOut)
+BOOST_AUTO_TEST_CASE_TEMPLATE(SetPongTimeOut, F, AddressFamilies)
{
- auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+ auto address = getTestIp(F::value, AddressScope::Loopback);
SKIP_IF_IP_UNAVAILABLE(address);
this->initialize(address, 600_ms, 300_ms);
- clientShouldPong = false;
+ this->clientShouldPong = false;
BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, faceClosedSignal
2_s), LimitedIo::EXCEED_OPS);
BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
- auto transport = static_cast<WebSocketTransport*>(listenerFaces.front()->getTransport());
+ auto transport = static_cast<WebSocketTransport*>(listenerFaces.at(0)->getTransport());
BOOST_CHECK(transport->getState() == TransportState::FAILED ||
transport->getState() == TransportState::CLOSED);
BOOST_CHECK_GE(transport->getCounters().nOutPings, 1);
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index 5c85e2e..1234737 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -61,10 +61,10 @@
parseConfig(CONFIG, true);
parseConfig(CONFIG, false);
- checkChannelListEqual(factory, {"ws://[::]:9696"});
+ checkChannelListEqual(factory, {"ws://0.0.0.0:9696", "ws://[::]:9696"});
auto channels = factory.getChannels();
BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
- [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
+ [] (const auto& ch) { return ch->isListening(); }));
}
BOOST_AUTO_TEST_CASE(DisableListen)
@@ -100,8 +100,10 @@
}
)CONFIG";
- BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
- BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+ parseConfig(CONFIG, true);
+ parseConfig(CONFIG, false);
+
+ checkChannelListEqual(factory, {"ws://[::]:7001"});
}
BOOST_AUTO_TEST_CASE(DisableV6)
@@ -124,7 +126,7 @@
checkChannelListEqual(factory, {"ws://0.0.0.0:7001"});
}
-BOOST_AUTO_TEST_CASE(ChangeEndpoint)
+BOOST_AUTO_TEST_CASE(ChangePort)
{
const std::string CONFIG1 = R"CONFIG(
face_system
@@ -137,7 +139,7 @@
)CONFIG";
parseConfig(CONFIG1, false);
- checkChannelListEqual(factory, {"ws://[::]:9001"});
+ checkChannelListEqual(factory, {"ws://0.0.0.0:9001", "ws://[::]:9001"});
const std::string CONFIG2 = R"CONFIG(
face_system
@@ -150,7 +152,8 @@
)CONFIG";
parseConfig(CONFIG2, false);
- checkChannelListEqual(factory, {"ws://[::]:9001", "ws://[::]:9002"});
+ checkChannelListEqual(factory, {"ws://0.0.0.0:9001", "ws://[::]:9001",
+ "ws://0.0.0.0:9002", "ws://[::]:9002"});
}
BOOST_AUTO_TEST_CASE(Omitted)
@@ -275,6 +278,21 @@
checkChannelListEqual(factory, expected);
}
+BOOST_AUTO_TEST_CASE(CreateChannel)
+{
+ auto channel1 = createChannel("127.0.0.1", "20070");
+ auto channel1a = createChannel("127.0.0.1", "20070");
+ BOOST_CHECK_EQUAL(channel1, channel1a);
+ BOOST_CHECK_EQUAL(channel1->getUri().toString(), "ws://127.0.0.1:20070");
+
+ auto channel2 = createChannel("127.0.0.1", "20071");
+ BOOST_CHECK_NE(channel1, channel2);
+
+ auto channel3 = createChannel("::1", "20071");
+ BOOST_CHECK_NE(channel2, channel3);
+ BOOST_CHECK_EQUAL(channel3->getUri().toString(), "ws://[::1]:20071");
+}
+
BOOST_AUTO_TEST_CASE(UnsupportedCreateFace)
{
createFace(factory,