face: Implement WebSocket channel, face, and factory.
Change-Id: Ic4dbb1abdbbdbec028746ba14b11be8ab9cc1edb Refs: #1468
This commit is contained in:
committed by
Alexander Afanasyev
parent
ff489a230a
commit
53df163b62
@@ -0,0 +1,3 @@
|
||||
[submodule "websocketpp"]
|
||||
path = websocketpp
|
||||
url = https://github.com/zaphoyd/websocketpp.git
|
||||
@@ -0,0 +1,77 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from waflib import Options, Logs, Errors
|
||||
from waflib.Configure import conf
|
||||
|
||||
import re
|
||||
|
||||
def addWebsocketOptions(self, opt):
|
||||
opt.add_option('--without-websocket', action='store_false', default=True,
|
||||
dest='with_websocket',
|
||||
help='Disable WebSocket face support')
|
||||
setattr(Options.OptionsContext, "addWebsocketOptions", addWebsocketOptions)
|
||||
|
||||
@conf
|
||||
def checkWebsocket(self, **kw):
|
||||
if not self.options.with_websocket:
|
||||
return
|
||||
|
||||
isMandatory = kw.get('mandatory', True)
|
||||
|
||||
self.start_msg('Checking for Websocket includes')
|
||||
|
||||
try:
|
||||
websocketDir = self.path.find_dir('websocketpp/websocketpp')
|
||||
if not websocketDir:
|
||||
raise Errors.WafError('Not found')
|
||||
|
||||
versionFile = websocketDir.find_node('version.hpp')
|
||||
if not websocketDir:
|
||||
raise Errors.WafError('Corrupted: Websocket version file not found')
|
||||
|
||||
try:
|
||||
txt = versionFile.read()
|
||||
except (OSError, IOError):
|
||||
raise Errors.WafError('Corrupted: cannot read Websocket version file')
|
||||
|
||||
# Looking for the following:
|
||||
# static int const major_version = 0;
|
||||
# static int const minor_version = 3;
|
||||
# static int const patch_version = 0;
|
||||
|
||||
version = [None, None, None]
|
||||
|
||||
majorVersion = re.compile('^static int const major_version = (\\d+);$', re.M)
|
||||
version[0] = majorVersion.search(txt)
|
||||
|
||||
minorVersion = re.compile('^static int const minor_version = (\\d+);$', re.M)
|
||||
version[1] = minorVersion.search(txt)
|
||||
|
||||
patchVersion = re.compile('^static int const patch_version = (\\d+);$', re.M)
|
||||
version[2] = patchVersion.search(txt)
|
||||
|
||||
if not version[0] or not version[1] or not version[2]:
|
||||
raise Errors.WafError('Corrupted: cannot detect websocket version')
|
||||
|
||||
self.env['WEBSOCKET_VERSION'] = [i.group(1) for i in version]
|
||||
|
||||
# todo: version checking, if necessary
|
||||
|
||||
self.end_msg('.'.join(self.env['WEBSOCKET_VERSION']))
|
||||
|
||||
self.env['HAVE_WEBSOCKET'] = True
|
||||
self.define('HAVE_WEBSOCKET', 1)
|
||||
|
||||
except Errors.WafError as error:
|
||||
if isMandatory:
|
||||
self.end_msg(str(error), color='RED')
|
||||
Logs.warn('If you are using git NFD repository, checkout websocketpp submodule: ')
|
||||
Logs.warn(' git submodule init && git submodule update')
|
||||
Logs.warn('Otherwise, manually download and extract websocketpp library:')
|
||||
Logs.warn(' mkdir websocketpp')
|
||||
Logs.warn(' curl -L -O https://github.com/zaphoyd/websocketpp/archive/0.3.0-alpha4.tar.gz')
|
||||
Logs.warn(' tar zxf 0.3.0-alpha4.tar.gz -C websocketpp/ --strip 1')
|
||||
Logs.warn('Alternatively, Websocket support can be disabled with --without-websocket')
|
||||
self.fatal("The configuration failed")
|
||||
else:
|
||||
self.end_msg(str(error))
|
||||
@@ -112,6 +112,14 @@ FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint)
|
||||
m_port = boost::lexical_cast<std::string>(endpoint.port());
|
||||
}
|
||||
|
||||
FaceUri::FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme)
|
||||
: m_scheme(scheme)
|
||||
{
|
||||
m_isV6 = endpoint.address().is_v6();
|
||||
m_host = endpoint.address().to_string();
|
||||
m_port = boost::lexical_cast<std::string>(endpoint.port());
|
||||
}
|
||||
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint)
|
||||
: m_isV6(false)
|
||||
|
||||
@@ -79,6 +79,9 @@ public: // scheme-specific construction
|
||||
explicit
|
||||
FaceUri(const boost::asio::ip::udp::endpoint& endpoint);
|
||||
|
||||
/// construct tcp canonical FaceUri with customized scheme
|
||||
FaceUri(const boost::asio::ip::tcp::endpoint& endpoint, const std::string& scheme);
|
||||
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
/// construct unix canonical FaceUri
|
||||
explicit
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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-channel.hpp"
|
||||
#include "core/face-uri.hpp"
|
||||
|
||||
namespace nfd {
|
||||
|
||||
NFD_LOG_INIT("WebSocketChannel");
|
||||
|
||||
using namespace boost::asio;
|
||||
|
||||
WebSocketChannel::WebSocketChannel(const websocket::Endpoint& localEndpoint)
|
||||
: m_localEndpoint(localEndpoint)
|
||||
, m_isListening(false)
|
||||
{
|
||||
// Setup WebSocket server
|
||||
m_server.clear_access_channels(websocketpp::log::alevel::all);
|
||||
m_server.clear_error_channels(websocketpp::log::alevel::all);
|
||||
|
||||
m_server.set_message_handler(bind(&WebSocketChannel::handleMessage, this, _1, _2));
|
||||
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());
|
||||
|
||||
this->setUri(FaceUri(localEndpoint, "ws"));
|
||||
}
|
||||
|
||||
WebSocketChannel::~WebSocketChannel()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketChannel::handleMessage(websocketpp::connection_hdl hdl,
|
||||
websocket::Server::message_ptr msg)
|
||||
{
|
||||
ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
|
||||
if (it != m_channelFaces.end())
|
||||
{
|
||||
it->second->handleReceive(msg->get_payload());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketChannel::handleOpen(websocketpp::connection_hdl hdl)
|
||||
{
|
||||
std::string remote;
|
||||
try
|
||||
{
|
||||
remote = "wsclient://" + m_server.get_con_from_hdl(hdl)->get_remote_endpoint();
|
||||
}
|
||||
catch (websocketpp::lib::error_code ec)
|
||||
{
|
||||
NFD_LOG_DEBUG("handleOpen: cannot get remote uri");
|
||||
websocketpp::lib::error_code ecode;
|
||||
m_server.close(hdl, websocketpp::close::status::normal, "closed by channel", ecode);
|
||||
}
|
||||
shared_ptr<WebSocketFace> face = make_shared<WebSocketFace>(FaceUri(remote), this->getUri(),
|
||||
hdl, boost::ref(m_server));
|
||||
m_onFaceCreatedCallback(face);
|
||||
m_channelFaces[hdl] = face;
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketChannel::handleClose(websocketpp::connection_hdl hdl)
|
||||
{
|
||||
ChannelFaceMap::iterator it = m_channelFaces.find(hdl);
|
||||
if (it != m_channelFaces.end())
|
||||
{
|
||||
NFD_LOG_DEBUG("handleClose: remove client");
|
||||
m_channelFaces.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocketChannel::listen(const FaceCreatedCallback& onFaceCreated)
|
||||
{
|
||||
if (m_isListening)
|
||||
{
|
||||
throw Error("Listen already called on this channel");
|
||||
}
|
||||
m_isListening = true;
|
||||
|
||||
m_onFaceCreatedCallback = onFaceCreated;
|
||||
|
||||
try
|
||||
{
|
||||
m_server.listen(m_localEndpoint);
|
||||
}
|
||||
catch (websocketpp::lib::error_code ec)
|
||||
{
|
||||
throw Error("Failed to listen on local endpoint");
|
||||
}
|
||||
|
||||
m_server.start_accept();
|
||||
}
|
||||
|
||||
size_t
|
||||
WebSocketChannel::size() const
|
||||
{
|
||||
return m_channelFaces.size();
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,122 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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_CHANNEL_HPP
|
||||
#define NFD_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
|
||||
|
||||
#include "channel.hpp"
|
||||
#include "core/global-io.hpp"
|
||||
#include "core/scheduler.hpp"
|
||||
#include "websocket-face.hpp"
|
||||
|
||||
namespace nfd {
|
||||
|
||||
/**
|
||||
* \brief Class implementing WebSocket-based channel to create faces
|
||||
*
|
||||
*
|
||||
*/
|
||||
class WebSocketChannel : public Channel
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Exception of WebSocketChannel
|
||||
*/
|
||||
class Error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
Error(const std::string& what)
|
||||
: runtime_error(what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Create WebSocket channel for the local endpoint
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
explicit
|
||||
WebSocketChannel(const websocket::Endpoint& localEndpoint);
|
||||
|
||||
virtual
|
||||
~WebSocketChannel();
|
||||
|
||||
/**
|
||||
* \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
|
||||
*/
|
||||
void
|
||||
listen(const FaceCreatedCallback& onFaceCreated);
|
||||
|
||||
/**
|
||||
* \brief Get number of faces in the channel
|
||||
*/
|
||||
size_t
|
||||
size() const;
|
||||
|
||||
private:
|
||||
void
|
||||
handleMessage(websocketpp::connection_hdl hdl, websocket::Server::message_ptr msg);
|
||||
|
||||
void
|
||||
handleOpen(websocketpp::connection_hdl hdl);
|
||||
|
||||
void
|
||||
handleClose(websocketpp::connection_hdl hdl);
|
||||
|
||||
private:
|
||||
websocket::Endpoint m_localEndpoint;
|
||||
|
||||
websocket::Server m_server;
|
||||
|
||||
/**
|
||||
* Callbacks for face creation.
|
||||
* New communications are detected using async_receive_from.
|
||||
* Its handler has a fixed signature. No space for the face callback
|
||||
*/
|
||||
FaceCreatedCallback m_onFaceCreatedCallback;
|
||||
|
||||
typedef std::map< websocketpp::connection_hdl, shared_ptr<WebSocketFace> > ChannelFaceMap;
|
||||
ChannelFaceMap m_channelFaces;
|
||||
|
||||
/**
|
||||
* \brief If true, it means the function listen has already been called
|
||||
*/
|
||||
bool m_isListening;
|
||||
|
||||
};
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_WEBSOCKET_CHANNEL_HPP
|
||||
@@ -0,0 +1,97 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
WebSocketFace::sendInterest(const Interest& interest)
|
||||
{
|
||||
this->onSendInterest(interest);
|
||||
const Block& payload = interest.wireEncode();
|
||||
m_server.send(m_handle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketFace::sendData(const Data& data)
|
||||
{
|
||||
this->onSendData(data);
|
||||
const Block& payload = data.wireEncode();
|
||||
m_server.send(m_handle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketFace::close()
|
||||
{
|
||||
if (m_closed == false)
|
||||
{
|
||||
m_closed = true;
|
||||
websocketpp::lib::error_code ecode;
|
||||
m_server.close(m_handle, websocketpp::close::status::normal, "closed by nfd", ecode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketFace::handleReceive(const std::string& msg)
|
||||
{
|
||||
// Copy message into Face internal buffer
|
||||
BOOST_ASSERT(msg.size() <= MAX_NDN_PACKET_SIZE);
|
||||
|
||||
// Try to parse message data
|
||||
bool isOk = true;
|
||||
Block element;
|
||||
isOk = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.c_str()), msg.size(), element);
|
||||
if (!isOk)
|
||||
{
|
||||
NFD_LOG_TRACE("[id:" << this->getId()
|
||||
<< "] Received invalid NDN packet of length ["
|
||||
<< msg.size() << "]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->decodeAndDispatchInput(element))
|
||||
{
|
||||
NFD_LOG_WARN("[id:" << this->getId()
|
||||
<< "] Received unrecognized block of type ["
|
||||
<< element.type() << "]");
|
||||
// ignore unknown packet and proceed
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,77 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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/logger.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 boost::asio::ip::tcp::endpoint Endpoint;
|
||||
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
|
||||
virtual void
|
||||
sendInterest(const Interest& interest);
|
||||
|
||||
virtual void
|
||||
sendData(const Data& data);
|
||||
|
||||
virtual void
|
||||
close();
|
||||
|
||||
void
|
||||
handleReceive(const std::string& msg);
|
||||
|
||||
private:
|
||||
websocketpp::connection_hdl m_handle;
|
||||
websocket::Server& m_server;
|
||||
bool m_closed;
|
||||
};
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_WEBSOCKET_FACE_HPP
|
||||
@@ -0,0 +1,84 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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-factory.hpp"
|
||||
|
||||
namespace nfd {
|
||||
|
||||
using namespace boost::asio;
|
||||
|
||||
NFD_LOG_INIT("WebSocketFactory");
|
||||
|
||||
WebSocketFactory::WebSocketFactory(const std::string& defaultPort)
|
||||
: m_defaultPort(defaultPort)
|
||||
{
|
||||
}
|
||||
|
||||
shared_ptr<WebSocketChannel>
|
||||
WebSocketFactory::createChannel(const websocket::Endpoint& endpoint)
|
||||
{
|
||||
shared_ptr<WebSocketChannel> channel = findChannel(endpoint);
|
||||
if (static_cast<bool>(channel))
|
||||
return channel;
|
||||
|
||||
channel = make_shared<WebSocketChannel>(boost::cref(endpoint));
|
||||
m_channels[endpoint] = channel;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
shared_ptr<WebSocketChannel>
|
||||
WebSocketFactory::createChannel(const std::string& localIPAddress,
|
||||
uint16_t localPort)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
ip::address address = ip::address::from_string(localIPAddress, ec);
|
||||
if (ec)
|
||||
{
|
||||
throw Error("Invalid address format: " + localIPAddress);
|
||||
}
|
||||
websocket::Endpoint endpoint(address, localPort);
|
||||
return createChannel(endpoint);
|
||||
}
|
||||
|
||||
shared_ptr<WebSocketChannel>
|
||||
WebSocketFactory::findChannel(const websocket::Endpoint& localEndpoint)
|
||||
{
|
||||
ChannelMap::iterator i = m_channels.find(localEndpoint);
|
||||
if (i != m_channels.end())
|
||||
return i->second;
|
||||
else
|
||||
return shared_ptr<WebSocketChannel>();
|
||||
}
|
||||
|
||||
void
|
||||
WebSocketFactory::createFace(const FaceUri& uri,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceConnectFailedCallback& onConnectFailed)
|
||||
{
|
||||
throw Error("WebSocketFactory does not support 'createFace' operation");
|
||||
}
|
||||
|
||||
} // namespace nfd
|
||||
@@ -0,0 +1,108 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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_FACTORY_HPP
|
||||
#define NFD_DAEMON_FACE_WEBSOCKET_FACTORY_HPP
|
||||
|
||||
#include "protocol-factory.hpp"
|
||||
#include "websocket-channel.hpp"
|
||||
|
||||
|
||||
namespace nfd {
|
||||
|
||||
class WebSocketFactory : public ProtocolFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Exception of WebSocketFactory
|
||||
*/
|
||||
class Error : public ProtocolFactory::Error
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
Error(const std::string& what)
|
||||
: ProtocolFactory::Error(what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
explicit
|
||||
WebSocketFactory(const std::string& defaultPort);
|
||||
|
||||
/**
|
||||
* \brief Create WebSocket-based channel using websocket::Endpoint
|
||||
*
|
||||
* websocket::Endpoint is really an alias for boost::asio::ip::tcp::endpoint.
|
||||
*
|
||||
* If this method called twice with the same endpoint, only one channel
|
||||
* will be created. The second call will just retrieve the existing
|
||||
* channel.
|
||||
*
|
||||
* \returns always a valid pointer to a WebSocketChannel object, an exception
|
||||
* is thrown if it cannot be created.
|
||||
*
|
||||
* \throws WebSocketFactory::Error
|
||||
*
|
||||
*/
|
||||
shared_ptr<WebSocketChannel>
|
||||
createChannel(const websocket::Endpoint& localEndpoint);
|
||||
|
||||
/**
|
||||
* \brief Create WebSocket-based channel using specified ip address and port number
|
||||
*
|
||||
* \throws WebSocketFactory::Error
|
||||
*/
|
||||
shared_ptr<WebSocketChannel>
|
||||
createChannel(const std::string& localIPAddress,
|
||||
uint16_t localPort);
|
||||
|
||||
// from Factory
|
||||
virtual void
|
||||
createFace(const FaceUri& uri,
|
||||
const FaceCreatedCallback& onCreated,
|
||||
const FaceConnectFailedCallback& onConnectFailed);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* \brief Look up WebSocketChannel using specified local endpoint
|
||||
*
|
||||
* \returns shared pointer to the existing WebSocketChannel object
|
||||
* or empty shared pointer when such channel does not exist
|
||||
*
|
||||
* \throws never
|
||||
*/
|
||||
shared_ptr<WebSocketChannel>
|
||||
findChannel(const websocket::Endpoint& localEndpoint);
|
||||
|
||||
typedef std::map< websocket::Endpoint, shared_ptr<WebSocketChannel> > ChannelMap;
|
||||
ChannelMap m_channels;
|
||||
|
||||
std::string m_defaultPort;
|
||||
};
|
||||
|
||||
} // namespace nfd
|
||||
|
||||
#endif // NFD_DAEMON_FACE_WEBSOCKET_FACTORY_HPP
|
||||
@@ -0,0 +1,36 @@
|
||||
/* -*- 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
|
||||
*
|
||||
* 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_WEBSOCKETPP_HPP
|
||||
#define NFD_DAEMON_FACE_WEBSOCKETPP_HPP
|
||||
|
||||
// suppress websocketpp warnings
|
||||
#pragma GCC system_header
|
||||
#pragma clang system_header
|
||||
|
||||
#include "websocketpp/config/asio_no_tls.hpp"
|
||||
#include "websocketpp/server.hpp"
|
||||
|
||||
#endif // NFD_DAEMON_FACE_WEBSOCKETPP_HPP
|
||||
@@ -41,6 +41,10 @@
|
||||
#include "face/ethernet-factory.hpp"
|
||||
#endif // HAVE_LIBPCAP
|
||||
|
||||
#ifdef HAVE_WEBSOCKET
|
||||
#include "face/websocket-factory.hpp"
|
||||
#endif // HAVE_WEBSOCKET
|
||||
|
||||
#include <ndn-cxx/management/nfd-face-event-notification.hpp>
|
||||
|
||||
namespace nfd {
|
||||
@@ -142,6 +146,7 @@ FaceManager::onConfig(const ConfigSection& configSection,
|
||||
bool hasSeenTcp = false;
|
||||
bool hasSeenUdp = false;
|
||||
bool hasSeenEther = false;
|
||||
bool hasSeenWebSocket = false;
|
||||
|
||||
const std::list<shared_ptr<NetworkInterfaceInfo> > nicList(listNetworkInterfaces());
|
||||
|
||||
@@ -181,6 +186,14 @@ FaceManager::onConfig(const ConfigSection& configSection,
|
||||
|
||||
processSectionEther(item->second, isDryRun, nicList);
|
||||
}
|
||||
else if (item->first == "websocket")
|
||||
{
|
||||
if (hasSeenWebSocket)
|
||||
throw Error("Duplicate \"websocket\" section");
|
||||
hasSeenWebSocket = true;
|
||||
|
||||
processSectionWebSocket(item->second, isDryRun);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Error("Unrecognized option \"" + item->first + "\"");
|
||||
@@ -621,6 +634,107 @@ FaceManager::processSectionEther(const ConfigSection& configSection,
|
||||
#endif // HAVE_LIBPCAP
|
||||
}
|
||||
|
||||
void
|
||||
FaceManager::processSectionWebSocket(const ConfigSection& configSection, bool isDryRun)
|
||||
{
|
||||
// ; the websocket section contains settings of WebSocket faces and channels
|
||||
// websocket
|
||||
// {
|
||||
// listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
|
||||
// port 9696 ; WebSocket listener port number
|
||||
// enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
|
||||
// enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
|
||||
// }
|
||||
|
||||
#if defined(HAVE_WEBSOCKET)
|
||||
|
||||
std::string port = "9696";
|
||||
bool needToListen = true;
|
||||
bool enableV4 = true;
|
||||
bool enableV6 = true;
|
||||
|
||||
for (ConfigSection::const_iterator i = configSection.begin();
|
||||
i != configSection.end();
|
||||
++i)
|
||||
{
|
||||
if (i->first == "port")
|
||||
{
|
||||
port = i->second.get_value<std::string>();
|
||||
try
|
||||
{
|
||||
uint16_t portNo = boost::lexical_cast<uint16_t>(port);
|
||||
NFD_LOG_TRACE("WebSocket port set to " << portNo);
|
||||
}
|
||||
catch (const std::bad_cast& error)
|
||||
{
|
||||
throw ConfigFile::Error("Invalid value for option " +
|
||||
i->first + "\" in \"websocket\" section");
|
||||
}
|
||||
}
|
||||
else if (i->first == "listen")
|
||||
{
|
||||
needToListen = parseYesNo(i, i->first, "websocket");
|
||||
}
|
||||
else if (i->first == "enable_v4")
|
||||
{
|
||||
enableV4 = parseYesNo(i, i->first, "websocket");
|
||||
}
|
||||
else if (i->first == "enable_v6")
|
||||
{
|
||||
enableV6 = parseYesNo(i, i->first, "websocket");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw ConfigFile::Error("Unrecognized option \"" +
|
||||
i->first + "\" in \"websocket\" section");
|
||||
}
|
||||
}
|
||||
|
||||
if (!enableV4 && !enableV6)
|
||||
{
|
||||
throw ConfigFile::Error("IPv4 and IPv6 channels have been disabled."
|
||||
" Remove \"websocket\" section to disable WebSocket channels or"
|
||||
" re-enable at least one channel type.");
|
||||
}
|
||||
|
||||
if (!enableV4 && enableV6)
|
||||
{
|
||||
throw ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel.");
|
||||
}
|
||||
|
||||
if (!isDryRun)
|
||||
{
|
||||
shared_ptr<WebSocketFactory> factory = make_shared<WebSocketFactory>(boost::cref(port));
|
||||
m_factories.insert(std::make_pair("websocket", factory));
|
||||
uint16_t portNo = boost::lexical_cast<uint16_t>(port);
|
||||
|
||||
if (enableV6 && enableV4)
|
||||
{
|
||||
shared_ptr<WebSocketChannel> ip46Channel = factory->createChannel("::", portNo);
|
||||
if (needToListen)
|
||||
{
|
||||
ip46Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
|
||||
}
|
||||
|
||||
m_factories.insert(std::make_pair("websocket46", factory));
|
||||
}
|
||||
else if (enableV4)
|
||||
{
|
||||
shared_ptr<WebSocketChannel> ipv4Channel = factory->createChannel("0.0.0.0", portNo);
|
||||
if (needToListen)
|
||||
{
|
||||
ipv4Channel->listen(bind(&FaceTable::add, &m_faceTable, _1));
|
||||
}
|
||||
|
||||
m_factories.insert(std::make_pair("websocket4", factory));
|
||||
}
|
||||
}
|
||||
#else
|
||||
throw ConfigFile::Error("NFD was compiled without WebSocket, "
|
||||
"cannot process \"websocket\" section");
|
||||
#endif // HAVE_WEBSOCKET
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FaceManager::onFaceRequest(const Interest& request)
|
||||
|
||||
@@ -144,6 +144,9 @@ private:
|
||||
bool isDryRun,
|
||||
const std::list<shared_ptr<NetworkInterfaceInfo> >& nicList);
|
||||
|
||||
void
|
||||
processSectionWebSocket(const ConfigSection& configSection, bool isDryRun);
|
||||
|
||||
/** \brief parse a config option that can be either "yes" or "no"
|
||||
* \throw ConfigFile::Error value is neither "yes" nor "no"
|
||||
* \return true if "yes", false if "no"
|
||||
|
||||
@@ -115,6 +115,16 @@ face_system
|
||||
@IF_HAVE_LIBPCAP@ mcast yes ; set to 'no' to disable Ethernet multicast, default 'yes'
|
||||
@IF_HAVE_LIBPCAP@ mcast_group 01:00:5E:00:17:AA ; Ethernet multicast group
|
||||
@IF_HAVE_LIBPCAP@}
|
||||
|
||||
; The websocket section contains settings of WebSocket faces and channels.
|
||||
|
||||
@IF_HAVE_WEBSOCKET@websocket
|
||||
@IF_HAVE_WEBSOCKET@{
|
||||
@IF_HAVE_WEBSOCKET@ listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
|
||||
@IF_HAVE_WEBSOCKET@ port 9696 ; WebSocket listener port number
|
||||
@IF_HAVE_WEBSOCKET@ enable_v4 yes ; set to 'no' to disable listening on IPv4 socket, default 'yes'
|
||||
@IF_HAVE_WEBSOCKET@ enable_v6 yes ; set to 'no' to disable listening on IPv6 socket, default 'yes'
|
||||
@IF_HAVE_WEBSOCKET@}
|
||||
}
|
||||
|
||||
; The authorizations section grants privileges to authorized keys.
|
||||
|
||||
+5
-1
@@ -52,7 +52,8 @@ def build(bld):
|
||||
features='cxx cxxprogram',
|
||||
source=bld.path.ant_glob(['daemon/**/*.cpp'],
|
||||
excl=['daemon/face/ethernet.cpp',
|
||||
'daemon/face/unix-*.cpp']),
|
||||
'daemon/face/unix-*.cpp',
|
||||
'daemon/face/websocket-*.cpp']),
|
||||
use='daemon-objects unit-tests-main',
|
||||
includes='.',
|
||||
install_path=None,
|
||||
@@ -64,6 +65,9 @@ def build(bld):
|
||||
if bld.env['HAVE_UNIX_SOCKETS']:
|
||||
unit_tests_nfd.source += bld.path.ant_glob('daemon/face/unix-*.cpp')
|
||||
|
||||
if bld.env['HAVE_WEBSOCKET']:
|
||||
unit_tests_nfd.source += bld.path.ant_glob('daemon/face/websocket-*.cpp')
|
||||
|
||||
unit_tests_rib = bld.program(
|
||||
target='../unit-tests-rib',
|
||||
features='cxx cxxprogram',
|
||||
|
||||
Submodule
+1
Submodule websocketpp added at e7ce038207
@@ -32,13 +32,14 @@ from waflib import Logs, Utils, Context
|
||||
|
||||
def options(opt):
|
||||
opt.load(['compiler_cxx', 'gnu_dirs'])
|
||||
opt.load(['boost', 'unix-socket', 'dependency-checker',
|
||||
opt.load(['boost', 'unix-socket', 'dependency-checker', 'websocket',
|
||||
'default-compiler-flags', 'coverage',
|
||||
'doxygen', 'sphinx_build'],
|
||||
tooldir=['.waf-tools'])
|
||||
|
||||
nfdopt = opt.add_option_group('NFD Options')
|
||||
opt.addUnixOptions(nfdopt)
|
||||
opt.addWebsocketOptions(nfdopt)
|
||||
opt.addDependencyOptions(nfdopt, 'libpcap')
|
||||
nfdopt.add_option('--without-libpcap', action='store_true', default=False,
|
||||
dest='without_libpcap',
|
||||
@@ -55,7 +56,7 @@ def options(opt):
|
||||
def configure(conf):
|
||||
conf.load(['compiler_cxx', 'gnu_dirs',
|
||||
'default-compiler-flags',
|
||||
'boost', 'dependency-checker',
|
||||
'boost', 'dependency-checker', 'websocket',
|
||||
'doxygen', 'sphinx_build'])
|
||||
|
||||
conf.check_cfg(package='libndn-cxx', args=['--cflags', '--libs'],
|
||||
@@ -79,6 +80,7 @@ def configure(conf):
|
||||
return
|
||||
|
||||
conf.load('unix-socket')
|
||||
conf.checkWebsocket(mandatory=True)
|
||||
|
||||
conf.checkDependency(name='librt', lib='rt', mandatory=False)
|
||||
conf.checkDependency(name='libresolv', lib='resolv', mandatory=False)
|
||||
@@ -130,9 +132,10 @@ def build(bld):
|
||||
source=bld.path.ant_glob(['daemon/**/*.cpp'],
|
||||
excl=['daemon/face/ethernet-*.cpp',
|
||||
'daemon/face/unix-*.cpp',
|
||||
'daemon/face/websocket-*.cpp',
|
||||
'daemon/main.cpp']),
|
||||
use='core-objects',
|
||||
includes='daemon',
|
||||
includes='daemon websocketpp',
|
||||
export_includes='daemon',
|
||||
)
|
||||
|
||||
@@ -143,6 +146,9 @@ def build(bld):
|
||||
if bld.env['HAVE_UNIX_SOCKETS']:
|
||||
nfd_objects.source += bld.path.ant_glob('daemon/face/unix-*.cpp')
|
||||
|
||||
if bld.env['HAVE_WEBSOCKET']:
|
||||
nfd_objects.source += bld.path.ant_glob('daemon/face/websocket-*.cpp')
|
||||
|
||||
bld(target='bin/nfd',
|
||||
features='cxx cxxprogram',
|
||||
source='daemon/main.cpp',
|
||||
@@ -177,7 +183,8 @@ def build(bld):
|
||||
source='nfd.conf.sample.in',
|
||||
target='nfd.conf.sample',
|
||||
install_path="${SYSCONFDIR}/ndn",
|
||||
IF_HAVE_LIBPCAP="" if bld.env['HAVE_LIBPCAP'] else "; ")
|
||||
IF_HAVE_LIBPCAP="" if bld.env['HAVE_LIBPCAP'] else "; ",
|
||||
IF_HAVE_WEBSOCKET="" if bld.env['HAVE_WEBSOCKET'] else "; ")
|
||||
|
||||
bld(features='subst',
|
||||
source='tools/nfd-status-http-server.py',
|
||||
|
||||
Reference in New Issue
Block a user