15 changed files with 1974 additions and 223 deletions
@ -1,114 +0,0 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
||||
/**
|
||||
* Copyright (c) 2013-2015 Regents of the University of California. |
||||
* |
||||
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions). |
||||
* |
||||
* ndn-cxx library is free software: you can redistribute it and/or modify it under the |
||||
* terms of the GNU Lesser General Public License as published by the Free Software |
||||
* Foundation, either version 3 of the License, or (at your option) any later version. |
||||
* |
||||
* ndn-cxx library 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 Lesser General Public License for more details. |
||||
* |
||||
* You should have received copies of the GNU General Public License and GNU Lesser |
||||
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see |
||||
* <http://www.gnu.org/licenses/>.
|
||||
* |
||||
* See AUTHORS.md for complete list of ndn-cxx authors and contributors. |
||||
*/ |
||||
|
||||
#include "nfd-control-response.hpp" |
||||
#include "encoding/tlv-nfd.hpp" |
||||
#include "encoding/block-helpers.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace nfd { |
||||
|
||||
//BOOST_CONCEPT_ASSERT((boost::EqualityComparable<ControlResponse>));
|
||||
BOOST_CONCEPT_ASSERT((WireEncodable<ControlResponse>)); |
||||
BOOST_CONCEPT_ASSERT((WireDecodable<ControlResponse>)); |
||||
static_assert(std::is_base_of<tlv::Error, ControlResponse::Error>::value, |
||||
"ControlResponse::Error must inherit from tlv::Error"); |
||||
|
||||
ControlResponse::ControlResponse() |
||||
: m_code(200) |
||||
{ |
||||
} |
||||
|
||||
ControlResponse::ControlResponse(uint32_t code, const std::string& text) |
||||
: m_code(code) |
||||
, m_text(text) |
||||
{ |
||||
} |
||||
|
||||
ControlResponse::ControlResponse(const Block& block) |
||||
{ |
||||
wireDecode(block); |
||||
} |
||||
|
||||
const Block& |
||||
ControlResponse::wireEncode() const |
||||
{ |
||||
if (m_wire.hasWire()) |
||||
return m_wire; |
||||
|
||||
m_wire = Block(tlv::nfd::ControlResponse); |
||||
m_wire.push_back(makeNonNegativeIntegerBlock(tlv::nfd::StatusCode, m_code)); |
||||
|
||||
m_wire.push_back(makeBinaryBlock(tlv::nfd::StatusText, m_text.c_str(), m_text.size())); |
||||
|
||||
if (m_body.hasWire()) { |
||||
m_wire.push_back(m_body); |
||||
} |
||||
|
||||
m_wire.encode(); |
||||
return m_wire; |
||||
} |
||||
|
||||
void |
||||
ControlResponse::wireDecode(const Block& wire) |
||||
{ |
||||
m_wire = wire; |
||||
m_wire.parse(); |
||||
|
||||
if (m_wire.type() != tlv::nfd::ControlResponse) |
||||
BOOST_THROW_EXCEPTION(Error("Requested decoding of ControlResponse, but Block is of different " |
||||
"type")); |
||||
|
||||
Block::element_const_iterator val = m_wire.elements_begin(); |
||||
if (val == m_wire.elements_end() || |
||||
val->type() != tlv::nfd::StatusCode) |
||||
{ |
||||
BOOST_THROW_EXCEPTION(Error("Incorrect ControlResponse format (StatusCode missing or not the " |
||||
"first item)")); |
||||
} |
||||
|
||||
m_code = readNonNegativeInteger(*val); |
||||
++val; |
||||
|
||||
if (val == m_wire.elements_end() || |
||||
val->type() != tlv::nfd::StatusText) |
||||
{ |
||||
BOOST_THROW_EXCEPTION(Error("Incorrect ControlResponse format (StatusText missing or not the " |
||||
"second item)")); |
||||
} |
||||
m_text.assign(reinterpret_cast<const char*>(val->value()), val->value_size()); |
||||
++val; |
||||
|
||||
if (val != m_wire.elements_end()) |
||||
m_body = *val; |
||||
else |
||||
m_body = Block(); |
||||
} |
||||
|
||||
std::ostream& |
||||
operator<<(std::ostream& os, const ControlResponse& response) |
||||
{ |
||||
os << response.getCode() << " " << response.getText(); |
||||
return os; |
||||
} |
||||
|
||||
} // namespace nfd
|
||||
} // namespace ndn
|
@ -0,0 +1,49 @@
|
||||
/* -*- 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 NDN_MGMT_CONTROL_PARAMETERS_HPP |
||||
#define NDN_MGMT_CONTROL_PARAMETERS_HPP |
||||
|
||||
#include "../encoding/block.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
/** \brief base class for a struct that contains ControlCommand parameters
|
||||
*/ |
||||
class ControlParameters |
||||
{ |
||||
public: |
||||
virtual void |
||||
wireDecode(const Block& wire) = 0; |
||||
|
||||
virtual Block |
||||
wireEncode() const = 0; |
||||
}; |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
||||
|
||||
#endif // NDN_MGMT_CONTROL_PARAMETERS_HPP
|
@ -0,0 +1,111 @@
|
||||
/* -*- 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 "control-response.hpp" |
||||
#include "../encoding/block-helpers.hpp" |
||||
#include "../encoding/tlv-nfd.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
// BOOST_CONCEPT_ASSERT((boost::EqualityComparable<ControlResponse>));
|
||||
BOOST_CONCEPT_ASSERT((WireEncodable<ControlResponse>)); |
||||
BOOST_CONCEPT_ASSERT((WireDecodable<ControlResponse>)); |
||||
static_assert(std::is_base_of<tlv::Error, ControlResponse::Error>::value, |
||||
"ControlResponse::Error must inherit from tlv::Error"); |
||||
|
||||
ControlResponse::ControlResponse() |
||||
: m_code(200) |
||||
{ |
||||
} |
||||
|
||||
ControlResponse::ControlResponse(uint32_t code, const std::string& text) |
||||
: m_code(code) |
||||
, m_text(text) |
||||
{ |
||||
} |
||||
|
||||
ControlResponse::ControlResponse(const Block& block) |
||||
{ |
||||
wireDecode(block); |
||||
} |
||||
|
||||
const Block& |
||||
ControlResponse::wireEncode() const |
||||
{ |
||||
if (m_wire.hasWire()) |
||||
return m_wire; |
||||
|
||||
m_wire = Block(tlv::nfd::ControlResponse); |
||||
m_wire.push_back(nonNegativeIntegerBlock(tlv::nfd::StatusCode, m_code)); |
||||
|
||||
m_wire.push_back(dataBlock(tlv::nfd::StatusText, m_text.c_str(), m_text.size())); |
||||
|
||||
if (m_body.hasWire()) { |
||||
m_wire.push_back(m_body); |
||||
} |
||||
|
||||
m_wire.encode(); |
||||
return m_wire; |
||||
} |
||||
|
||||
void |
||||
ControlResponse::wireDecode(const Block& wire) |
||||
{ |
||||
m_wire = wire; |
||||
m_wire.parse(); |
||||
|
||||
if (m_wire.type() != tlv::nfd::ControlResponse) |
||||
throw Error("Requested decoding of ControlResponse, but Block is of different type"); |
||||
|
||||
Block::element_const_iterator val = m_wire.elements_begin(); |
||||
if (val == m_wire.elements_end() || val->type() != tlv::nfd::StatusCode) { |
||||
throw Error("Incorrect ControlResponse format (StatusCode missing or not the first item)"); |
||||
} |
||||
|
||||
m_code = readNonNegativeInteger(*val); |
||||
++val; |
||||
|
||||
if (val == m_wire.elements_end() || val->type() != tlv::nfd::StatusText) { |
||||
throw Error("Incorrect ControlResponse format (StatusText missing or not the second item)"); |
||||
} |
||||
m_text.assign(reinterpret_cast<const char*>(val->value()), val->value_size()); |
||||
++val; |
||||
|
||||
if (val != m_wire.elements_end()) |
||||
m_body = *val; |
||||
else |
||||
m_body = Block(); |
||||
} |
||||
|
||||
std::ostream& |
||||
operator<<(std::ostream& os, const ControlResponse& response) |
||||
{ |
||||
os << response.getCode() << " " << response.getText(); |
||||
return os; |
||||
} |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
@ -0,0 +1,137 @@
|
||||
/* -*- 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 NDN_MGMT_CONTROL_RESPONSE_HPP |
||||
#define NDN_MGMT_CONTROL_RESPONSE_HPP |
||||
|
||||
#include "../encoding/block.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
/** \brief ControlCommand response
|
||||
*/ |
||||
class ControlResponse |
||||
{ |
||||
public: |
||||
class Error : public tlv::Error |
||||
{ |
||||
public: |
||||
explicit |
||||
Error(const std::string& what) |
||||
: tlv::Error(what) |
||||
{ |
||||
} |
||||
}; |
||||
|
||||
ControlResponse(); |
||||
|
||||
ControlResponse(uint32_t code, const std::string& text); |
||||
|
||||
explicit |
||||
ControlResponse(const Block& block); |
||||
|
||||
uint32_t |
||||
getCode() const; |
||||
|
||||
ControlResponse& |
||||
setCode(uint32_t code); |
||||
|
||||
const std::string& |
||||
getText() const; |
||||
|
||||
ControlResponse& |
||||
setText(const std::string& text); |
||||
|
||||
const Block& |
||||
getBody() const; |
||||
|
||||
ControlResponse& |
||||
setBody(const Block& body); |
||||
|
||||
const Block& |
||||
wireEncode() const; |
||||
|
||||
void |
||||
wireDecode(const Block& block); |
||||
|
||||
protected: |
||||
uint32_t m_code; |
||||
std::string m_text; |
||||
Block m_body; |
||||
|
||||
mutable Block m_wire; |
||||
}; |
||||
|
||||
inline uint32_t |
||||
ControlResponse::getCode() const |
||||
{ |
||||
return m_code; |
||||
} |
||||
|
||||
inline ControlResponse& |
||||
ControlResponse::setCode(uint32_t code) |
||||
{ |
||||
m_code = code; |
||||
m_wire.reset(); |
||||
return *this; |
||||
} |
||||
|
||||
inline const std::string& |
||||
ControlResponse::getText() const |
||||
{ |
||||
return m_text; |
||||
} |
||||
|
||||
inline ControlResponse& |
||||
ControlResponse::setText(const std::string& text) |
||||
{ |
||||
m_text = text; |
||||
m_wire.reset(); |
||||
return *this; |
||||
} |
||||
|
||||
inline const Block& |
||||
ControlResponse::getBody() const |
||||
{ |
||||
return m_body; |
||||
} |
||||
|
||||
inline ControlResponse& |
||||
ControlResponse::setBody(const Block& body) |
||||
{ |
||||
m_body = body; |
||||
m_body.encode(); // will do nothing if already encoded
|
||||
m_wire.reset(); |
||||
return *this; |
||||
} |
||||
|
||||
std::ostream& |
||||
operator<<(std::ostream& os, const ControlResponse& response); |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
||||
|
||||
#endif // NDN_MGMT_CONTRO_RESPONSE_HPP
|
@ -0,0 +1,310 @@
|
||||
/* -*- 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 "dispatcher.hpp" |
||||
|
||||
#include <algorithm> |
||||
|
||||
// #define NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING
|
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
Authorization |
||||
makeAcceptAllAuthorization() |
||||
{ |
||||
return [] (const Name& prefix, |
||||
const Interest& interest, |
||||
const ControlParameters* params, |
||||
AcceptContinuation accept, |
||||
RejectContinuation reject) { |
||||
accept(""); |
||||
}; |
||||
} |
||||
|
||||
Dispatcher::Dispatcher(Face& face, security::KeyChain& keyChain, |
||||
const security::SigningInfo& signingInfo) |
||||
: m_face(face) |
||||
, m_keyChain(keyChain) |
||||
, m_signingInfo(signingInfo) |
||||
{ |
||||
} |
||||
|
||||
Dispatcher::~Dispatcher() |
||||
{ |
||||
std::vector<Name> topPrefixNames; |
||||
|
||||
std::transform(m_topLevelPrefixes.begin(), |
||||
m_topLevelPrefixes.end(), |
||||
std::back_inserter(topPrefixNames), |
||||
[] (const std::unordered_map<Name, TopPrefixEntry>::value_type& entry) { |
||||
return entry.second.topPrefix; |
||||
}); |
||||
|
||||
for (auto&& name : topPrefixNames) { |
||||
removeTopPrefix(name); |
||||
} |
||||
} |
||||
|
||||
void |
||||
Dispatcher::addTopPrefix(const Name& prefix, |
||||
bool wantRegister, |
||||
const security::SigningInfo& signingInfo) |
||||
{ |
||||
bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(), |
||||
m_topLevelPrefixes.end(), |
||||
[&] (const std::unordered_map<Name, TopPrefixEntry>::value_type& x) { |
||||
return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first); |
||||
}); |
||||
if (hasOverlap) { |
||||
BOOST_THROW_EXCEPTION(std::out_of_range("Top-level Prefixes overlapped")); |
||||
} |
||||
|
||||
TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];; |
||||
topPrefixEntry.topPrefix = prefix; |
||||
topPrefixEntry.wantRegister = wantRegister; |
||||
|
||||
if (wantRegister) { |
||||
RegisterPrefixFailureCallback failure = [] (const Name& name, const std::string& reason) { |
||||
BOOST_THROW_EXCEPTION(std::runtime_error(reason)); |
||||
}; |
||||
topPrefixEntry.registerPrefixId = |
||||
m_face.registerPrefix(prefix, bind([]{}), failure, signingInfo); |
||||
} |
||||
|
||||
for (auto&& entry : m_handlers) { |
||||
Name fullPrefix = prefix; |
||||
fullPrefix.append(entry.first); |
||||
|
||||
const InterestFilterId* interestFilterId = |
||||
m_face.setInterestFilter(fullPrefix, std::bind(entry.second, prefix, _2)); |
||||
|
||||
topPrefixEntry.interestFilters.push_back(interestFilterId); |
||||
} |
||||
} |
||||
|
||||
void |
||||
Dispatcher::removeTopPrefix(const Name& prefix) |
||||
{ |
||||
auto it = m_topLevelPrefixes.find(prefix); |
||||
if (it == m_topLevelPrefixes.end()) { |
||||
return; |
||||
} |
||||
|
||||
const TopPrefixEntry& topPrefixEntry = it->second; |
||||
if (topPrefixEntry.wantRegister) { |
||||
m_face.unregisterPrefix(topPrefixEntry.registerPrefixId, bind([]{}), bind([]{})); |
||||
} |
||||
|
||||
for (auto&& filter : topPrefixEntry.interestFilters) { |
||||
m_face.unsetInterestFilter(filter); |
||||
} |
||||
|
||||
m_topLevelPrefixes.erase(it); |
||||
} |
||||
|
||||
bool |
||||
Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix) |
||||
{ |
||||
bool hasOverlapWithHandlers = |
||||
std::any_of(m_handlers.begin(), m_handlers.end(), |
||||
[&] (const HandlerMap::value_type& entry) { |
||||
return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first); |
||||
}); |
||||
bool hasOverlapWithStreams = |
||||
std::any_of(m_streams.begin(), m_streams.end(), |
||||
[&] (const std::unordered_map<PartialName, uint64_t>::value_type& entry) { |
||||
return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first); |
||||
}); |
||||
|
||||
return hasOverlapWithHandlers || hasOverlapWithStreams; |
||||
} |
||||
|
||||
void |
||||
Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest) |
||||
{ |
||||
if (act == RejectReply::STATUS403) { |
||||
sendControlResponse(ControlResponse(403, "authorization rejected"), interest); |
||||
} |
||||
} |
||||
|
||||
void |
||||
Dispatcher::sendData(const Name& dataName, const Block& content, |
||||
const MetaInfo& metaInfo) |
||||
{ |
||||
shared_ptr<Data> data = make_shared<Data>(dataName); |
||||
data->setContent(content).setMetaInfo(metaInfo); |
||||
|
||||
m_keyChain.sign(*data, m_signingInfo); |
||||
|
||||
try { |
||||
m_face.put(*data); |
||||
} |
||||
catch (Face::Error& e) { |
||||
#ifdef NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING |
||||
std::clog << e.what() << std::endl; |
||||
#endif // NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING.
|
||||
} |
||||
} |
||||
|
||||
void |
||||
Dispatcher::processControlCommandInterest(const Name& prefix, |
||||
const Name& relPrefix, |
||||
const Interest& interest, |
||||
const ControlParametersParser& parser, |
||||
const Authorization& authorization, |
||||
const AuthorizationAcceptedCallback& accepted, |
||||
const AuthorizationRejectedCallback& rejected) |
||||
{ |
||||
// /<prefix>/<relPrefix>/<parameters>
|
||||
size_t parametersLoc = prefix.size() + relPrefix.size(); |
||||
const name::Component& pc = interest.getName().get(parametersLoc); |
||||
|
||||
shared_ptr<ControlParameters> parameters; |
||||
try { |
||||
parameters = parser(pc); |
||||
} |
||||
catch (tlv::Error& e) { |
||||
return; |
||||
} |
||||
|
||||
AcceptContinuation accept = bind(accepted, _1, prefix, interest, parameters.get()); |
||||
RejectContinuation reject = bind(rejected, _1, interest); |
||||
authorization(prefix, interest, parameters.get(), accept, reject); |
||||
} |
||||
|
||||
void |
||||
Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester, |
||||
const Name& prefix, |
||||
const Interest& interest, |
||||
const ControlParameters* parameters, |
||||
const ValidateParameters& validateParams, |
||||
const ControlCommandHandler& handler) |
||||
{ |
||||
if (validateParams(*parameters)) { |
||||
handler(prefix, interest, *parameters, |
||||
bind(&Dispatcher::sendControlResponse, this, _1, interest, false)); |
||||
} |
||||
else { |
||||
sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest); |
||||
} |
||||
} |
||||
|
||||
void |
||||
Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest, |
||||
bool isNack/*= false*/) |
||||
{ |
||||
MetaInfo info; |
||||
if (isNack) { |
||||
info.setType(tlv::ContentType_Nack); |
||||
} |
||||
|
||||
sendData(interest.getName(), resp.wireEncode(), info); |
||||
} |
||||
|
||||
void |
||||
Dispatcher::addStatusDataset(const PartialName& relPrefix, |
||||
Authorization authorization, |
||||
StatusDatasetHandler handler) |
||||
{ |
||||
if (!m_topLevelPrefixes.empty()) { |
||||
BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added")); |
||||
} |
||||
|
||||
if (isOverlappedWithOthers(relPrefix)) { |
||||
BOOST_THROW_EXCEPTION(std::out_of_range("relPrefix overlapped")); |
||||
} |
||||
|
||||
AuthorizationAcceptedCallback accepted = |
||||
bind(&Dispatcher::processAuthorizedStatusDatasetInterest, this, |
||||
_1, _2, _3, handler); |
||||
AuthorizationRejectedCallback rejected = |
||||
bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2); |
||||
m_handlers[relPrefix] = bind(&Dispatcher::processStatusDatasetInterest, this, |
||||
_1, _2, authorization, accepted, rejected); |
||||
} |
||||
|
||||
void |
||||
Dispatcher::processStatusDatasetInterest(const Name& prefix, |
||||
const Interest& interest, |
||||
const Authorization& authorization, |
||||
const AuthorizationAcceptedCallback& accepted, |
||||
const AuthorizationRejectedCallback& rejected) |
||||
{ |
||||
const Name& interestName = interest.getName(); |
||||
bool endsWithVersionOrSegment = interestName.size() >= 1 && |
||||
(interestName[-1].isVersion() || interestName[-1].isSegment()); |
||||
if (endsWithVersionOrSegment) { |
||||
return; |
||||
} |
||||
|
||||
AcceptContinuation accept = bind(accepted, _1, prefix, interest, nullptr); |
||||
RejectContinuation reject = bind(rejected, _1, interest); |
||||
authorization(prefix, interest, nullptr, accept, reject); |
||||
} |
||||
|
||||
void |
||||
Dispatcher::processAuthorizedStatusDatasetInterest(const std::string& requester, |
||||
const Name& prefix, |
||||
const Interest& interest, |
||||
const StatusDatasetHandler& handler) |
||||
{ |
||||
StatusDatasetContext context(interest, bind(&Dispatcher::sendData, this, _1, _2, _3)); |
||||
handler(prefix, interest, context); |
||||
} |
||||
|
||||
PostNotification |
||||
Dispatcher::addNotificationStream(const PartialName& relPrefix) |
||||
{ |
||||
if (!m_topLevelPrefixes.empty()) { |
||||
throw std::domain_error("one or more top-level prefix has been added"); |
||||
} |
||||
|
||||
if (isOverlappedWithOthers(relPrefix)) { |
||||
throw std::out_of_range("relPrefix overlaps with another relPrefix"); |
||||
} |
||||
|
||||
m_streams[relPrefix] = 0; |
||||
return bind(&Dispatcher::postNotification, this, _1, relPrefix); |
||||
} |
||||
|
||||
void |
||||
Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix) |
||||
{ |
||||
if (m_topLevelPrefixes.empty() || m_topLevelPrefixes.size() > 1) { |
||||
#ifdef NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING |
||||
std::clog << "no top-level prefix or too many top-level prefixes" << std::endl; |
||||
#endif // NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING.
|
||||
return; |
||||
} |
||||
|
||||
Name streamName(m_topLevelPrefixes.begin()->second.topPrefix); |
||||
streamName.append(relPrefix); |
||||
streamName.appendSequenceNumber(m_streams[streamName]++); |
||||
sendData(streamName, notification, MetaInfo()); |
||||
} |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
@ -0,0 +1,451 @@
|
||||
/* -*- 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 NDN_MGMT_DISPATCHER_HPP |
||||
#define NDN_MGMT_DISPATCHER_HPP |
||||
|
||||
#include "../face.hpp" |
||||
#include "../security/key-chain.hpp" |
||||
#include "../encoding/block.hpp" |
||||
#include "control-response.hpp" |
||||
#include "control-parameters.hpp" |
||||
#include "status-dataset-context.hpp" |
||||
|
||||
#include <unordered_map> |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
// ---- AUTHORIZATION ----
|
||||
|
||||
/** \brief a function to be called if authorization is successful
|
||||
* \param requester a string that indicates the requester, whose semantics is determined by |
||||
* the Authorization function; this value is intended for logging only, |
||||
* and should not affect how the request is processed |
||||
*/ |
||||
typedef std::function<void(const std::string& requester)> AcceptContinuation; |
||||
|
||||
/** \brief indicate how to reply in case authorization is rejected
|
||||
*/ |
||||
enum class RejectReply { |
||||
/** \brief do not reply
|
||||
*/ |
||||
SILENT, |
||||
/** \brief reply with a ControlResponse where StatusCode is 403
|
||||
*/ |
||||
STATUS403 |
||||
}; |
||||
|
||||
/** \brief a function to be called if authorization is rejected
|
||||
*/ |
||||
typedef std::function<void(RejectReply act)> RejectContinuation; |
||||
|
||||
/** \brief a function that performs authorization
|
||||
* \param prefix top-level prefix, e.g., "/localhost/nfd"; |
||||
* This argument can be inspected to allow Interests only under a subset of |
||||
* top-level prefixes (e.g., allow "/localhost/nfd" only), |
||||
* or to use different trust model regarding to the prefix. |
||||
* \param interest incoming Interest |
||||
* \param params parsed ControlParameters for ControlCommand, otherwise nullptr; |
||||
* This is guaranteed to be not-null and have correct type for the command, |
||||
* but may not be valid (e.g., can have missing fields). |
||||
* |
||||
* Either accept or reject must be called after authorization completes. |
||||
*/ |
||||
typedef std::function<void(const Name& prefix, const Interest& interest, |
||||
const ControlParameters* params, |
||||
AcceptContinuation accept, |
||||
RejectContinuation reject)> Authorization; |
||||
|
||||
/** \return an Authorization that accepts all Interests, with empty string as requester
|
||||
*/ |
||||
Authorization |
||||
makeAcceptAllAuthorization(); |
||||
|
||||
// ---- CONTROL COMMAND ----
|
||||
|
||||
/** \brief a function to validate input ControlParameters
|
||||
* \param params parsed ControlParameters; |
||||
* This is guaranteed to have correct type for the command. |
||||
*/ |
||||
typedef std::function<bool(const ControlParameters& params)> ValidateParameters; |
||||
|
||||
/** \brief a function to be called after ControlCommandHandler completes
|
||||
* \param resp the response to be sent to requester |
||||
*/ |
||||
typedef std::function<void(const ControlResponse& resp)> CommandContinuation; |
||||
|
||||
/** \brief a function to handle an authorized ControlCommand
|
||||
* \param prefix top-level prefix, e.g., "/localhost/nfd"; |
||||
* \param interest incoming Interest |
||||
* \param params parsed ControlParameters; |
||||
* This is guaranteed to have correct type for the command, |
||||
* and is valid (e.g., has all required fields). |
||||
*/ |
||||
typedef std::function<void(const Name& prefix, const Interest& interest, |
||||
const ControlParameters& params, |
||||
CommandContinuation done)> ControlCommandHandler; |
||||
|
||||
|
||||
/** \brief a function to handle a StatusDataset request
|
||||
* \param prefix top-level prefix, e.g., "/localhost/nfd"; |
||||
* \param interest incoming Interest; its Name doesn't contain version and segment components |
||||
* |
||||
* This function can generate zero or more blocks and pass them to \p append, |
||||
* and must call \p end upon completion. |
||||
*/ |
||||
typedef std::function<void(const Name& prefix, const Interest& interest, |
||||
StatusDatasetContext& context)> StatusDatasetHandler; |
||||
|
||||
//---- NOTIFICATION STREAM ----
|
||||
|
||||
/** \brief a function to post a notification
|
||||
*/ |
||||
typedef std::function<void(const Block& notification)> PostNotification; |
||||
|
||||
// ---- DISPATCHER ----
|
||||
|
||||
/** \brief represents a dispatcher on server side of NFD Management protocol
|
||||
*/ |
||||
class Dispatcher : noncopyable |
||||
{ |
||||
class Error : public std::runtime_error |
||||
{ |
||||
public: |
||||
explicit |
||||
Error(const std::string& what) |
||||
: std::runtime_error(what) |
||||
{ |
||||
} |
||||
}; |
||||
|
||||
public: |
||||
/** \brief constructor
|
||||
* \param face the Face on which the dispatcher operates |
||||
* \param keyChain a KeyChain to sign Data |
||||
* \param signingInfo signing parameters to sign Data with \p keyChain |
||||
*/ |
||||
Dispatcher(Face& face, security::KeyChain& keyChain, |
||||
const security::SigningInfo& signingInfo = security::SigningInfo()); |
||||
|
||||
virtual |
||||
~Dispatcher(); |
||||
|
||||
/** \brief add a top-level prefix
|
||||
* \param prefix a top-level prefix, e.g., "/localhost/nfd" |
||||
* \param wantRegister whether prefix registration should be performed through the Face |
||||
* \param signingInfo signing parameters to sign the prefix registration command |
||||
* \throw std::out_of_range \p prefix overlaps with an existing top-level prefix |
||||
* |
||||
* Procedure for adding a top-level prefix: |
||||
* 1. if the new top-level prefix overlaps with an existing top-level prefix |
||||
* (one top-level prefix is a prefix of another top-level prefix), throw std::domain_error |
||||
* 2. if wantRegister is true, invoke face.registerPrefix for the top-level prefix; |
||||
* the returned RegisteredPrefixId shall be recorded internally, indexed by the top-level |
||||
* prefix |
||||
* 3. foreach relPrefix from ControlCommands and StatusDatasets, |
||||
* join the top-level prefix with the relPrefix to obtain the full prefix, |
||||
* and invoke non-registering overload of face.setInterestFilter, |
||||
* with the InterestHandler set to an appropriate private method to handle incoming Interests |
||||
* for the ControlCommand or StatusDataset; |
||||
* the returned InterestFilterId shall be recorded internally, indexed by the top-level |
||||
* prefix |
||||
*/ |
||||
void |
||||
addTopPrefix(const Name& prefix, bool wantRegister = true, |
||||
const security::SigningInfo& signingInfo = security::SigningInfo()); |
||||
|
||||
/** \brief remove a top-level prefix
|
||||
* \param prefix a top-level prefix, e.g., "/localhost/nfd" |
||||
* |
||||
* Procedure for removing a top-level prefix: |
||||
* 1. if the top-level prefix has not been added, abort these steps |
||||
* 2. if the top-level prefix has been added with wantRegister, |
||||
* invoke face.unregisterPrefix with the RegisteredPrefixId |
||||
* 3. foreach InterestFilterId recorded during addTopPrefix, |
||||
* invoke face.unsetInterestFilter with the InterestFilterId |
||||
*/ |
||||
void |
||||
removeTopPrefix(const Name& prefix); |
||||
|
||||
public: // ControlCommand
|
||||
/** \brief register a ControlCommand
|
||||
* \tparam CP subclass of ControlParameters used by this command |
||||
* \param relPrefix a prefix for this command, e.g., "faces/create"; |
||||
* relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be |
||||
* non-overlapping |
||||
* (no relPrefix is a prefix of another relPrefix) |
||||
* \pre no top-level prefix has been added |
||||
* \throw std::out_of_range \p relPrefix overlaps with an existing relPrefix |
||||
* \throw std::domain_error one or more top-level prefix has been added |
||||
* |
||||
* Procedure for processing a ControlCommand: |
||||
* 1. extract the NameComponent containing ControlParameters (the component after relPrefix), |
||||
* and parse ControlParameters into type CP; if parsing fails, abort these steps |
||||
* 2. perform authorization; if authorization is rejected, |
||||
* perform the RejectReply action, and abort these steps |
||||
* 3. validate ControlParameters; if validation fails, |
||||
* make ControlResponse with StatusCode 400, and go to step 5 |
||||
* 4. invoke handler, wait until CommandContinuation is called |
||||
* 5. encode the ControlResponse into one Data packet |
||||
* 6. sign the Data packet |
||||
* 7. if the Data packet is too large, abort these steps and log an error |
||||
* 8. send the signed Data packet |
||||
*/ |
||||
template<typename CP> |
||||
void |
||||
addControlCommand(const PartialName& relPrefix, |
||||
Authorization authorization, |
||||
ValidateParameters validateParams, |
||||
ControlCommandHandler handler); |
||||
|
||||
public: // StatusDataset
|
||||
/** \brief register a StatusDataset or a prefix under which StatusDatasets can be requested
|
||||
* \param relPrefix a prefix for this dataset, e.g., "faces/list"; |
||||
* relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be |
||||
* non-overlapping |
||||
* (no relPrefix is a prefix of another relPrefix) |
||||
* \param authorization should set identity to Name() if the dataset is public |
||||
* \pre no top-level prefix has been added |
||||
* \throw std::out_of_range \p relPrefix overlaps with an existing relPrefix |
||||
* \throw std::domain_error one or more top-level prefix has been added |
||||
* |
||||
* The payload of the returned status dataset data packet is at most half of the maximum |
||||
* data packet size. |
||||
* |
||||
* Procedure for processing a StatusDataset request: |
||||
* 1. if the request Interest contains version or segment components, abort these steps; |
||||
* note: the request may contain more components after relPrefix, e.g., a query condition |
||||
* 2. perform authorization; if authorization is rejected, |
||||
* perform the RejectReply action, and abort these steps |
||||
* 3. invoke handler, store blocks passed to StatusDatasetAppend calls in a buffer, |
||||
* wait until StatusDatasetEnd is called |
||||
* 4. allocate a version |
||||
* 5. segment the buffer into one or more segments under the allocated version, |
||||
* such that the Data packets will not become too large after signing |
||||
* 6. set FinalBlockId on at least the last segment |
||||
* 7. sign the Data packets |
||||
* 8. send the signed Data packets |
||||
* |
||||
* As an optimization, a Data packet may be sent as soon as enough octets have been collected |
||||
* through StatusDatasetAppend calls. |
||||
*/ |
||||
void |
||||
addStatusDataset(const PartialName& relPrefix, |
||||
Authorization authorization, |
||||
StatusDatasetHandler handler); |
||||
|
||||
public: // NotificationStream
|
||||
/** \brief register a NotificationStream
|
||||
* \param relPrefix a prefix for this notification stream, e.g., "faces/events"; |
||||
* relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be |
||||
* non-overlapping |
||||
* (no relPrefix is a prefix of another relPrefix) |
||||
* \return a function into which notifications can be posted |
||||
* \pre no top-level prefix has been added |
||||
* \throw std::out_of_range \p relPrefix overlaps with an existing relPrefix |
||||
* \throw std::domain_error one or more top-level prefix has been added |
||||
* |
||||
* Procedure for posting a notification: |
||||
* 1. if no top-level prefix has been added, or more than one top-level prefixes have been |
||||
* added, |
||||
* abort these steps and log an error |
||||
* 2. assign the next sequence number to the notification |
||||
* 3. place the notification block into one Data packet under the sole top-level prefix |
||||
* 4. sign the Data packet |
||||
* 5. if the Data packet is too large, abort these steps and log an error |
||||
* 6. send the signed Data packet |
||||
*/ |
||||
PostNotification |
||||
addNotificationStream(const PartialName& relPrefix); |
||||
|
||||
private: |
||||
typedef std::function<void(const Name& prefix, |
||||
const Interest& interest)> InterestHandler; |
||||
|
||||
typedef std::function<void(const std::string& requester, |
||||
const Name& prefix, |
||||
const Interest& interest, |
||||
const ControlParameters*)> AuthorizationAcceptedCallback; |
||||
|
||||
typedef std::function<void(RejectReply act, |
||||
const Interest& interest)> AuthorizationRejectedCallback; |
||||
|
||||
/**
|
||||
* @brief the parser of extracting control parameters from name component. |
||||
* |
||||
* @param component name component that may encode control parameters. |
||||
* @return a shared pointer to the extracted control parameters. |
||||
* @throw tlv::Error if the NameComponent cannot be parsed as ControlParameters |
||||
*/ |
||||
typedef std::function<shared_ptr<ControlParameters>(const name::Component& component)> |
||||
ControlParametersParser; |
||||
|
||||
bool |
||||
isOverlappedWithOthers(const PartialName& relPrefix); |
||||
|
||||
/**
|
||||
* @brief process unauthorized request |
||||
* |
||||
* @param act action to reply |
||||
* @param interest the incoming Interest |
||||
*/ |
||||
void |
||||
afterAuthorizationRejected(RejectReply act, const Interest& interest); |
||||
|
||||
void |
||||
sendData(const Name& dataName, const Block& content, |
||||
const MetaInfo& metaInfo); |
||||
|
||||
/**
|
||||
* @brief process the control-command Interest before authorization. |
||||
* |
||||
* @param prefix the top-level prefix |
||||
* @param relPrefix the relative prefix |
||||
* @param interest the incoming Interest |
||||
* @param parser to extract control parameters from the \p interest |
||||
* @param authorization to process validation on this command |
||||
* @param accepted the callback for successful authorization |
||||
* @param rejected the callback for failed authorization |
||||
*/ |
||||
void |
||||
processControlCommandInterest(const Name& prefix, |
||||
const Name& relPrefix, |
||||
const Interest& interest, |
||||
const ControlParametersParser& parser, |
||||
const Authorization& authorization, |
||||
const AuthorizationAcceptedCallback& accepted, |
||||
const AuthorizationRejectedCallback& rejected); |
||||
|
||||
/**
|
||||
* @brief process the authorized control-command. |
||||
* |
||||
* @param requester the requester |
||||
* @param prefix the top-level prefix |
||||
* @param interest the incoming Interest |
||||
* @param parameters control parameters of this command |
||||
* @param validate to validate control parameters |
||||
* @param handler to process this command |
||||
*/ |
||||
void |
||||
processAuthorizedControlCommandInterest(const std::string& requester, |
||||
const Name& prefix, |
||||
const Interest& interest, |
||||
const ControlParameters* parameters, |
||||
const ValidateParameters& validate, |
||||
const ControlCommandHandler& handler); |
||||
|
||||
void |
||||
sendControlResponse(const ControlResponse& resp, const Interest& interest, bool isNack = false); |
||||
|
||||
/**
|
||||
* @brief process the status-dataset Interest before authorization. |
||||
* |
||||
* @param prefix the top-level prefix |
||||
* @param interest the incoming Interest |
||||
* @param authorization to process verification |
||||
* @param accepted callback for successful authorization |
||||
* @param rejected callback for failed authorization |
||||
*/ |
||||
void |
||||
processStatusDatasetInterest(const Name& prefix, |
||||
const Interest& interest, |
||||
const Authorization& authorization, |
||||
const AuthorizationAcceptedCallback& accepted, |
||||
const AuthorizationRejectedCallback& rejected); |
||||
|
||||
/**
|
||||
* @brief process the authorized status-dataset request |
||||
* |
||||
* @param requester the requester |
||||
* @param prefix the top-level prefix |
||||
* @param interest the incoming Interest |
||||
* @param handler to process this request |
||||
*/ |
||||
void |
||||
processAuthorizedStatusDatasetInterest(const std::string& requester, |
||||
const Name& prefix, |
||||
const Interest& interest, |
||||
const StatusDatasetHandler& handler); |
||||
|
||||
void |
||||
postNotification(const Block& notification, const PartialName& relPrefix); |
||||
|
||||
private: |
||||
struct TopPrefixEntry |
||||
{ |
||||
Name topPrefix; |
||||
bool wantRegister; |
||||
const ndn::RegisteredPrefixId* registerPrefixId; |
||||
std::vector<const ndn::InterestFilterId*> interestFilters; |
||||
}; |
||||
std::unordered_map<Name, TopPrefixEntry> m_topLevelPrefixes; |
||||
|
||||
Face& m_face; |
||||
security::KeyChain& m_keyChain; |
||||
security::SigningInfo m_signingInfo; |
||||
|
||||
typedef std::unordered_map<PartialName, InterestHandler> HandlerMap; |
||||
typedef HandlerMap::iterator HandlerMapIt; |
||||
HandlerMap m_handlers; |
||||
|
||||
// NotificationStream name => next sequence number
|
||||
std::unordered_map<Name, uint64_t> m_streams; |
||||
}; |
||||
|
||||
template<typename CP> |
||||
void |
||||
Dispatcher::addControlCommand(const PartialName& relPrefix, |
||||
Authorization authorization, |
||||
ValidateParameters validateParams, |
||||
ControlCommandHandler handler) |
||||
{ |
||||
if (!m_topLevelPrefixes.empty()) { |
||||
throw std::domain_error("one or more top-level prefix has been added"); |
||||
} |
||||
|
||||
if (isOverlappedWithOthers(relPrefix)) { |
||||
throw std::out_of_range("relPrefix overlaps with another relPrefix"); |
||||
} |
||||
|
||||
ControlParametersParser parser = |
||||
[] (const name::Component& component) -> shared_ptr<ControlParameters> { |
||||
return make_shared<CP>(component.blockFromValue()); |
||||
}; |
||||
|
||||
AuthorizationAcceptedCallback accepted = |
||||
bind(&Dispatcher::processAuthorizedControlCommandInterest, this, |
||||
_1, _2, _3, _4, validateParams, handler); |
||||
|
||||
AuthorizationRejectedCallback rejected = |
||||
bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2); |
||||
|
||||
m_handlers[relPrefix] = bind(&Dispatcher::processControlCommandInterest, this, |
||||
_1, relPrefix, _2, parser, authorization, accepted, rejected); |
||||
} |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
||||
#endif // NDN_MGMT_DISPATCHER_HPP
|
@ -0,0 +1,139 @@
|
||||
/* -*- 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 "status-dataset-context.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
const time::milliseconds DEFAULT_STATUS_DATASET_FRESHNESS_PERIOD = time::milliseconds(1000); |
||||
|
||||
const Name& |
||||
StatusDatasetContext::getPrefix() const |
||||
{ |
||||
return m_prefix; |
||||
} |
||||
|
||||
StatusDatasetContext& |
||||
StatusDatasetContext::setPrefix(const Name& prefix) |
||||
{ |
||||
if (!m_interest.getName().isPrefixOf(prefix)) { |
||||
BOOST_THROW_EXCEPTION(std::invalid_argument("prefix does not start with Interest Name")); |
||||
} |
||||
|
||||
if (m_state != State::INITIAL) { |
||||
BOOST_THROW_EXCEPTION(std::domain_error("state is not in INITIAL")); |
||||
} |
||||
|
||||
m_prefix = prefix; |
||||
|
||||
if (!m_prefix[-1].isVersion()) { |
||||
m_prefix.appendVersion(); |
||||
} |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
const time::milliseconds& |
||||
StatusDatasetContext::getExpiry() const |
||||
{ |
||||
return m_expiry; |
||||
} |
||||
|
||||
StatusDatasetContext& |
||||
StatusDatasetContext::setExpiry(const time::milliseconds& expiry) |
||||
{ |
||||
m_expiry = expiry; |
||||
return *this; |
||||
} |
||||
|
||||
void |
||||
StatusDatasetContext::append(const Block& block) |
||||
{ |
||||
if (m_state == State::FINALIZED) { |
||||
BOOST_THROW_EXCEPTION(std::domain_error("state is in FINALIZED")); |
||||
} |
||||
|
||||
m_state = State::RESPONDED; |
||||
|
||||
size_t nBytesLeft = block.size(); |
||||
|
||||
while (nBytesLeft > 0) { |
||||
size_t nBytesAppend = std::min(nBytesLeft, |
||||
(ndn::MAX_NDN_PACKET_SIZE >> 1) - m_buffer->size()); |
||||
m_buffer->appendByteArray(block.wire() + (block.size() - nBytesLeft), nBytesAppend); |
||||
nBytesLeft -= nBytesAppend; |
||||
|
||||
if (nBytesLeft > 0) { |
||||
const Block& content = makeBinaryBlock(tlv::Content, m_buffer->buf(), m_buffer->size()); |
||||
m_dataSender(Name(m_prefix).appendSegment(m_segmentNo++), content, |
||||
MetaInfo().setFreshnessPeriod(m_expiry)); |
||||
|
||||
m_buffer = std::make_shared<EncodingBuffer>(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void |
||||
StatusDatasetContext::end() |
||||
{ |
||||
if (m_state == State::FINALIZED) { |
||||
BOOST_THROW_EXCEPTION(std::domain_error("state is in FINALIZED")); |
||||
} |
||||
|
||||
m_state = State::FINALIZED; |
||||
|
||||
auto dataName = Name(m_prefix).appendSegment(m_segmentNo++); |
||||
m_dataSender(dataName, makeBinaryBlock(tlv::Content, m_buffer->buf(), m_buffer->size()), |
||||
MetaInfo().setFreshnessPeriod(m_expiry).setFinalBlockId(dataName[-1])); |
||||
} |
||||
|
||||
void |
||||
StatusDatasetContext::reject(const ControlResponse& resp /*= a ControlResponse with 400*/) |
||||
{ |
||||
if (m_state != State::INITIAL) { |
||||
BOOST_THROW_EXCEPTION(std::domain_error("state is in REPONSED or FINALIZED")); |
||||
} |
||||
|
||||
m_state = State::FINALIZED; |
||||
|
||||
m_dataSender(m_interest.getName(), resp.wireEncode(), |
||||
MetaInfo().setType(tlv::ContentType_Nack)); |
||||
} |
||||
|
||||
StatusDatasetContext::StatusDatasetContext(const Interest& interest, |
||||
const DataSender& dataSender) |
||||
: m_interest(interest) |
||||
, m_dataSender(dataSender) |
||||
, m_expiry(DEFAULT_STATUS_DATASET_FRESHNESS_PERIOD) |
||||
, m_buffer(make_shared<EncodingBuffer>()) |
||||
, m_segmentNo(0) |
||||
, m_state(State::INITIAL) |
||||
{ |
||||
setPrefix(interest.getName()); |
||||
} |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
@ -0,0 +1,125 @@
|
||||
/* -*- 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 NDN_MGMT_STATUS_DATASET_CONTEXT_HPP |
||||
#define NDN_MGMT_STATUS_DATASET_CONTEXT_HPP |
||||
|
||||
#include "../interest.hpp" |
||||
#include "../data.hpp" |
||||
#include "../util/time.hpp" |
||||
#include "../encoding/encoding-buffer.hpp" |
||||
#include "control-response.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
|
||||
class StatusDatasetContext |
||||
{ |
||||
public: |
||||
/** \return prefix of Data packets, with version component but without segment component
|
||||
*/ |
||||
const Name& |
||||
getPrefix() const; |
||||
|
||||
/** \brief change prefix of Data packets
|
||||
* \param prefix the prefix; it must start with Interest Name, may contain version component, |
||||
* but must not contain segment component |
||||
* \throw std::invalid_argument prefix does not start with Interest Name |
||||
* \throw std::domain_error append, end, or reject has been invoked |
||||
* |
||||
* StatusDatasetHandler may change the prefix of Data packets with this method, |
||||
* before sending any response. |
||||
* The version component is optional, and will be generated from current timestamp when omitted. |
||||
*/ |
||||
StatusDatasetContext& |
||||
setPrefix(const Name& prefix); |
||||
|
||||
/** \return expiration duration for this dataset response
|
||||
*/ |
||||
const time::milliseconds& |
||||
getExpiry() const; |
||||
|
||||
/** \brief set expiration duration
|
||||
* |
||||
* The response will be cached for the specified duration. |
||||
* Incoming Interest that matches a cached response will be satisfied with that response, |
||||
* without invoking StatusDatasetHandler again. |
||||
*/ |
||||
StatusDatasetContext& |
||||
setExpiry(const time::milliseconds& expiry); |
||||
|
||||
/** \brief append a Block to the response
|
||||
* \throw std::domain_error end or reject has been invoked |
||||
*/ |
||||
void |
||||
append(const Block& block); |
||||
|
||||
/** \brief end the response successfully after appending zero or more blocks
|
||||
* \throw std::domain_error reject has been invoked |
||||
*/ |
||||
void |
||||
end(); |
||||
|
||||
/** \brief declare the non-existence of a response
|
||||
* \throw std::domain_error append or end has been invoked |
||||
* |
||||
* This should be invoked when the incoming Interest is malformed. |
||||
* A producer-generated NACK will be returned to requester. |
||||
* |
||||
* \param content Content of producer-generated NACK |
||||
*/ |
||||
void |
||||
reject(const ControlResponse& resp = ControlResponse().setCode(400)); |
||||
|
||||
NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE: |
||||
typedef std::function<void(const Name& dataName, const Block& content, |
||||
const MetaInfo& metaInfo)> DataSender; |
||||
|
||||
StatusDatasetContext(const Interest& interest, const DataSender& dataSender); |
||||
|
||||
private: |
||||
friend class Dispatcher; |
||||
|
||||
const Interest& m_interest; |
||||
DataSender m_dataSender; |
||||
Name m_prefix; |
||||
time::milliseconds m_expiry; |
||||
|
||||
NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE: |
||||
shared_ptr<EncodingBuffer> m_buffer; |
||||
uint64_t m_segmentNo; |
||||
|
||||
enum class State { |
||||
INITIAL, ///< none of .append, .end, .reject has been invoked
|
||||
RESPONDED, ///< .append has been invoked
|
||||
FINALIZED ///< .end or .reject has been invoked
|
||||
}; |
||||
State m_state; |
||||
}; |
||||
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
||||
|
||||
#endif // NDN_MGMT_STATUS_DATASET_CONTEXT_HPP
|
@ -0,0 +1,386 @@
|
||||
/* -*- 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 "mgmt/dispatcher.hpp" |
||||
#include "management/nfd-control-parameters.hpp" |
||||
#include "util/dummy-client-face.hpp" |
||||
|
||||
#include "boost-test.hpp" |
||||
#include "identity-management-fixture.hpp" |
||||
#include "unit-tests/unit-test-time-fixture.hpp" |
||||
#include "unit-tests/make-interest-data.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
namespace tests { |
||||
|
||||
using namespace ndn::tests; |
||||
|
||||
BOOST_AUTO_TEST_SUITE(MgmtDispatcher) |
||||
|
||||
class DispatcherFixture : public UnitTestTimeFixture |
||||
, public security::IdentityManagementFixture |
||||
{ |
||||
public: |
||||
DispatcherFixture() |
||||
: face(util::makeDummyClientFace(io, {true, true})) |
||||
, dispatcher(*face, m_keyChain, security::SigningInfo()) |
||||
{ |
||||
} |
||||
|
||||
public: |
||||
shared_ptr<util::DummyClientFace> face; |
||||
mgmt::Dispatcher dispatcher; |
||||
}; |
||||
|
||||
class VoidParameters : public mgmt::ControlParameters |
||||
{ |
||||
public: |
||||
explicit |
||||
VoidParameters(const Block& wire) |
||||
{ |
||||
wireDecode(wire); |
||||
} |
||||
|
||||
virtual Block |
||||
wireEncode() const NDN_CXX_DECL_FINAL |
||||
{ |
||||
return Block(128); |
||||
} |
||||
|
||||
virtual void |
||||
wireDecode(const Block& wire) NDN_CXX_DECL_FINAL |
||||
{ |
||||
if (wire.type() != 128) |
||||
throw tlv::Error("Expecting TLV type 128"); |
||||
} |
||||
}; |
||||
|
||||
static Authorization |
||||
makeTestAuthorization() |
||||
{ |
||||
return [] (const Name& prefix, |
||||
const Interest& interest, |
||||
const ControlParameters* params, |
||||
AcceptContinuation accept, |
||||
RejectContinuation reject) { |
||||
if (interest.getName()[-1] == name::Component("valid")) { |
||||
accept(""); |
||||
} |
||||
else { |
||||
if (interest.getName()[-1] == name::Component("silent")) { |
||||
reject(RejectReply::SILENT); |
||||
} |
||||
else { |
||||
reject(RejectReply::STATUS403); |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
BOOST_FIXTURE_TEST_CASE(BasicUsageSemantics, DispatcherFixture) |
||||
{ |
||||
BOOST_CHECK_NO_THROW(dispatcher |
||||
.addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([]{}))); |
||||
BOOST_CHECK_NO_THROW(dispatcher |
||||
.addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([]{}))); |
||||
|
||||
BOOST_CHECK_THROW(dispatcher |
||||
.addControlCommand<VoidParameters>("test", makeAcceptAllAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([]{})), |
||||
std::out_of_range); |
||||
|
||||
BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/1", |
||||
makeAcceptAllAuthorization(), bind([]{}))); |
||||
BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/2", |
||||
makeAcceptAllAuthorization(), bind([]{}))); |
||||
BOOST_CHECK_THROW(dispatcher.addStatusDataset("status", |
||||
makeAcceptAllAuthorization(), bind([]{})), |
||||
std::out_of_range); |
||||
|
||||
BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/1")); |
||||
BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/2")); |
||||
BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream"), std::out_of_range); |
||||
|
||||
|
||||
BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/1")); |
||||
BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/2")); |
||||
BOOST_CHECK_THROW(dispatcher.addTopPrefix("/root"), std::out_of_range); |
||||
|
||||
BOOST_CHECK_THROW(dispatcher |
||||
.addControlCommand<VoidParameters>("test/3", makeAcceptAllAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([]{})), |
||||
std::domain_error); |
||||
|
||||
BOOST_CHECK_THROW(dispatcher.addStatusDataset("status/3", |
||||
makeAcceptAllAuthorization(), bind([]{})), |
||||
std::domain_error); |
||||
|
||||
BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream/3"), std::domain_error); |
||||
} |
||||
|
||||
BOOST_FIXTURE_TEST_CASE(AddRemoveTopPrefix, DispatcherFixture) |
||||
{ |
||||
std::map<std::string, size_t> nCallbackCalled; |
||||
dispatcher |
||||
.addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([&nCallbackCalled] { ++nCallbackCalled["test/1"]; })); |
||||
|
||||
dispatcher |
||||
.addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([&nCallbackCalled] { ++nCallbackCalled["test/2"]; })); |
||||
|
||||
face->receive(*util::makeInterest("/root/1/test/1/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 0); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0); |
||||
|
||||
dispatcher.addTopPrefix("/root/1"); |
||||
advanceClocks(time::milliseconds(1)); |
||||
|
||||
face->receive(*util::makeInterest("/root/1/test/1/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0); |
||||
|
||||
face->receive(*util::makeInterest("/root/1/test/2/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1); |
||||
|
||||
face->receive(*util::makeInterest("/root/2/test/1/%80%00")); |
||||
face->receive(*util::makeInterest("/root/2/test/2/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1); |
||||
|
||||
dispatcher.addTopPrefix("/root/2"); |
||||
advanceClocks(time::milliseconds(1)); |
||||
|
||||
face->receive(*util::makeInterest("/root/1/test/1/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 2); |
||||
|
||||
face->receive(*util::makeInterest("/root/2/test/1/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3); |
||||
|
||||
dispatcher.removeTopPrefix("/root/1"); |
||||
advanceClocks(time::milliseconds(1)); |
||||
|
||||
face->receive(*util::makeInterest("/root/1/test/1/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3); |
||||
|
||||
face->receive(*util::makeInterest("/root/2/test/1/%80%00")); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 4); |
||||
} |
||||
|
||||
BOOST_FIXTURE_TEST_CASE(ControlCommand, DispatcherFixture) |
||||
{ |
||||
size_t nCallbackCalled = 0; |
||||
dispatcher |
||||
.addControlCommand<VoidParameters>("test", |
||||
makeTestAuthorization(), |
||||
bind([] { return true; }), |
||||
bind([&nCallbackCalled] { ++nCallbackCalled; })); |
||||
|
||||
dispatcher.addTopPrefix("/root"); |
||||
advanceClocks(time::milliseconds(1)); |
||||
face->sentDatas.clear(); |
||||
|
||||
face->receive(*util::makeInterest("/root/test/%80%00")); // returns 403
|
||||
face->receive(*util::makeInterest("/root/test/%80%00/invalid")); // returns 403
|
||||
face->receive(*util::makeInterest("/root/test/%80%00/silent")); // silently ignored
|
||||
face->receive(*util::makeInterest("/root/test/.../invalid")); // silently ignored (wrong format)
|
||||
face->receive(*util::makeInterest("/root/test/.../valid")); // silently ignored (wrong format)
|
||||
advanceClocks(time::milliseconds(1), 20); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled, 0); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 2); |
||||
|
||||
BOOST_CHECK(face->sentDatas[0].getContentType() == tlv::ContentType_Blob); |
||||
BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[0].getContent().blockFromValue()).getCode(), 403); |
||||
BOOST_CHECK(face->sentDatas[1].getContentType() == tlv::ContentType_Blob); |
||||
BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[1].getContent().blockFromValue()).getCode(), 403); |
||||
|
||||
face->receive(*util::makeInterest("/root/test/%80%00/valid")); |
||||
advanceClocks(time::milliseconds(1), 10); |
||||
BOOST_CHECK_EQUAL(nCallbackCalled, 1); |
||||
} |
||||
|
||||
BOOST_FIXTURE_TEST_CASE(StatusDataset, DispatcherFixture) |
||||
{ |
||||
static Block smallBlock("\x81\x01\0x01", 3); |
||||
static Block largeBlock = [] () -> Block { |
||||
EncodingBuffer encoder; |
||||
for (size_t i = 0; i < 2500; ++i) { |
||||
encoder.prependByte(1); |
||||
} |
||||
encoder.prependVarNumber(2500); |
||||
encoder.prependVarNumber(129); |
||||
return encoder.block(); |
||||
}(); |
||||
|
||||
dispatcher.addStatusDataset("test/small", |
||||
makeTestAuthorization(), |
||||
[] (const Name& prefix, const Interest& interest, |
||||
StatusDatasetContext context) { |
||||
context.append(smallBlock); |
||||
context.append(smallBlock); |
||||
context.append(smallBlock); |
||||
context.end(); |
||||
}); |
||||
|
||||
dispatcher.addStatusDataset("test/large", |
||||
makeTestAuthorization(), |
||||
[] (const Name& prefix, const Interest& interest, |
||||
StatusDatasetContext context) { |
||||
context.append(largeBlock); |
||||
context.append(largeBlock); |
||||
context.append(largeBlock); |
||||
context.end(); |
||||
}); |
||||
|
||||
dispatcher.addStatusDataset("test/reject", |
||||
makeTestAuthorization(), |
||||
[] (const Name& prefix, const Interest& interest, |
||||
StatusDatasetContext context) { |
||||
context.reject(); |
||||
}); |
||||
|
||||
dispatcher.addTopPrefix("/root"); |
||||
advanceClocks(time::milliseconds(1)); |
||||
face->sentDatas.clear(); |
||||
|
||||
face->receive(*util::makeInterest("/root/test/small/%80%00")); // returns 403
|
||||
face->receive(*util::makeInterest("/root/test/small/%80%00/invalid")); // returns 403
|
||||
face->receive(*util::makeInterest("/root/test/small/%80%00/silent")); // silently ignored
|
||||
advanceClocks(time::milliseconds(1), 20); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 2); |
||||
|
||||
BOOST_CHECK(face->sentDatas[0].getContentType() == tlv::ContentType_Blob); |
||||
BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[0].getContent().blockFromValue()).getCode(), 403); |
||||
BOOST_CHECK(face->sentDatas[1].getContentType() == tlv::ContentType_Blob); |
||||
BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[1].getContent().blockFromValue()).getCode(), 403); |
||||
|
||||
face->sentDatas.clear(); |
||||
face->receive(*util::makeInterest("/root/test/small/valid")); |
||||
advanceClocks(time::milliseconds(1), 10); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 1); |
||||
|
||||
face->receive(*util::makeInterest(Name("/root/test/small/valid").appendVersion(10))); // should be ignored
|
||||
face->receive(*util::makeInterest(Name("/root/test/small/valid").appendSegment(20))); // should be ignored
|
||||
advanceClocks(time::milliseconds(1), 10); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 1); |
||||
|
||||
Block content = face->sentDatas[0].getContent(); |
||||
BOOST_CHECK_NO_THROW(content.parse()); |
||||
|
||||
BOOST_CHECK_EQUAL(content.elements().size(), 3); |
||||
BOOST_CHECK(content.elements()[0] == smallBlock); |
||||
BOOST_CHECK(content.elements()[1] == smallBlock); |
||||
BOOST_CHECK(content.elements()[2] == smallBlock); |
||||
|
||||
face->sentDatas.clear(); |
||||
face->receive(*util::makeInterest("/root/test/large/valid")); |
||||
advanceClocks(time::milliseconds(1), 10); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 2); |
||||
|
||||
const auto& datas = face->sentDatas; |
||||
content = [&datas] () -> Block { |
||||
EncodingBuffer encoder; |
||||
size_t valueLength = encoder.prependByteArray(datas[1].getContent().value(), |
||||
datas[1].getContent().value_size()); |
||||
valueLength += encoder.prependByteArray(datas[0].getContent().value(), |
||||
datas[0].getContent().value_size()); |
||||
encoder.prependVarNumber(valueLength); |
||||
encoder.prependVarNumber(tlv::Content); |
||||
return encoder.block(); |
||||
}(); |
||||
|
||||
BOOST_CHECK_NO_THROW(content.parse()); |
||||
|
||||
BOOST_CHECK_EQUAL(content.elements().size(), 3); |
||||
BOOST_CHECK(content.elements()[0] == largeBlock); |
||||
BOOST_CHECK(content.elements()[1] == largeBlock); |
||||
BOOST_CHECK(content.elements()[2] == largeBlock); |
||||
|
||||
face->sentDatas.clear(); |
||||
face->receive(*util::makeInterest("/root/test/reject/%80%00/valid")); // returns nack
|
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 1); |
||||
BOOST_CHECK(face->sentDatas[0].getContentType() == tlv::ContentType_Nack); |
||||
BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[0].getContent().blockFromValue()).getCode(), 400); |
||||
} |
||||
|
||||
BOOST_FIXTURE_TEST_CASE(NotificationStream, DispatcherFixture) |
||||
{ |
||||
static Block block("\x82\x01\x02", 3); |
||||
|
||||
auto post = dispatcher.addNotificationStream("test"); |
||||
|
||||
post(block); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 0); |
||||
|
||||
dispatcher.addTopPrefix("/root"); |
||||
advanceClocks(time::milliseconds(1)); |
||||
face->sentDatas.clear(); |
||||
|
||||
post(block); |
||||
advanceClocks(time::milliseconds(1)); |
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 1); |
||||
|
||||
post(block); |
||||
post(block); |
||||
post(block); |
||||
advanceClocks(time::milliseconds(1), 10); |
||||
|
||||
BOOST_CHECK_EQUAL(face->sentDatas.size(), 4); |
||||
BOOST_CHECK_EQUAL(face->sentDatas[0].getName(), "/root/test/%FE%00"); |
||||
BOOST_CHECK_EQUAL(face->sentDatas[1].getName(), "/root/test/%FE%01"); |
||||
BOOST_CHECK_EQUAL(face->sentDatas[2].getName(), "/root/test/%FE%02"); |
||||
BOOST_CHECK_EQUAL(face->sentDatas[3].getName(), "/root/test/%FE%03"); |
||||
|
||||
BOOST_CHECK(face->sentDatas[0].getContent().blockFromValue() == block); |
||||
BOOST_CHECK(face->sentDatas[1].getContent().blockFromValue() == block); |
||||
BOOST_CHECK(face->sentDatas[2].getContent().blockFromValue() == block); |
||||
BOOST_CHECK(face->sentDatas[3].getContent().blockFromValue() == block); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() |
||||
|
||||
} // namespace tests
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
@ -0,0 +1,256 @@
|
||||
/* -*- 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 "mgmt/status-dataset-context.hpp" |
||||
#include "boost-test.hpp" |
||||
#include "unit-tests/make-interest-data.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace mgmt { |
||||
namespace tests { |
||||
|
||||
class StatusDatasetContextFixture |
||||
{ |
||||
public: |
||||
StatusDatasetContextFixture() |
||||
: interest(util::makeInterest("/test/context/interest")) |
||||
, contentBlock(makeStringBlock(tlv::Content, "/test/data/content")) |
||||
, context(*interest, bind(&StatusDatasetContextFixture::sendData, this, _1, _2, _3)) |
||||
{ |
||||
} |
||||
|
||||
void |
||||
sendData(const Name& dataName, const Block& content, const MetaInfo& info) |
||||
{ |
||||
sentData.push_back(Data(dataName).setContent(content).setMetaInfo(info)); |
||||
} |
||||
|
||||
Block |
||||
concatenate() |
||||
{ |
||||
EncodingBuffer encoder; |
||||
size_t valueLength = 0; |
||||
for (auto data : sentData) { |
||||
valueLength += encoder.appendByteArray(data.getContent().value(), |
||||
data.getContent().value_size()); |
||||
} |
||||
encoder.prependVarNumber(valueLength); |
||||
encoder.prependVarNumber(tlv::Content); |
||||
return encoder.block(); |
||||
} |
||||
|
||||
public: |
||||
std::vector<Data> sentData; |
||||
shared_ptr<Interest> interest; |
||||
Block contentBlock; |
||||
mgmt::StatusDatasetContext context; |
||||
}; |
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(MgmtStatusDatasetContext, StatusDatasetContextFixture) |
||||
|
||||
BOOST_AUTO_TEST_CASE(GetPrefix) |
||||
{ |
||||
Name dataName = context.getPrefix(); |
||||
BOOST_CHECK(dataName[-1].isVersion()); |
||||
BOOST_CHECK(dataName.getPrefix(-1) == interest->getName()); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE(SetPrefix) |
||||
|
||||
BOOST_AUTO_TEST_CASE(Valid) |
||||
{ |
||||
Name validPrefix = Name(interest->getName()).append("/valid"); |
||||
BOOST_CHECK_NO_THROW(context.setPrefix(validPrefix)); |
||||
BOOST_CHECK(context.getPrefix()[-1].isVersion()); |
||||
BOOST_CHECK(context.getPrefix().getPrefix(-1) == validPrefix); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(Invalid) |
||||
{ |
||||
Name invalidPrefix = Name(interest->getName()).getPrefix(-1).append("/invalid"); |
||||
BOOST_CHECK_THROW(context.setPrefix(invalidPrefix), std::invalid_argument); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(ValidWithAppendCalled) |
||||
{ |
||||
Name validPrefix = Name(interest->getName()).append("/valid"); |
||||
context.append(contentBlock); |
||||
BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(ValidWithEndCalled) |
||||
{ |
||||
Name validPrefix = Name(interest->getName()).append("/valid"); |
||||
context.end(); |
||||
BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(ValidWithRejectCalled) |
||||
{ |
||||
Name validPrefix = Name(interest->getName()).append("/valid"); |
||||
context.reject(); |
||||
BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // SetPrefix
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Expiry) |
||||
{ |
||||
// getExpiry & setExpiry
|
||||
auto period = time::milliseconds(9527); |
||||
BOOST_CHECK_EQUAL(context.getExpiry(), time::milliseconds(1000)); |
||||
BOOST_CHECK_EQUAL(context.setExpiry(period).getExpiry(), period); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(Respond) |
||||
{ |
||||
BOOST_CHECK_NO_THROW(context.append(contentBlock)); |
||||
BOOST_CHECK(sentData.empty()); // does not call end yet
|
||||
|
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
BOOST_CHECK_EQUAL(sentData.size(), 1); |
||||
BOOST_CHECK_EQUAL(sentData[0].getName()[-1].toSegment(), 0); |
||||
BOOST_CHECK_EQUAL(sentData[0].getName().getPrefix(-1), context.getPrefix()); |
||||
BOOST_CHECK_EQUAL(sentData[0].getFinalBlockId().toSegment(), 0); |
||||
BOOST_CHECK(sentData[0].getContent().blockFromValue() == contentBlock); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(RespondLarge) |
||||
{ |
||||
static Block largeBlock = [] () -> Block { |
||||
EncodingBuffer encoder; |
||||
size_t maxBlockSize = MAX_NDN_PACKET_SIZE >> 1; |
||||
for (size_t i = 0; i < maxBlockSize; ++i) { |
||||
encoder.prependByte(1); |
||||
} |
||||
encoder.prependVarNumber(maxBlockSize); |
||||
encoder.prependVarNumber(tlv::Content); |
||||
return encoder.block(); |
||||
}(); |
||||
|
||||
BOOST_CHECK_NO_THROW(context.append(largeBlock)); |
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
BOOST_CHECK_EQUAL(sentData.size(), 2); |
||||
BOOST_CHECK_EQUAL(sentData[0].getName()[-1].toSegment(), 0); |
||||
BOOST_CHECK_EQUAL(sentData[0].getName().getPrefix(-1), context.getPrefix()); |
||||
BOOST_CHECK_EQUAL(sentData[1].getName()[-1].toSegment(), 1); |
||||
BOOST_CHECK_EQUAL(sentData[1].getName().getPrefix(-1), context.getPrefix()); |
||||
BOOST_CHECK_EQUAL(sentData[1].getFinalBlockId().toSegment(), 1); |
||||
|
||||
auto contentLargeBlock = concatenate(); |
||||
BOOST_CHECK_NO_THROW(contentLargeBlock.parse()); |
||||
BOOST_CHECK_EQUAL(contentLargeBlock.elements().size(), 1); |
||||
BOOST_CHECK(contentLargeBlock.elements()[0] == largeBlock); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(ResponseMultipleSmall) |
||||
{ |
||||
size_t nBlocks = 100; |
||||
for (size_t i = 0 ; i < nBlocks ; i ++) { |
||||
BOOST_CHECK_NO_THROW(context.append(contentBlock)); |
||||
} |
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
BOOST_CHECK_EQUAL(sentData.size(), 1); |
||||
BOOST_CHECK_EQUAL(sentData[0].getName()[-1].toSegment(), 0); |
||||
BOOST_CHECK_EQUAL(sentData[0].getName().getPrefix(-1), context.getPrefix()); |
||||
BOOST_CHECK_EQUAL(sentData[0].getFinalBlockId().toSegment(), 0); |
||||
|
||||
auto contentMultiBlocks = concatenate(); |
||||
BOOST_CHECK_NO_THROW(contentMultiBlocks.parse()); |
||||
BOOST_CHECK_EQUAL(contentMultiBlocks.elements().size(), nBlocks); |
||||
for (auto&& element : contentMultiBlocks.elements()) { |
||||
BOOST_CHECK(element == contentBlock); |
||||
} |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(Reject) |
||||
{ |
||||
BOOST_CHECK_NO_THROW(context.reject()); |
||||
BOOST_CHECK_EQUAL(sentData.size(), 1); |
||||
BOOST_CHECK(sentData[0].getContentType() == tlv::ContentType_Nack); |
||||
BOOST_CHECK_EQUAL(ControlResponse(sentData[0].getContent().blockFromValue()).getCode(), 400); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE(AbnormalState) |
||||
|
||||
BOOST_AUTO_TEST_CASE(AppendReject) |
||||
{ |
||||
mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.append(Block("\x82\x01\x02", 3))); |
||||
BOOST_CHECK_THROW(context.reject(), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(AppendEndReject) |
||||
{ |
||||
mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.append(Block("\x82\x01\x02", 3))); |
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
BOOST_CHECK_THROW(context.reject(), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(EndAppend) |
||||
{ |
||||
mgmt::StatusDatasetContext context (Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
// end, append -> error
|
||||
BOOST_CHECK_THROW(context.append(Block("\x82\x01\x02", 3)), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(EndEnd) |
||||
{ |
||||
mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
BOOST_CHECK_THROW(context.end(), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(EndReject) |
||||
{ |
||||
mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.end()); |
||||
BOOST_CHECK_THROW(context.reject(), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(RejectAppend) |
||||
{ |
||||
mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.reject()); |
||||
BOOST_CHECK_THROW(context.append(Block("\x82\x01\x02", 3)), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(RejectEnd) |
||||
{ |
||||
mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{})); |
||||
BOOST_CHECK_NO_THROW(context.reject()); |
||||
BOOST_CHECK_THROW(context.end(), std::domain_error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // AbnormalState
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() |
||||
|
||||
} // namespace tests
|
||||
} // namespace mgmt
|
||||
} // namespace ndn
|
Loading…
Reference in new issue