Browse Source

mgmt: Dispatcher

Change-Id: I92b3dc9daae75abac9d791632b6a0bec111b4573
refs: #2107
pull/3/head
Yanbiao Li 10 years ago committed by Alexander Afanasyev
parent
commit
8ee37edb7e
  1. 1
      AUTHORS.md
  2. 1
      src/management/nfd-control-command.hpp
  3. 2
      src/management/nfd-control-parameters.cpp
  4. 11
      src/management/nfd-control-parameters.hpp
  5. 114
      src/management/nfd-control-response.cpp
  6. 104
      src/management/nfd-control-response.hpp
  7. 49
      src/mgmt/control-parameters.hpp
  8. 111
      src/mgmt/control-response.cpp
  9. 137
      src/mgmt/control-response.hpp
  10. 310
      src/mgmt/dispatcher.cpp
  11. 451
      src/mgmt/dispatcher.hpp
  12. 139
      src/mgmt/status-dataset-context.cpp
  13. 125
      src/mgmt/status-dataset-context.hpp
  14. 386
      tests/unit-tests/mgmt/dispatcher.t.cpp
  15. 256
      tests/unit-tests/mgmt/status-dataset-context.t.cpp

1
AUTHORS.md

@ -35,3 +35,4 @@ in the library:
* Eric Newberry <http://ericnewberry.com>
* João Pereira <http://website.jpereira.co.uk>
* Mickey Sweatt <https://www.linkedin.com/in/michaelsweatt>
* Yanbiao Li <https://www.linkedin.com/pub/yanbiao-li/24/7a1/4ba>

1
src/management/nfd-control-command.hpp

@ -307,7 +307,6 @@ public:
validateResponse(const ControlParameters& parameters) const;
};
} // namespace nfd
} // namespace ndn

2
src/management/nfd-control-parameters.cpp

@ -95,7 +95,7 @@ ControlParameters::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::Encod
template size_t
ControlParameters::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>&) const;
const Block&
Block
ControlParameters::wireEncode() const
{
if (m_wire.hasWire())

11
src/management/nfd-control-parameters.hpp

@ -25,6 +25,7 @@
#include "../encoding/nfd-constants.hpp"
#include "../name.hpp"
#include "../util/time.hpp"
#include "../mgmt/control-parameters.hpp"
namespace ndn {
namespace nfd {
@ -71,7 +72,7 @@ enum LocalControlFeature {
* @sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand#ControlParameters
* @detail This type is copyable because it's an abstraction of a TLV type.
*/
class ControlParameters
class ControlParameters : public ndn::mgmt::ControlParameters
{
public:
class Error : public tlv::Error
@ -93,11 +94,11 @@ public:
size_t
wireEncode(EncodingImpl<TAG>& encoder) const;
const Block&
wireEncode() const;
virtual Block
wireEncode() const NDN_CXX_DECL_FINAL;
void
wireDecode(const Block& wire);
virtual void
wireDecode(const Block& wire) NDN_CXX_DECL_FINAL;
public: // getters & setters

114
src/management/nfd-control-response.cpp

@ -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

104
src/management/nfd-control-response.hpp

@ -22,112 +22,12 @@
#ifndef NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
#define NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
#include "../encoding/block.hpp"
#include "../mgmt/dispatcher.hpp"
namespace ndn {
namespace nfd {
/**
* @ingroup management
* @brief Class defining abstraction of ControlResponse for NFD Control Protocol
*
* @see http://redmine.named-data.net/projects/nfd/wiki/ControlCommand#Response-format
* @detail This type is copyable because it's an abstraction of a TLV type.
*/
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;
void
setCode(uint32_t code);
const std::string&
getText() const;
void
setText(const std::string& text);
const Block&
getBody() const;
void
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 void
ControlResponse::setCode(uint32_t code)
{
m_code = code;
m_wire.reset();
}
inline const std::string&
ControlResponse::getText() const
{
return m_text;
}
inline void
ControlResponse::setText(const std::string& text)
{
m_text = text;
m_wire.reset();
}
inline const Block&
ControlResponse::getBody() const
{
return m_body;
}
inline void
ControlResponse::setBody(const Block& body)
{
m_body = body;
m_body.encode(); // will do nothing if already encoded
m_wire.reset();
}
std::ostream&
operator<<(std::ostream& os, const ControlResponse& response);
typedef ndn::mgmt::ControlResponse ControlResponse;
} // namespace nfd
} // namespace ndn

49
src/mgmt/control-parameters.hpp

@ -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

111
src/mgmt/control-response.cpp

@ -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

137
src/mgmt/control-response.hpp

@ -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

310
src/mgmt/dispatcher.cpp

@ -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

451
src/mgmt/dispatcher.hpp

@ -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

139
src/mgmt/status-dataset-context.cpp

@ -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

125
src/mgmt/status-dataset-context.hpp

@ -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

386
tests/unit-tests/mgmt/dispatcher.t.cpp

@ -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

256
tests/unit-tests/mgmt/status-dataset-context.t.cpp

@ -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…
Cancel
Save