face: WebSocketTransport
Change-Id: I28db9cd6f828e4a9fe5895bb7a6152cca5baa9fc
Refs: #3169, #3160
diff --git a/daemon/face/websocket-channel.cpp b/daemon/face/websocket-channel.cpp
index 7997b1e..520bb7d 100644
--- a/daemon/face/websocket-channel.cpp
+++ b/daemon/face/websocket-channel.cpp
@@ -24,10 +24,9 @@
*/
#include "websocket-channel.hpp"
+#include "generic-link-service.hpp"
+#include "websocket-transport.hpp"
#include "core/global-io.hpp"
-#include "core/scheduler.hpp"
-
-#include <boost/date_time/posix_time/posix_time.hpp>
namespace nfd {
@@ -35,23 +34,23 @@
WebSocketChannel::WebSocketChannel(const websocket::Endpoint& localEndpoint)
: m_localEndpoint(localEndpoint)
- , m_isListening(false)
, m_pingInterval(10000)
{
setUri(FaceUri(m_localEndpoint, "ws"));
- // Setup WebSocket server
+ // Be quiet
m_server.clear_access_channels(websocketpp::log::alevel::all);
- m_server.clear_error_channels(websocketpp::log::alevel::all);
+ m_server.clear_error_channels(websocketpp::log::elevel::all);
- m_server.set_message_handler(bind(&WebSocketChannel::handleMessage, this, _1, _2));
+ // Setup WebSocket server
+ m_server.init_asio(&getGlobalIoService());
m_server.set_open_handler(bind(&WebSocketChannel::handleOpen, this, _1));
m_server.set_close_handler(bind(&WebSocketChannel::handleClose, this, _1));
- m_server.init_asio(&getGlobalIoService());
+ m_server.set_message_handler(bind(&WebSocketChannel::handleMessage, this, _1, _2));
// Detect disconnections using ping-pong messages
- m_server.set_pong_handler(bind(&WebSocketChannel::handlePong, this, _1, _2));
- m_server.set_pong_timeout_handler(bind(&WebSocketChannel::handlePongTimeout, this, _1, _2));
+ m_server.set_pong_handler(bind(&WebSocketChannel::handlePong, this, _1));
+ m_server.set_pong_timeout_handler(bind(&WebSocketChannel::handlePongTimeout, this, _1));
// Always set SO_REUSEADDR flag
m_server.set_reuse_addr(true);
@@ -60,32 +59,40 @@
void
WebSocketChannel::setPingInterval(time::milliseconds interval)
{
+ BOOST_ASSERT(!m_server.is_listening());
+
m_pingInterval = interval;
}
void
WebSocketChannel::setPongTimeout(time::milliseconds timeout)
{
+ BOOST_ASSERT(!m_server.is_listening());
+
m_server.set_pong_timeout(static_cast<long>(timeout.count()));
}
void
-WebSocketChannel::handlePongTimeout(websocketpp::connection_hdl hdl, std::string msg)
+WebSocketChannel::handlePongTimeout(websocketpp::connection_hdl hdl)
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
- NFD_LOG_TRACE(__func__ << ": " << it->second->getRemoteUri());
- it->second->close();
- m_channelFaces.erase(it);
+ static_cast<face::WebSocketTransport*>(it->second->getLpFace()->getTransport())->handlePongTimeout();
+ }
+ else {
+ NFD_LOG_WARN("Pong timeout on unknown transport");
}
}
void
-WebSocketChannel::handlePong(websocketpp::connection_hdl hdl, std::string msg)
+WebSocketChannel::handlePong(websocketpp::connection_hdl hdl)
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
- NFD_LOG_TRACE("Pong from " << it->second->getRemoteUri());
+ static_cast<face::WebSocketTransport*>(it->second->getLpFace()->getTransport())->handlePong();
+ }
+ else {
+ NFD_LOG_WARN("Pong received on unknown transport");
}
}
@@ -95,62 +102,30 @@
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
- it->second->handleReceive(msg->get_payload());
+ static_cast<face::WebSocketTransport*>(it->second->getLpFace()->getTransport())->receiveMessage(msg->get_payload());
+ }
+ else {
+ NFD_LOG_WARN("Message received on unknown transport");
}
}
void
WebSocketChannel::handleOpen(websocketpp::connection_hdl hdl)
{
- try {
- std::string remote = "wsclient://" + m_server.get_con_from_hdl(hdl)->get_remote_endpoint();
- auto face = make_shared<WebSocketFace>(FaceUri(remote), this->getUri(),
- hdl, ref(m_server));
- m_onFaceCreatedCallback(face);
- m_channelFaces[hdl] = face;
- // Schedule ping message
- scheduler::EventId pingEvent = scheduler::schedule(m_pingInterval,
- bind(&WebSocketChannel::sendPing,
- this, hdl));
- face->setPingEventId(pingEvent);
- }
- catch (const FaceUri::Error& e) {
- NFD_LOG_WARN(e.what());
- websocketpp::lib::error_code ec;
- m_server.close(hdl, websocketpp::close::status::normal, "closed by channel", ec);
- // ignore error on close
- }
- catch (const websocketpp::exception& e) {
- NFD_LOG_WARN("Cannot get remote connection: " << e.what());
- websocketpp::lib::error_code ec;
- m_server.close(hdl, websocketpp::close::status::normal, "closed by channel", ec);
- // ignore error on close
- }
-}
+ auto linkService = make_unique<face::GenericLinkService>();
+ auto transport = make_unique<face::WebSocketTransport>(hdl, ref(m_server), m_pingInterval);
+ auto lpFace = make_unique<face::LpFace>(std::move(linkService), std::move(transport));
+ auto face = make_shared<face::LpFaceWrapper>(std::move(lpFace));
-void
-WebSocketChannel::sendPing(websocketpp::connection_hdl hdl)
-{
- auto it = m_channelFaces.find(hdl);
- if (it != m_channelFaces.end()) {
- NFD_LOG_TRACE("Sending ping to " << it->second->getRemoteUri());
-
- websocketpp::lib::error_code ec;
- m_server.ping(hdl, "NFD-WebSocket", ec);
- if (ec)
- {
- NFD_LOG_WARN("Failed to ping " << it->second->getRemoteUri() << ": " << ec.message());
- it->second->close();
- m_channelFaces.erase(it);
- return;
+ face->getLpFace()->afterStateChange.connect(
+ [this, hdl] (face::FaceState oldState, face::FaceState newState) {
+ if (newState == face::FaceState::CLOSED) {
+ m_channelFaces.erase(hdl);
}
+ });
+ m_channelFaces[hdl] = face;
- // Schedule next ping message
- scheduler::EventId pingEvent = scheduler::schedule(m_pingInterval,
- bind(&WebSocketChannel::sendPing,
- this, hdl));
- it->second->setPingEventId(pingEvent);
- }
+ m_onFaceCreatedCallback(face);
}
void
@@ -158,20 +133,20 @@
{
auto it = m_channelFaces.find(hdl);
if (it != m_channelFaces.end()) {
- NFD_LOG_TRACE(__func__ << ": " << it->second->getRemoteUri());
- it->second->close();
- m_channelFaces.erase(it);
+ it->second->getLpFace()->close();
+ }
+ else {
+ NFD_LOG_WARN("Close on unknown transport");
}
}
void
WebSocketChannel::listen(const FaceCreatedCallback& onFaceCreated)
{
- if (isListening()) {
+ if (m_server.is_listening()) {
NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
return;
}
- m_isListening = true;
m_onFaceCreatedCallback = onFaceCreated;
m_server.listen(m_localEndpoint);
diff --git a/daemon/face/websocket-channel.hpp b/daemon/face/websocket-channel.hpp
index 027085c..47eda8c 100644
--- a/daemon/face/websocket-channel.hpp
+++ b/daemon/face/websocket-channel.hpp
@@ -27,7 +27,8 @@
#define NFD_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
#include "channel.hpp"
-#include "websocket-face.hpp"
+#include "lp-face-wrapper.hpp"
+#include "websocketpp.hpp"
namespace nfd {
@@ -46,9 +47,7 @@
*
* To enable creation of faces upon incoming connections,
* one needs to explicitly call WebSocketChannel::listen method.
- * The created socket is bound to the localEndpoint.
- *
- * \throw WebSocketChannel::Error if bind on the socket fails
+ * The created channel is bound to the localEndpoint.
*/
explicit
WebSocketChannel(const websocket::Endpoint& localEndpoint);
@@ -56,9 +55,8 @@
/**
* \brief Enable listening on the local endpoint, accept connections,
* and create faces when remote host makes a connection
- * \param onFaceCreated Callback to notify successful creation of the face
*
- * \throws WebSocketChannel::Error if called multiple times
+ * \param onFaceCreated Callback to notify successful creation of a face
*/
void
listen(const FaceCreatedCallback& onFaceCreated);
@@ -73,24 +71,26 @@
isListening() const;
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ /** \pre listen hasn't been invoked
+ */
void
setPingInterval(time::milliseconds interval);
+ /** \pre listen hasn't been invoked
+ */
void
setPongTimeout(time::milliseconds timeout);
+ void
+ handlePong(websocketpp::connection_hdl hdl);
+
+ void
+ handlePongTimeout(websocketpp::connection_hdl hdl);
+
private:
void
- sendPing(websocketpp::connection_hdl hdl);
-
- void
- handlePong(websocketpp::connection_hdl hdl, std::string msg);
-
- void
- handlePongTimeout(websocketpp::connection_hdl hdl, std::string msg);
-
- void
- handleMessage(websocketpp::connection_hdl hdl, websocket::Server::message_ptr msg);
+ handleMessage(websocketpp::connection_hdl hdl,
+ websocket::Server::message_ptr msg);
void
handleOpen(websocketpp::connection_hdl hdl);
@@ -102,26 +102,17 @@
websocket::Endpoint m_localEndpoint;
websocket::Server m_server;
- std::map<websocketpp::connection_hdl, shared_ptr<WebSocketFace>,
+ std::map<websocketpp::connection_hdl, shared_ptr<face::LpFaceWrapper>,
std::owner_less<websocketpp::connection_hdl>> m_channelFaces;
- /**
- * Callback for face creation
- */
FaceCreatedCallback m_onFaceCreatedCallback;
-
- /**
- * \brief If true, it means the function listen has already been called
- */
- bool m_isListening;
-
time::milliseconds m_pingInterval;
};
inline bool
WebSocketChannel::isListening() const
{
- return m_isListening;
+ return m_server.is_listening();
}
} // namespace nfd
diff --git a/daemon/face/websocket-face.cpp b/daemon/face/websocket-face.cpp
deleted file mode 100644
index 3b94e6e..0000000
--- a/daemon/face/websocket-face.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015, 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/>.
- */
-
-#include "websocket-face.hpp"
-
-namespace nfd {
-
-NFD_LOG_INIT("WebSocketFace");
-
-WebSocketFace::WebSocketFace(const FaceUri& remoteUri, const FaceUri& localUri,
- websocketpp::connection_hdl hdl,
- websocket::Server& server)
- : Face(remoteUri, localUri)
- , m_handle(hdl)
- , m_server(server)
- , m_closed(false)
-{
- NFD_LOG_FACE_INFO("Creating face");
- this->setPersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
-}
-
-void
-WebSocketFace::sendInterest(const Interest& interest)
-{
- if (m_closed)
- return;
-
- NFD_LOG_FACE_TRACE(__func__);
-
- this->emitSignal(onSendInterest, interest);
-
- const Block& payload = interest.wireEncode();
- this->getMutableCounters().getNOutBytes() += payload.size();
-
- websocketpp::lib::error_code ec;
- m_server.send(m_handle, payload.wire(), payload.size(),
- websocketpp::frame::opcode::binary, ec);
- if (ec)
- NFD_LOG_FACE_WARN("Failed to send Interest: " << ec.message());
-}
-
-void
-WebSocketFace::sendData(const Data& data)
-{
- if (m_closed)
- return;
-
- NFD_LOG_FACE_TRACE(__func__);
-
- this->emitSignal(onSendData, data);
-
- const Block& payload = data.wireEncode();
- this->getMutableCounters().getNOutBytes() += payload.size();
-
- websocketpp::lib::error_code ec;
- m_server.send(m_handle, payload.wire(), payload.size(),
- websocketpp::frame::opcode::binary, ec);
- if (ec)
- NFD_LOG_FACE_WARN("Failed to send Data: " << ec.message());
-}
-
-void
-WebSocketFace::close()
-{
- if (m_closed)
- return;
-
- NFD_LOG_FACE_INFO("Closing face");
-
- m_closed = true;
- scheduler::cancel(m_pingEventId);
- websocketpp::lib::error_code ec;
- m_server.close(m_handle, websocketpp::close::status::normal, "closed by nfd", ec);
- // ignore error on close
- fail("Face closed");
-}
-
-void
-WebSocketFace::handleReceive(const std::string& msg)
-{
- // Copy message into Face internal buffer
- if (msg.size() > ndn::MAX_NDN_PACKET_SIZE)
- {
- NFD_LOG_FACE_WARN("Received WebSocket message is too big (" << msg.size() << " bytes)");
- return;
- }
-
- NFD_LOG_FACE_TRACE("Received: " << msg.size() << " bytes");
- this->getMutableCounters().getNInBytes() += msg.size();
-
- // Try to parse message data
- bool isOk = false;
- Block element;
- std::tie(isOk, element) = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.c_str()),
- msg.size());
- if (!isOk)
- {
- NFD_LOG_FACE_WARN("Received block is invalid or too large to process");
- return;
- }
-
- if (!this->decodeAndDispatchInput(element))
- {
- NFD_LOG_FACE_WARN("Received unrecognized TLV block of type " << element.type());
- // ignore unknown packet and proceed
- }
-}
-
-} // namespace nfd
diff --git a/daemon/face/websocket-face.hpp b/daemon/face/websocket-face.hpp
deleted file mode 100644
index 3c7fbf1..0000000
--- a/daemon/face/websocket-face.hpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2015, 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_DAEMON_FACE_WEBSOCKET_FACE_HPP
-#define NFD_DAEMON_FACE_WEBSOCKET_FACE_HPP
-
-#include "face.hpp"
-#include "core/scheduler.hpp"
-
-#ifndef HAVE_WEBSOCKET
-#error "Cannot include this file when WebSocket support is not enabled"
-#endif // HAVE_WEBSOCKET
-
-#include "websocketpp.hpp"
-
-namespace nfd {
-
-namespace websocket {
-typedef websocketpp::server<websocketpp::config::asio> Server;
-} // namespace websocket
-
-/**
- * \brief Implementation of Face abstraction that uses WebSocket
- * as underlying transport mechanism
- */
-class WebSocketFace : public Face
-{
-public:
- WebSocketFace(const FaceUri& remoteUri, const FaceUri& localUri,
- websocketpp::connection_hdl hdl, websocket::Server& server);
-
- // from Face
- void
- sendInterest(const Interest& interest) DECL_OVERRIDE;
-
- void
- sendData(const Data& data) DECL_OVERRIDE;
-
- void
- close() DECL_OVERRIDE;
-
- void
- setPingEventId(scheduler::EventId& id)
- {
- m_pingEventId = id;
- }
-
-protected:
- // friend because it needs to invoke protected handleReceive
- friend class WebSocketChannel;
-
- void
- handleReceive(const std::string& msg);
-
-private:
- websocketpp::connection_hdl m_handle;
- websocket::Server& m_server;
- scheduler::EventId m_pingEventId;
- bool m_closed;
-};
-
-} // namespace nfd
-
-#endif // NFD_DAEMON_FACE_WEBSOCKET_FACE_HPP
diff --git a/daemon/face/websocket-transport.cpp b/daemon/face/websocket-transport.cpp
new file mode 100644
index 0000000..4fbed81
--- /dev/null
+++ b/daemon/face/websocket-transport.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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/>.
+ */
+
+#include "websocket-transport.hpp"
+
+namespace nfd {
+namespace face {
+
+NFD_LOG_INIT("WebSocketTransport");
+
+WebSocketTransport::WebSocketTransport(websocketpp::connection_hdl hdl,
+ websocket::Server& server,
+ time::milliseconds pingInterval)
+ : m_handle(hdl)
+ , m_server(server)
+ , m_pingInterval(pingInterval)
+{
+ const auto& sock = m_server.get_con_from_hdl(hdl)->get_socket();
+ this->setLocalUri(FaceUri(sock.local_endpoint(), "ws"));
+ this->setRemoteUri(FaceUri(sock.remote_endpoint(), "wsclient"));
+
+ if (sock.local_endpoint().address().is_loopback() &&
+ sock.remote_endpoint().address().is_loopback())
+ this->setScope(ndn::nfd::FACE_SCOPE_LOCAL);
+ else
+ this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
+
+ this->setPersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+ this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+ this->setMtu(MTU_UNLIMITED);
+
+ this->schedulePing();
+
+ NFD_LOG_FACE_INFO("Creating transport");
+}
+
+void WebSocketTransport::beforeChangePersistency(ndn::nfd::FacePersistency newPersistency)
+{
+ if (newPersistency != ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
+ BOOST_THROW_EXCEPTION(
+ std::invalid_argument("WebSocketTransport supports only FACE_PERSISTENCY_ON_DEMAND"));
+ }
+}
+
+void
+WebSocketTransport::doSend(Transport::Packet&& packet)
+{
+ NFD_LOG_FACE_TRACE(__func__);
+
+ websocketpp::lib::error_code error;
+ m_server.send(m_handle, packet.packet.wire(), packet.packet.size(),
+ websocketpp::frame::opcode::binary, error);
+ if (error)
+ return processErrorCode(error);
+
+ NFD_LOG_FACE_TRACE("Successfully sent: " << packet.packet.size() << " bytes");
+}
+
+void
+WebSocketTransport::receiveMessage(const std::string& msg)
+{
+ NFD_LOG_FACE_TRACE("Received: " << msg.size() << " bytes");
+
+ bool isOk = false;
+ Block element;
+ std::tie(isOk, element) = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.c_str()), msg.size());
+ if (!isOk) {
+ NFD_LOG_FACE_WARN("Failed to parse message payload");
+ return;
+ }
+
+ this->receive(Transport::Packet(std::move(element)));
+}
+
+void
+WebSocketTransport::schedulePing()
+{
+ m_pingEventId = scheduler::schedule(m_pingInterval, bind(&WebSocketTransport::sendPing, this));
+}
+
+void
+WebSocketTransport::sendPing()
+{
+ NFD_LOG_FACE_TRACE(__func__);
+
+ websocketpp::lib::error_code error;
+ m_server.ping(m_handle, "NFD-WebSocket", error);
+ if (error)
+ return processErrorCode(error);
+
+ this->schedulePing();
+}
+
+void
+WebSocketTransport::handlePong()
+{
+ NFD_LOG_FACE_TRACE(__func__);
+}
+
+void
+WebSocketTransport::handlePongTimeout()
+{
+ NFD_LOG_FACE_WARN(__func__);
+ this->setState(TransportState::FAILED);
+ doClose();
+}
+
+void
+WebSocketTransport::processErrorCode(const websocketpp::lib::error_code& error)
+{
+ NFD_LOG_FACE_TRACE(__func__);
+
+ if (getState() == TransportState::CLOSING ||
+ getState() == TransportState::FAILED ||
+ getState() == TransportState::CLOSED)
+ // transport is shutting down, ignore any errors
+ return;
+
+ NFD_LOG_FACE_WARN("Send or ping operation failed: " << error.message());
+
+ this->setState(TransportState::FAILED);
+ doClose();
+}
+
+void
+WebSocketTransport::doClose()
+{
+ NFD_LOG_FACE_TRACE(__func__);
+
+ m_pingEventId.cancel();
+
+ // use the non-throwing variant and ignore errors, if any
+ websocketpp::lib::error_code error;
+ m_server.close(m_handle, websocketpp::close::status::normal, "closed by NFD", error);
+
+ this->setState(TransportState::CLOSED);
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/websocket-transport.hpp b/daemon/face/websocket-transport.hpp
new file mode 100644
index 0000000..6bfbdc3
--- /dev/null
+++ b/daemon/face/websocket-transport.hpp
@@ -0,0 +1,88 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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_DAEMON_FACE_WEBSOCKET_TRANSPORT_HPP
+#define NFD_DAEMON_FACE_WEBSOCKET_TRANSPORT_HPP
+
+#include "transport.hpp"
+#include "websocketpp.hpp"
+#include "core/scheduler.hpp"
+
+namespace nfd {
+namespace face {
+
+/**
+ * \brief A Transport that communicates on a WebSocket connection
+ */
+class WebSocketTransport : public Transport
+{
+public:
+ WebSocketTransport(websocketpp::connection_hdl hdl,
+ websocket::Server& server,
+ time::milliseconds pingInterval);
+
+ /** \brief Translates a message into a Block
+ * and delivers it to the link service
+ */
+ void
+ receiveMessage(const std::string& msg);
+
+ void
+ handlePong();
+
+ void
+ handlePongTimeout();
+
+protected:
+ virtual void
+ beforeChangePersistency(ndn::nfd::FacePersistency newPersistency) DECL_OVERRIDE;
+
+ virtual void
+ doClose() DECL_OVERRIDE;
+
+private:
+ virtual void
+ doSend(Transport::Packet&& packet) DECL_OVERRIDE;
+
+ void
+ schedulePing();
+
+ void
+ sendPing();
+
+ void
+ processErrorCode(const websocketpp::lib::error_code& error);
+
+private:
+ websocketpp::connection_hdl m_handle;
+ websocket::Server& m_server;
+ time::milliseconds m_pingInterval;
+ scheduler::ScopedEventId m_pingEventId;
+};
+
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_DAEMON_FACE_WEBSOCKET_TRANSPORT_HPP
diff --git a/daemon/face/websocketpp.hpp b/daemon/face/websocketpp.hpp
index f590bd1..35581fe 100644
--- a/daemon/face/websocketpp.hpp
+++ b/daemon/face/websocketpp.hpp
@@ -1,12 +1,12 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014, 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
+ * Copyright (c) 2014-2015, 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.
@@ -27,14 +27,25 @@
#define NFD_DAEMON_FACE_WEBSOCKETPP_HPP
#ifndef HAVE_WEBSOCKET
-#error "This file must not be included when WebSocket Face support is disabled"
-#endif
+#error "Cannot include this file when WebSocket support is disabled"
+#endif // HAVE_WEBSOCKET
// suppress websocketpp warnings
#pragma GCC system_header
#pragma clang system_header
+#include <websocketpp/config/asio_no_tls_client.hpp>
+#include <websocketpp/client.hpp>
#include "websocketpp/config/asio_no_tls.hpp"
#include "websocketpp/server.hpp"
+namespace nfd {
+namespace websocket {
+
+typedef websocketpp::client<websocketpp::config::asio_client> Client;
+typedef websocketpp::server<websocketpp::config::asio> Server;
+
+} // namespace websocket
+} // namespace nfd
+
#endif // NFD_DAEMON_FACE_WEBSOCKETPP_HPP
diff --git a/tests/daemon/face/dummy-receive-link-service.hpp b/tests/daemon/face/dummy-receive-link-service.hpp
new file mode 100644
index 0000000..91dabd2
--- /dev/null
+++ b/tests/daemon/face/dummy-receive-link-service.hpp
@@ -0,0 +1,75 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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_DUMMY_RECEIVE_LINK_SERVICE_HPP
+#define NFD_TESTS_DAEMON_FACE_DUMMY_RECEIVE_LINK_SERVICE_HPP
+
+#include "common.hpp"
+
+#include "face/link-service.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+/** \brief a dummy LinkService that logs all received packets, for Transport testing
+ * \warning This LinkService does not allow sending.
+ */
+class DummyReceiveLinkService : public LinkService
+{
+private:
+ virtual void
+ doSendInterest(const Interest& interest) DECL_OVERRIDE
+ {
+ BOOST_ASSERT(false);
+ }
+
+ virtual void
+ doSendData(const Data& data) DECL_OVERRIDE
+ {
+ BOOST_ASSERT(false);
+ }
+
+ virtual void
+ doSendNack(const lp::Nack& nack) DECL_OVERRIDE
+ {
+ BOOST_ASSERT(false);
+ }
+
+ virtual void
+ doReceivePacket(Transport::Packet&& packet) DECL_OVERRIDE
+ {
+ receivedPackets.push_back(packet);
+ }
+
+public:
+ std::vector<Transport::Packet> receivedPackets;
+};
+
+} // namespace tests
+} // namespace face
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_DUMMY_RECEIVE_LINK_SERVICE_HPP
diff --git a/tests/daemon/face/websocket-transport.t.cpp b/tests/daemon/face/websocket-transport.t.cpp
new file mode 100644
index 0000000..f4115bc
--- /dev/null
+++ b/tests/daemon/face/websocket-transport.t.cpp
@@ -0,0 +1,323 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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/>.
+ */
+
+#include "face/websocket-transport.hpp"
+#include "face/lp-face.hpp"
+#include "dummy-receive-link-service.hpp"
+#include "transport-properties.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace face {
+namespace tests {
+
+using namespace nfd::tests;
+namespace ip = boost::asio::ip;
+
+BOOST_AUTO_TEST_SUITE(Face)
+
+/** \brief a fixture that accepts a single WebSocket connection from a client
+ */
+class SingleWebSocketFixture : public BaseFixture
+{
+public:
+ SingleWebSocketFixture()
+ : transport(nullptr)
+ , serverReceivedPackets(nullptr)
+ , clientShouldPong(true)
+ {
+ }
+
+ /** \brief initialize server and start listening
+ */
+ void
+ serverListen(const ip::tcp::endpoint& ep,
+ const time::milliseconds& pongTimeout = time::milliseconds(1000))
+ {
+ server.clear_access_channels(websocketpp::log::alevel::all);
+ server.clear_error_channels(websocketpp::log::elevel::all);
+
+ server.init_asio(&g_io);
+ server.set_open_handler(bind(&SingleWebSocketFixture::serverHandleOpen, this, _1));
+ server.set_close_handler(bind(&SingleWebSocketFixture::serverHandleClose, this));
+ server.set_message_handler(bind(&SingleWebSocketFixture::serverHandleMessage, this, _2));
+ server.set_pong_handler(bind(&SingleWebSocketFixture::serverHandlePong, this));
+ server.set_pong_timeout_handler(bind(&SingleWebSocketFixture::serverHandlePongTimeout, this));
+ server.set_pong_timeout(pongTimeout.count());
+
+ server.set_reuse_addr(true);
+
+ server.listen(ep);
+ server.start_accept();
+ }
+
+ /** \brief initialize client and connect to server
+ */
+ void
+ clientConnect(const std::string& uri)
+ {
+ 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(&SingleWebSocketFixture::clientHandleOpen, this, _1));
+ client.set_message_handler(bind(&SingleWebSocketFixture::clientHandleMessage, this, _2));
+ client.set_ping_handler(bind(&SingleWebSocketFixture::clientHandlePing, this));
+
+ websocketpp::lib::error_code ec;
+ websocket::Client::connection_ptr con = client.get_connection("ws://127.0.0.1:20070", ec);
+ BOOST_REQUIRE(!ec);
+
+ client.connect(con);
+ }
+
+ void
+ makeFace(const time::milliseconds& pingInterval = time::milliseconds(10000))
+ {
+ face = make_unique<LpFace>(
+ make_unique<DummyReceiveLinkService>(),
+ make_unique<WebSocketTransport>(serverHdl, ref(server), pingInterval));
+ transport = static_cast<WebSocketTransport*>(face->getTransport());
+ serverReceivedPackets = &static_cast<DummyReceiveLinkService*>(face->getLinkService())->receivedPackets;
+ }
+
+ /** \brief initialize both server and client, and have each other connected, create Transport
+ */
+ void
+ endToEndInitialize(const ip::tcp::endpoint& ep,
+ const time::milliseconds& pingInterval = time::milliseconds(10000),
+ const time::milliseconds& pongTimeout = time::milliseconds(1000))
+ {
+ this->serverListen(ep, pongTimeout);
+ std::string uri = "ws://" + ep.address().to_string() + ":" + to_string(ep.port());
+ this->clientConnect(uri);
+ BOOST_REQUIRE_EQUAL(limitedIo.run(2, // serverHandleOpen, clientHandleOpen
+ time::seconds(1)), LimitedIo::EXCEED_OPS);
+ this->makeFace(pingInterval);
+ }
+
+private:
+ void
+ serverHandleOpen(websocketpp::connection_hdl hdl)
+ {
+ serverHdl = hdl;
+ limitedIo.afterOp();
+ }
+
+ void
+ serverHandleClose()
+ {
+ if (transport == nullptr) {
+ return;
+ }
+
+ transport->close();
+ limitedIo.afterOp();
+ }
+
+ void
+ serverHandleMessage(websocket::Server::message_ptr msg)
+ {
+ if (transport == nullptr) {
+ return;
+ }
+
+ transport->receiveMessage(msg->get_payload());
+ limitedIo.afterOp();
+ }
+
+ void
+ serverHandlePong()
+ {
+ if (transport == nullptr) {
+ return;
+ }
+
+ transport->handlePong();
+ limitedIo.afterOp();
+ }
+
+ void
+ serverHandlePongTimeout()
+ {
+ if (transport == nullptr) {
+ return;
+ }
+
+ transport->handlePongTimeout();
+ limitedIo.afterOp();
+ }
+
+ void
+ clientHandleOpen(websocketpp::connection_hdl hdl)
+ {
+ clientHdl = hdl;
+ limitedIo.afterOp();
+ }
+
+ void
+ clientHandleMessage(websocket::Client::message_ptr msg)
+ {
+ clientReceivedMessages.push_back(msg->get_payload());
+ limitedIo.afterOp();
+ }
+
+ bool
+ clientHandlePing()
+ {
+ limitedIo.afterOp();
+ return clientShouldPong;
+ }
+
+public:
+ LimitedIo limitedIo;
+
+ websocket::Server server;
+ websocketpp::connection_hdl serverHdl;
+ unique_ptr<LpFace> face;
+ WebSocketTransport* transport;
+ std::vector<Transport::Packet>* serverReceivedPackets;
+
+ websocket::Client client;
+ websocketpp::connection_hdl clientHdl;
+ bool clientShouldPong;
+ std::vector<std::string> clientReceivedMessages;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestWebSocketTransport, SingleWebSocketFixture)
+
+BOOST_AUTO_TEST_CASE(StaticProperties)
+{
+ ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
+ this->endToEndInitialize(ep);
+ checkStaticPropertiesInitialized(*transport);
+
+ BOOST_CHECK_EQUAL(transport->getLocalUri(), FaceUri("ws://127.0.0.1:20070"));
+ BOOST_CHECK_EQUAL(transport->getRemoteUri().getScheme(), "wsclient");
+ BOOST_CHECK_EQUAL(transport->getRemoteUri().getHost(), "127.0.0.1");
+ BOOST_CHECK_EQUAL(transport->getRemoteUri().getPath(), "");
+ BOOST_CHECK_EQUAL(transport->getScope(), ndn::nfd::FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(transport->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+ BOOST_CHECK_EQUAL(transport->getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
+ BOOST_CHECK_EQUAL(transport->getMtu(), MTU_UNLIMITED);
+}
+
+BOOST_AUTO_TEST_CASE(PingPong)
+{
+ ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
+ this->endToEndInitialize(ep, time::milliseconds(500), time::milliseconds(300));
+
+ BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePong
+ time::milliseconds(1500)), LimitedIo::EXCEED_OPS);
+ BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
+
+ this->clientShouldPong = false;
+ BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePongTimeout
+ time::milliseconds(2000)), LimitedIo::EXCEED_OPS);
+ BOOST_CHECK_MESSAGE(transport->getState() == TransportState::FAILED ||
+ transport->getState() == TransportState::CLOSED,
+ "expect FAILED or CLOSED state, actual state=" << transport->getState());
+}
+
+BOOST_AUTO_TEST_CASE(Send)
+{
+ ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
+ this->endToEndInitialize(ep);
+
+ Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
+ transport->send(Transport::Packet(Block(pkt1)));
+ BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
+ time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
+
+ Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
+ transport->send(Transport::Packet(Block(pkt2)));
+ BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
+ time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
+
+ BOOST_REQUIRE_EQUAL(clientReceivedMessages.size(), 2);
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()),
+ reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()) + clientReceivedMessages[0].size(),
+ pkt1.begin(), pkt1.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()),
+ reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()) + clientReceivedMessages[1].size(),
+ pkt2.begin(), pkt2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Receive)
+{
+ ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
+ this->endToEndInitialize(ep);
+
+ Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
+ client.send(clientHdl, pkt1.wire(), pkt1.size(), websocketpp::frame::opcode::binary);
+ BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
+ time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
+
+ Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
+ client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
+ BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
+ time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
+
+ BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 2);
+ BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt1);
+ BOOST_CHECK(serverReceivedPackets->at(1).packet == pkt2);
+ BOOST_CHECK_EQUAL(serverReceivedPackets->at(0).remoteEndpoint, serverReceivedPackets->at(1).remoteEndpoint);
+}
+
+BOOST_AUTO_TEST_CASE(ReceiveMalformed)
+{
+ ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
+ this->endToEndInitialize(ep);
+
+ Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
+ client.send(clientHdl, pkt1.wire(), pkt1.size() - 1, // truncated
+ websocketpp::frame::opcode::binary);
+ BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
+ time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
+
+ // bad packet is dropped
+ BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
+ BOOST_CHECK_EQUAL(serverReceivedPackets->size(), 0);
+
+ Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
+ client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
+ BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
+ time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
+
+ // next valid packet is still received normally
+ BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 1);
+ BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestWebSocketTransport
+BOOST_AUTO_TEST_SUITE_END() // Face
+
+} // namespace tests
+} // namespace face
+} // namespace nfd
diff --git a/tests/daemon/face/websocket.t.cpp b/tests/daemon/face/websocket.t.cpp
index a1d8a52..ee98bf3 100644
--- a/tests/daemon/face/websocket.t.cpp
+++ b/tests/daemon/face/websocket.t.cpp
@@ -24,21 +24,19 @@
*/
#include "face/websocket-channel.hpp"
-#include "face/websocket-face.hpp"
#include "face/websocket-factory.hpp"
+#include "face/websocketpp.hpp"
#include "tests/test-common.hpp"
#include "tests/limited-io.hpp"
-#include <websocketpp/config/asio_no_tls_client.hpp>
-#include <websocketpp/client.hpp>
-
-typedef websocketpp::client<websocketpp::config::asio_client> Client;
-
namespace nfd {
namespace tests {
-BOOST_FIXTURE_TEST_SUITE(FaceWebSocket, BaseFixture)
+BOOST_AUTO_TEST_SUITE(Face)
+BOOST_FIXTURE_TEST_SUITE(TestWebSocket, BaseFixture)
+
+using nfd::Face;
BOOST_AUTO_TEST_CASE(GetChannels)
{
@@ -212,7 +210,7 @@
shared_ptr<Face> face1;
std::vector<Interest> face1_receivedInterests;
std::vector<Data> face1_receivedDatas;
- Client client1;
+ websocket::Client client1;
websocketpp::connection_hdl handle;
std::vector<Interest> client1_receivedInterests;
std::vector<Data> client1_receivedDatas;
@@ -243,7 +241,7 @@
client1.set_ping_handler(bind(&EndToEndFixture::client1_onPing, this, _1, _2));
websocketpp::lib::error_code ec;
- Client::connection_ptr con = client1.get_connection("ws://127.0.0.1:20070", ec);
+ auto con = client1.get_connection("ws://127.0.0.1:20070", ec);
client1.connect(con);
BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
@@ -252,7 +250,7 @@
BOOST_CHECK_EQUAL(channel1->size(), 1);
BOOST_REQUIRE(static_cast<bool>(face1));
- BOOST_CHECK_EQUAL(face1->isLocal(), false);
+ BOOST_CHECK_EQUAL(face1->isLocal(), true);
BOOST_CHECK_EQUAL(face1->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
BOOST_CHECK_EQUAL(face1->isMultiAccess(), false);
BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "ws://127.0.0.1:20070");
@@ -262,19 +260,15 @@
shared_ptr<Interest> interest2 = makeInterest("ndn:/QWiIMfj5sL");
shared_ptr<Data> data2 = makeData("ndn:/XNBV796f");
- std::string bigName("ndn:/");
- bigName.append(9000, 'a');
- shared_ptr<Interest> bigInterest = makeInterest(bigName);
-
client1_sendInterest(*interest1);
client1_sendInterest(*interest1);
client1_sendInterest(*interest1);
- client1_sendInterest(*bigInterest); // This one should be ignored by face1
face1->sendData (*data1);
face1->sendInterest (*interest2);
client1_sendData (*data2);
client1_sendData (*data2);
client1_sendData (*data2);
+
size_t nBytesSent = data1->wireEncode().size() + interest2->wireEncode().size();
size_t nBytesReceived = interest1->wireEncode().size() * 3 + data2->wireEncode().size() * 3;
@@ -303,7 +297,8 @@
BOOST_CHECK_EQUAL(channel1->size(), 0);
}
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestWebSocket
+BOOST_AUTO_TEST_SUITE_END() // Face
} // namespace tests
} // namespace nfd