mgmt: add face/list support and general purpose data segementer
refs: #1245
Change-Id: I3769941022b7ed6e2a8d39622032e4e16909f645
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 701bddd..bf31a10 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -9,7 +9,6 @@
#include "core/face-uri.hpp"
#include "core/network-interface.hpp"
#include "fw/face-table.hpp"
-
#include "face/tcp-factory.hpp"
#include "face/udp-factory.hpp"
@@ -25,7 +24,7 @@
NFD_LOG_INIT("FaceManager");
-const Name FaceManager::COMMAND_PREFIX = "/localhost/nfd/faces";
+const Name FaceManager::COMMAND_PREFIX("/localhost/nfd/faces");
const size_t FaceManager::COMMAND_UNSIGNED_NCOMPS =
FaceManager::COMMAND_PREFIX.size() +
@@ -36,28 +35,44 @@
FaceManager::COMMAND_UNSIGNED_NCOMPS +
4; // (timestamp, nonce, signed info tlv, signature tlv)
-const FaceManager::VerbAndProcessor FaceManager::COMMAND_VERBS[] =
+const FaceManager::SignedVerbAndProcessor FaceManager::SIGNED_COMMAND_VERBS[] =
{
- VerbAndProcessor(
+ SignedVerbAndProcessor(
Name::Component("create"),
&FaceManager::createFace
),
- VerbAndProcessor(
+ SignedVerbAndProcessor(
Name::Component("destroy"),
&FaceManager::destroyFace
),
};
+const FaceManager::UnsignedVerbAndProcessor FaceManager::UNSIGNED_COMMAND_VERBS[] =
+ {
+ UnsignedVerbAndProcessor(
+ Name::Component("list"),
+ &FaceManager::listFaces
+ ),
+ };
+
+const Name FaceManager::LIST_COMMAND_PREFIX("/localhost/nfd/faces/list");
+const size_t FaceManager::LIST_COMMAND_NCOMPS = LIST_COMMAND_PREFIX.size();
+
FaceManager::FaceManager(FaceTable& faceTable,
shared_ptr<InternalFace> face)
: ManagerBase(face, FACE_MANAGER_PRIVILEGE)
, m_faceTable(faceTable)
- , m_verbDispatch(COMMAND_VERBS,
- COMMAND_VERBS +
- (sizeof(COMMAND_VERBS) / sizeof(VerbAndProcessor)))
+ , m_statusPublisher(m_faceTable, m_face, LIST_COMMAND_PREFIX)
+ , m_signedVerbDispatch(SIGNED_COMMAND_VERBS,
+ SIGNED_COMMAND_VERBS +
+ (sizeof(SIGNED_COMMAND_VERBS) / sizeof(SignedVerbAndProcessor)))
+ , m_unsignedVerbDispatch(UNSIGNED_COMMAND_VERBS,
+ UNSIGNED_COMMAND_VERBS +
+ (sizeof(UNSIGNED_COMMAND_VERBS) / sizeof(UnsignedVerbAndProcessor)))
+
{
face->setInterestFilter("/localhost/nfd/faces",
bind(&FaceManager::onFaceRequest, this, _2));
@@ -487,26 +502,32 @@
{
const Name& command = request.getName();
const size_t commandNComps = command.size();
+ const Name::Component& verb = command.get(COMMAND_PREFIX.size());
- if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+ UnsignedVerbDispatchTable::const_iterator unsignedVerbProcessor = m_unsignedVerbDispatch.find(verb);
+ if (unsignedVerbProcessor != m_unsignedVerbDispatch.end())
+ {
+ NFD_LOG_INFO("command result: processing verb: " << verb);
+ (unsignedVerbProcessor->second)(this, boost::cref(request));
+ }
+ else if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
commandNComps < COMMAND_SIGNED_NCOMPS)
{
NFD_LOG_INFO("command result: unsigned verb: " << command);
sendResponse(command, 401, "Signature required");
-
- return;
}
else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
!COMMAND_PREFIX.isPrefixOf(command))
{
NFD_LOG_INFO("command result: malformed");
sendResponse(command, 400, "Malformed command");
- return;
}
-
- validate(request,
- bind(&FaceManager::onValidatedFaceRequest, this, _1),
- bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
+ else
+ {
+ validate(request,
+ bind(&FaceManager::onValidatedFaceRequest, this, _1),
+ bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
+ }
}
void
@@ -515,8 +536,8 @@
const Name& command = request->getName();
const Name::Component& verb = command.get(COMMAND_PREFIX.size());
- VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find (verb);
- if (verbProcessor != m_verbDispatch.end())
+ SignedVerbDispatchTable::const_iterator signedVerbProcessor = m_signedVerbDispatch.find(verb);
+ if (signedVerbProcessor != m_signedVerbDispatch.end())
{
ndn::nfd::FaceManagementOptions options;
if (!extractOptions(*request, options))
@@ -526,7 +547,7 @@
}
NFD_LOG_INFO("command result: processing verb: " << verb);
- (verbProcessor->second)(this, command, options);
+ (signedVerbProcessor->second)(this, command, options);
}
else
{
@@ -617,6 +638,21 @@
sendResponse(requestName, 200, "Success");
}
+void
+FaceManager::listFaces(const Interest& request)
+{
+ const Name& command = request.getName();
+ const size_t commandNComps = command.size();
+ if (commandNComps < LIST_COMMAND_NCOMPS ||
+ !LIST_COMMAND_PREFIX.isPrefixOf(command))
+ {
+ NFD_LOG_INFO("command result: malformed");
+ sendResponse(command, 400, "Malformed command");
+ return;
+ }
+
+ m_statusPublisher.publish();
+}
} // namespace nfd
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 55e4df4..4139152 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -12,6 +12,8 @@
#include "mgmt/app-face.hpp"
#include "mgmt/manager-base.hpp"
#include "mgmt/config-file.hpp"
+#include "mgmt/face-status-publisher.hpp"
+#include "fw/face-table.hpp"
#include <ndn-cpp-dev/management/nfd-face-management-options.hpp>
#include <ndn-cpp-dev/management/nfd-control-response.hpp>
@@ -19,15 +21,16 @@
namespace nfd {
const std::string FACE_MANAGER_PRIVILEGE = "faces";
-class FaceTable;
+
class ProtocolFactory;
class NetworkInterfaceInfo;
class FaceManager : public ManagerBase
{
public:
- struct Error : public ManagerBase::Error
+ class Error : public ManagerBase::Error
{
+ public:
Error(const std::string& what) : ManagerBase::Error(what) {}
};
@@ -49,6 +52,10 @@
void
onFaceRequest(const Interest& request);
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ void
+ listFaces(const Interest& request);
+
PROTECTED_WITH_TESTS_ELSE_PRIVATE:
void
@@ -107,28 +114,39 @@
typedef std::map< std::string/*protocol*/, shared_ptr<ProtocolFactory> > FactoryMap;
FactoryMap m_factories;
FaceTable& m_faceTable;
+ FaceStatusPublisher m_statusPublisher;
typedef function<void(FaceManager*,
const Name&,
- ndn::nfd::FaceManagementOptions&)> VerbProcessor;
+ ndn::nfd::FaceManagementOptions&)> SignedVerbProcessor;
- typedef std::map<Name::Component, VerbProcessor> VerbDispatchTable;
+ typedef std::map<Name::Component, SignedVerbProcessor> SignedVerbDispatchTable;
+ typedef std::pair<Name::Component, SignedVerbProcessor> SignedVerbAndProcessor;
- typedef std::pair<Name::Component, VerbProcessor> VerbAndProcessor;
+ typedef function<void(FaceManager*, const Interest&)> UnsignedVerbProcessor;
- const VerbDispatchTable m_verbDispatch;
+ typedef std::map<Name::Component, UnsignedVerbProcessor> UnsignedVerbDispatchTable;
+ typedef std::pair<Name::Component, UnsignedVerbProcessor> UnsignedVerbAndProcessor;
- static const Name COMMAND_PREFIX; // /localhost/nfd/fib
- // number of components in an invalid, but not malformed, unsigned command.
- // (/localhost/nfd/fib + verb + options) = 5
+ const SignedVerbDispatchTable m_signedVerbDispatch;
+ const UnsignedVerbDispatchTable m_unsignedVerbDispatch;
+
+ static const Name COMMAND_PREFIX; // /localhost/nfd/faces
+
+ // number of components in an invalid signed command (i.e. should be signed, but isn't)
+ // (/localhost/nfd/faces + verb + options) = 5
static const size_t COMMAND_UNSIGNED_NCOMPS;
- // number of components in a valid signed Interest.
+ // number of components in a valid signed command.
// (see UNSIGNED_NCOMPS), 9 with signed Interest support.
static const size_t COMMAND_SIGNED_NCOMPS;
- static const VerbAndProcessor COMMAND_VERBS[];
+ static const SignedVerbAndProcessor SIGNED_COMMAND_VERBS[];
+ static const UnsignedVerbAndProcessor UNSIGNED_COMMAND_VERBS[];
+
+ static const Name LIST_COMMAND_PREFIX;
+ static const size_t LIST_COMMAND_NCOMPS;
};
inline bool
diff --git a/daemon/mgmt/face-status-publisher.cpp b/daemon/mgmt/face-status-publisher.cpp
new file mode 100644
index 0000000..2cf2603
--- /dev/null
+++ b/daemon/mgmt/face-status-publisher.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+#include "face-status-publisher.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("FaceStatusPublisher");
+
+
+FaceStatusPublisher::FaceStatusPublisher(const FaceTable& faceTable,
+ shared_ptr<AppFace> face,
+ const Name& prefix)
+ : SegmentPublisher(face, prefix)
+ , m_faceTable(faceTable)
+{
+
+}
+
+
+FaceStatusPublisher::~FaceStatusPublisher()
+{
+
+}
+
+size_t
+FaceStatusPublisher::generate(ndn::EncodingBuffer& outBuffer)
+{
+ size_t totalLength = 0;
+
+ for (FaceTable::const_iterator i = m_faceTable.begin();
+ i != m_faceTable.end();
+ ++i)
+ {
+ const shared_ptr<Face>& face = *i;
+ const FaceCounters& counters = face->getCounters();
+ const std::string uri = face->getUri().toString();
+
+ size_t statusLength = 0;
+
+ statusLength += prependNonNegativeIntegerBlock(outBuffer,
+ ndn::tlv::nfd::TotalOutgoingDataCounter,
+ counters.getOutData());
+
+ statusLength += prependNonNegativeIntegerBlock(outBuffer,
+ ndn::tlv::nfd::TotalOutgoingInterestCounter,
+ counters.getOutInterest());
+
+ statusLength += prependNonNegativeIntegerBlock(outBuffer,
+ ndn::tlv::nfd::TotalIncomingDataCounter,
+ counters.getInData());
+
+ statusLength += prependNonNegativeIntegerBlock(outBuffer,
+ ndn::tlv::nfd::TotalIncomingInterestCounter,
+ counters.getInInterest());
+
+ statusLength += prependByteArrayBlock(outBuffer,
+ ndn::tlv::nfd::Uri,
+ reinterpret_cast<const uint8_t*>(uri.c_str()),
+ uri.size());
+
+ statusLength += prependNonNegativeIntegerBlock(outBuffer,
+ ndn::tlv::nfd::FaceId,
+ face->getId());
+
+ statusLength += outBuffer.prependVarNumber(statusLength);
+ statusLength += outBuffer.prependVarNumber(ndn::tlv::nfd::FaceStatus);
+
+ totalLength += statusLength;
+ }
+ return totalLength;
+}
+
+} // namespace nfd
diff --git a/daemon/mgmt/face-status-publisher.hpp b/daemon/mgmt/face-status-publisher.hpp
new file mode 100644
index 0000000..2006462
--- /dev/null
+++ b/daemon/mgmt/face-status-publisher.hpp
@@ -0,0 +1,36 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_MGMT_FACE_STATUS_PUBLISHER_HPP
+#define NFD_MGMT_FACE_STATUS_PUBLISHER_HPP
+
+#include "fw/face-table.hpp"
+#include "mgmt/segment-publisher.hpp"
+
+namespace nfd {
+
+class FaceStatusPublisher : public SegmentPublisher
+{
+public:
+ FaceStatusPublisher(const FaceTable& faceTable,
+ shared_ptr<AppFace> face,
+ const Name& prefix);
+
+ virtual
+ ~FaceStatusPublisher();
+
+protected:
+
+ virtual size_t
+ generate(ndn::EncodingBuffer& outBuffer);
+
+private:
+ const FaceTable& m_faceTable;
+};
+
+} // namespace nfd
+
+#endif // NFD_MGMT_FACE_STATUS_PUBLISHER_HPP
diff --git a/daemon/mgmt/segment-publisher.cpp b/daemon/mgmt/segment-publisher.cpp
new file mode 100644
index 0000000..bd7d971
--- /dev/null
+++ b/daemon/mgmt/segment-publisher.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "segment-publisher.hpp"
+
+#include "common.hpp"
+#include "face/face.hpp"
+
+#include <ndn-cpp-dev/util/time.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("SegmentPublisher");
+
+SegmentPublisher::SegmentPublisher(shared_ptr<AppFace> face,
+ const Name& prefix)
+ : m_face(face)
+ , m_prefix(prefix)
+{
+
+}
+
+
+SegmentPublisher::~SegmentPublisher()
+{
+
+}
+
+void
+SegmentPublisher::publish()
+{
+ Name segmentPrefix(m_prefix);
+ segmentPrefix.appendSegment(ndn::ndn_getNowMilliseconds());
+
+ static const size_t MAX_SEGMENT_SIZE = MAX_NDN_PACKET_SIZE >> 1;
+
+ ndn::EncodingBuffer buffer;
+
+ generate(buffer);
+
+ const uint8_t* rawBuffer = buffer.buf();
+ const uint8_t* segmentBegin = rawBuffer;
+ const uint8_t* end = rawBuffer + buffer.size();
+
+ uint64_t segmentNo = 0;
+ while (segmentBegin < end)
+ {
+ const uint8_t* segmentEnd = segmentBegin + MAX_SEGMENT_SIZE;
+ if (segmentEnd > end)
+ {
+ segmentEnd = end;
+ }
+
+ Name segmentName(segmentPrefix);
+ segmentName.appendSegment(segmentNo);
+
+ shared_ptr<Data> data(make_shared<Data>(segmentName));
+ data->setContent(segmentBegin, segmentEnd - segmentBegin);
+
+ segmentBegin = segmentEnd;
+ if (segmentBegin >= end)
+ {
+ NFD_LOG_DEBUG("final block is " << segmentNo);
+ data->setFinalBlockId(segmentName[-1]);
+ }
+
+ NFD_LOG_DEBUG("publishing segment #" << segmentNo);
+ publishSegment(data);
+ segmentNo++;
+ }
+}
+
+void
+SegmentPublisher::publishSegment(shared_ptr<Data>& data)
+{
+ m_face->sign(*data);
+ m_face->put(*data);
+}
+
+} // namespace nfd
diff --git a/daemon/mgmt/segment-publisher.hpp b/daemon/mgmt/segment-publisher.hpp
new file mode 100644
index 0000000..4b5dc4f
--- /dev/null
+++ b/daemon/mgmt/segment-publisher.hpp
@@ -0,0 +1,47 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_MGMT_SEGMENT_PUBLISHER_HPP
+#define NFD_MGMT_SEGMENT_PUBLISHER_HPP
+
+#include "common.hpp"
+#include "mgmt/app-face.hpp"
+
+#include <ndn-cpp-dev/encoding/encoding-buffer.hpp>
+
+namespace nfd {
+
+class AppFace;
+
+class SegmentPublisher : noncopyable
+{
+public:
+ SegmentPublisher(shared_ptr<AppFace> face,
+ const Name& prefix);
+
+ virtual
+ ~SegmentPublisher();
+
+ void
+ publish();
+
+protected:
+
+ virtual size_t
+ generate(ndn::EncodingBuffer& outBuffer) =0;
+
+private:
+ void
+ publishSegment(shared_ptr<Data>& data);
+
+private:
+ shared_ptr<AppFace> m_face;
+ const Name m_prefix;
+};
+
+} // namespace nfd
+
+#endif // NFD_MGMT_SEGMENT_PUBLISHER_HPP
diff --git a/tests/mgmt/face-manager.cpp b/tests/mgmt/face-manager.cpp
index 191de04..80e14d7 100644
--- a/tests/mgmt/face-manager.cpp
+++ b/tests/mgmt/face-manager.cpp
@@ -6,6 +6,7 @@
#include "mgmt/face-manager.hpp"
#include "mgmt/internal-face.hpp"
+#include "mgmt/face-status-publisher.hpp"
#include "face/face.hpp"
#include "../face/dummy-face.hpp"
#include "fw/face-table.hpp"
@@ -14,24 +15,27 @@
#include "common.hpp"
#include "tests/test-common.hpp"
#include "validation-common.hpp"
+#include "face-status-publisher-common.hpp"
+
+#include <ndn-cpp-dev/encoding/tlv.hpp>
namespace nfd {
namespace tests {
NFD_LOG_INIT("FaceManagerTest");
-class TestDummyFace : public DummyFace
+class FaceManagerTestFace : public DummyFace
{
public:
- TestDummyFace()
+ FaceManagerTestFace()
: m_closeFired(false)
{
}
virtual
- ~TestDummyFace()
+ ~FaceManagerTestFace()
{
}
@@ -60,7 +64,7 @@
m_addFired(false),
m_removeFired(false),
m_getFired(false),
- m_dummy(make_shared<TestDummyFace>())
+ m_dummy(make_shared<FaceManagerTestFace>())
{
}
@@ -116,7 +120,7 @@
m_getFired = false;
}
- shared_ptr<TestDummyFace>&
+ shared_ptr<FaceManagerTestFace>&
getDummyFace()
{
return m_dummy;
@@ -126,7 +130,7 @@
bool m_addFired;
bool m_removeFired;
mutable bool m_getFired;
- shared_ptr<TestDummyFace> m_dummy;
+ shared_ptr<FaceManagerTestFace> m_dummy;
};
@@ -257,18 +261,18 @@
m_manager.setConfigFile(m_config);
}
- void
- parseConfig(const std::string configuration, bool isDryRun)
- {
- m_config.parse(configuration, isDryRun, "dummy-config");
- }
-
virtual
~FaceManagerFixture()
{
}
+ void
+ parseConfig(const std::string configuration, bool isDryRun)
+ {
+ m_config.parse(configuration, isDryRun, "dummy-config");
+ }
+
FaceManager&
getManager()
{
@@ -1022,8 +1026,72 @@
BOOST_CHECK(TestFaceTableFixture::m_faceTable.getDummyFace()->didCloseFire());
}
+class FaceListFixture : public FaceStatusPublisherFixture
+{
+public:
+ FaceListFixture()
+ : m_manager(m_table, m_face)
+ {
+
+ }
+
+ virtual
+ ~FaceListFixture()
+ {
+
+ }
+
+protected:
+ FaceManager m_manager;
+};
+
+BOOST_FIXTURE_TEST_CASE(TestFaceList, FaceListFixture)
+
+{
+ Name commandName("/localhost/nfd/faces/list");
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 55
+ // 55 divides 4400 (== 80), so only use 79 FaceStatuses and then two smaller ones
+ // to force a FaceStatus to span Data packets
+ for (int i = 0; i < 79; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+
+ uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_front(dummy);
+
+ add(dummy);
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+ uint64_t filler = std::numeric_limits<uint32_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_front(dummy);
+
+ add(dummy);
+ }
+
+ ndn::EncodingBuffer buffer;
+
+ m_face->onReceiveData +=
+ bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+ m_manager.listFaces(*command);
+ BOOST_REQUIRE(m_finished);
+}
+
+
+
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests
} // namespace nfd
+
diff --git a/tests/mgmt/face-status-publisher-common.hpp b/tests/mgmt/face-status-publisher-common.hpp
new file mode 100644
index 0000000..e6ff706
--- /dev/null
+++ b/tests/mgmt/face-status-publisher-common.hpp
@@ -0,0 +1,219 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_TESTS_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
+#define NFD_TESTS_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
+
+#include "mgmt/face-status-publisher.hpp"
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "fw/forwarder.hpp"
+#include "../face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+class TestCountersFace : public DummyFace
+{
+public:
+
+ TestCountersFace()
+ {
+ }
+
+ virtual
+ ~TestCountersFace()
+ {
+ }
+
+ void
+ setCounters(FaceCounter inInterest,
+ FaceCounter inData,
+ FaceCounter outInterest,
+ FaceCounter outData)
+ {
+ FaceCounters& counters = getMutableCounters();
+ counters.getInInterest() = inInterest;
+ counters.getInData() = inData;
+ counters.getOutInterest() = outInterest;
+ counters.getOutData() = outData;
+ }
+
+
+};
+
+static inline uint64_t
+readNonNegativeIntegerType(const Block& block,
+ uint32_t type)
+{
+ if (block.type() == type)
+ {
+ return readNonNegativeInteger(block);
+ }
+ std::stringstream error;
+ error << "expected type " << type << " got " << block.type();
+ throw ndn::Tlv::Error(error.str());
+}
+
+static inline uint64_t
+checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
+ Block::element_const_iterator end,
+ uint32_t type)
+{
+ if (i != end)
+ {
+ const Block& block = *i;
+ ++i;
+ return readNonNegativeIntegerType(block, type);
+ }
+ throw ndn::Tlv::Error("Unexpected end of FaceStatus");
+}
+
+class FaceStatusPublisherFixture : public BaseFixture
+{
+public:
+
+ FaceStatusPublisherFixture()
+ : m_table(m_forwarder)
+ , m_face(make_shared<InternalFace>())
+ , m_publisher(m_table, m_face, "/localhost/nfd/FaceStatusPublisherFixture")
+ , m_finished(false)
+ {
+
+ }
+
+ virtual
+ ~FaceStatusPublisherFixture()
+ {
+
+ }
+
+ void
+ add(shared_ptr<Face> face)
+ {
+ m_table.add(face);
+ }
+
+ void
+ validateFaceStatus(const Block& status, const shared_ptr<Face>& reference)
+ {
+ const FaceCounters& counters = reference->getCounters();
+
+ FaceId faceId = INVALID_FACEID;
+ std::string uri;
+ FaceCounter inInterest = 0;
+ FaceCounter inData = 0;
+ FaceCounter outInterest = 0;
+ FaceCounter outData = 0;
+
+ status.parse();
+
+ for (Block::element_const_iterator i = status.elements_begin();
+ i != status.elements_end();
+ ++i)
+ {
+ // parse a full set of FaceStatus sub-blocks
+ faceId =
+ checkedReadNonNegativeIntegerType(i,
+ status.elements_end(),
+ ndn::tlv::nfd::FaceId);
+
+ BOOST_REQUIRE_EQUAL(faceId, reference->getId());
+
+ BOOST_REQUIRE(i->type() == ndn::tlv::nfd::Uri);
+
+ uri.append(reinterpret_cast<const char*>(i->value()), i->value_size());
+ ++i;
+
+ BOOST_REQUIRE(i != status.elements_end());
+
+ BOOST_REQUIRE_EQUAL(uri, reference->getUri().toString());
+
+ inInterest =
+ checkedReadNonNegativeIntegerType(i,
+ status.elements_end(),
+ ndn::tlv::nfd::TotalIncomingInterestCounter);
+
+ BOOST_REQUIRE_EQUAL(inInterest, counters.getInInterest());
+
+ inData =
+ checkedReadNonNegativeIntegerType(i,
+ status.elements_end(),
+ ndn::tlv::nfd::TotalIncomingDataCounter);
+
+ BOOST_REQUIRE_EQUAL(inData, counters.getInData());
+
+ outInterest =
+ checkedReadNonNegativeIntegerType(i,
+ status.elements_end(),
+ ndn::tlv::nfd::TotalOutgoingInterestCounter);
+ BOOST_REQUIRE_EQUAL(outInterest, counters.getOutInterest());
+
+ outData =
+ readNonNegativeIntegerType(*i,
+ ndn::tlv::nfd::TotalOutgoingDataCounter);
+
+ BOOST_REQUIRE_EQUAL(outData, counters.getOutData());
+ }
+ }
+
+ void
+ decodeFaceStatusBlock(const Data& data)
+ {
+ Block payload = data.getContent();
+
+ m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+ uint64_t segmentNo = data.getName()[-1].toSegment();
+ if (data.getFinalBlockId() != data.getName()[-1])
+ {
+ return;
+ }
+
+ // wrap the Face Statuses in a single Content TLV for easy parsing
+ m_buffer.prependVarNumber(m_buffer.size());
+ m_buffer.prependVarNumber(ndn::Tlv::Content);
+
+ ndn::Block parser(m_buffer.buf(), m_buffer.size());
+ parser.parse();
+
+ BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceFaces.size());
+
+ std::list<shared_ptr<Face> >::const_iterator iReference = m_referenceFaces.begin();
+ for (Block::element_const_iterator i = parser.elements_begin();
+ i != parser.elements_end();
+ ++i)
+ {
+ if (i->type() != ndn::tlv::nfd::FaceStatus)
+ {
+ BOOST_FAIL("expected face status, got type #" << i->type());
+ }
+ validateFaceStatus(*i, *iReference);
+ ++iReference;
+ }
+ m_finished = true;
+ }
+
+protected:
+ Forwarder m_forwarder;
+ FaceTable m_table;
+ shared_ptr<InternalFace> m_face;
+ FaceStatusPublisher m_publisher;
+ ndn::EncodingBuffer m_buffer;
+ std::list<shared_ptr<Face> > m_referenceFaces;
+
+protected:
+ bool m_finished;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
diff --git a/tests/mgmt/face-status-publisher.cpp b/tests/mgmt/face-status-publisher.cpp
new file mode 100644
index 0000000..eb9946c
--- /dev/null
+++ b/tests/mgmt/face-status-publisher.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "face-status-publisher-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FaceStatusPublisherTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceManager, FaceStatusPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(TestFaceStatusPublisher)
+{
+ Name commandName("/localhost/nfd/faces/list");
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 55
+ // 55 divides 4400 (== 80), so only use 79 FaceStatuses and then two smaller ones
+ // to force a FaceStatus to span Data packets
+ for (int i = 0; i < 79; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+
+ uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_front(dummy);
+
+ add(dummy);
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+ uint64_t filler = std::numeric_limits<uint32_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_front(dummy);
+
+ add(dummy);
+ }
+
+ ndn::EncodingBuffer buffer;
+
+ m_face->onReceiveData +=
+ bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+ m_publisher.publish();
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/mgmt/segment-publisher.cpp b/tests/mgmt/segment-publisher.cpp
new file mode 100644
index 0000000..0d65ee6
--- /dev/null
+++ b/tests/mgmt/segment-publisher.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "mgmt/segment-publisher.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/app-face.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("SegmentPublisherTest");
+
+class TestSegmentPublisher : public SegmentPublisher
+{
+public:
+ TestSegmentPublisher(shared_ptr<AppFace> face,
+ const Name& prefix,
+ const uint64_t limit=10000)
+ : SegmentPublisher(face, prefix)
+ , m_limit((limit == 0)?(1):(limit))
+ {
+
+ }
+
+ virtual
+ ~TestSegmentPublisher()
+ {
+
+ }
+
+ uint16_t
+ getLimit() const
+ {
+ return m_limit;
+ }
+
+protected:
+
+ virtual size_t
+ generate(ndn::EncodingBuffer& outBuffer)
+ {
+ size_t totalLength = 0;
+ for (uint64_t i = 0; i < m_limit; i++)
+ {
+ totalLength += prependNonNegativeIntegerBlock(outBuffer, ndn::Tlv::Content, i);
+ }
+ return totalLength;
+ }
+
+protected:
+ const uint64_t m_limit;
+};
+
+class SegmentPublisherFixture : public BaseFixture
+{
+public:
+ SegmentPublisherFixture()
+ : m_face(make_shared<InternalFace>())
+ , m_publisher(m_face, "/localhost/nfd/SegmentPublisherFixture")
+ , m_finished(false)
+ {
+
+ }
+
+ void
+ validate(const Data& data)
+ {
+ Block payload = data.getContent();
+ NFD_LOG_DEBUG("payload size (w/o Content TLV): " << payload.value_size());
+
+ m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+ uint64_t segmentNo = data.getName()[-1].toSegment();
+ if (data.getFinalBlockId() != data.getName()[-1])
+ {
+ return;
+ }
+
+ NFD_LOG_DEBUG("got final block: #" << segmentNo);
+
+ // wrap data in a single Content TLV for easy parsing
+ m_buffer.prependVarNumber(m_buffer.size());
+ m_buffer.prependVarNumber(ndn::Tlv::Content);
+
+ BOOST_TEST_CHECKPOINT("creating parser");
+ ndn::Block parser(m_buffer.buf(), m_buffer.size());
+ BOOST_TEST_CHECKPOINT("parsing aggregated response");
+ parser.parse();
+
+ BOOST_REQUIRE_EQUAL(parser.elements_size(), m_publisher.getLimit());
+
+ uint64_t expectedNo = m_publisher.getLimit() - 1;
+ for (Block::element_const_iterator i = parser.elements_begin();
+ i != parser.elements_end();
+ ++i)
+ {
+ uint64_t number = readNonNegativeInteger(*i);
+ BOOST_REQUIRE_EQUAL(number, expectedNo);
+ --expectedNo;
+ }
+ m_finished = true;
+ }
+
+protected:
+ shared_ptr<InternalFace> m_face;
+ TestSegmentPublisher m_publisher;
+ ndn::EncodingBuffer m_buffer;
+
+protected:
+ bool m_finished;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtSegmentPublisher, SegmentPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Generate)
+{
+ m_face->onReceiveData +=
+ bind(&SegmentPublisherFixture::validate, this, _1);
+
+ m_publisher.publish();
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd