6 changed files with 387 additions and 31 deletions
@ -0,0 +1,97 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
||||
/*
|
||||
* Copyright (c) 2013-2019 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/>.
|
||||
* |
||||
* @author Chavoosh Ghasemi <chghasemi@cs.arizona.edu> |
||||
*/ |
||||
|
||||
#include "ndn-cxx/metadata-object.hpp" |
||||
|
||||
#include <boost/lexical_cast.hpp> |
||||
|
||||
namespace ndn { |
||||
|
||||
static_assert(std::is_base_of<tlv::Error, MetadataObject::Error>::value, |
||||
"MetadataObject::Error must inherit from tlv::Error"); |
||||
|
||||
const name::Component KEYWORD_METADATA_COMP = "20 08 6D65746164617461"_block; // 32=metadata
|
||||
|
||||
MetadataObject::MetadataObject() = default; |
||||
|
||||
MetadataObject::MetadataObject(const Data& data) |
||||
{ |
||||
if (data.getContentType() != tlv::ContentType_Blob) { |
||||
BOOST_THROW_EXCEPTION(Error("Expected ContentType to be BLOB but " + |
||||
boost::lexical_cast<std::string>(data.getContentType()) + |
||||
" is provided")); |
||||
} |
||||
|
||||
if (!isValidName(data.getName())) { |
||||
BOOST_THROW_EXCEPTION(Error("Name " + data.getName().toUri() + |
||||
" is not a valid MetadataObject name")); |
||||
} |
||||
|
||||
data.getContent().parse(); |
||||
// ignore non-Name elements before the first one
|
||||
m_versionedName.wireDecode(data.getContent().get(tlv::Name)); |
||||
} |
||||
|
||||
Data |
||||
MetadataObject::makeData(Name discoveryInterestName, |
||||
KeyChain& keyChain, |
||||
const ndn::security::SigningInfo& si, |
||||
optional<uint64_t> version, |
||||
time::milliseconds freshnessPeriod) const |
||||
{ |
||||
if (discoveryInterestName.empty() || discoveryInterestName[-1] != KEYWORD_METADATA_COMP) { |
||||
BOOST_THROW_EXCEPTION(Error("Name " + discoveryInterestName.toUri() + |
||||
" is not a valid discovery Interest name")); |
||||
} |
||||
discoveryInterestName.appendVersion(version); |
||||
discoveryInterestName.appendSegment(0); |
||||
|
||||
Data data(discoveryInterestName); |
||||
data.setContent(m_versionedName.wireEncode()); |
||||
data.setFreshnessPeriod(freshnessPeriod); |
||||
keyChain.sign(data, si); |
||||
|
||||
return data; |
||||
} |
||||
|
||||
MetadataObject& |
||||
MetadataObject::setVersionedName(const Name& name) |
||||
{ |
||||
m_versionedName = name; |
||||
return *this; |
||||
} |
||||
|
||||
bool |
||||
MetadataObject::isValidName(const Name& name) |
||||
{ |
||||
return name.size() >= 3 && name[-3] == KEYWORD_METADATA_COMP && |
||||
name[-2].isVersion() && name[-1].isSegment(); |
||||
} |
||||
|
||||
Interest |
||||
MetadataObject::makeDiscoveryInterest(Name name) |
||||
{ |
||||
return Interest(name.append(KEYWORD_METADATA_COMP)) |
||||
.setCanBePrefix(true) |
||||
.setMustBeFresh(true); |
||||
} |
||||
|
||||
} // namespace ndn
|
@ -0,0 +1,126 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
||||
/*
|
||||
* Copyright (c) 2013-2019 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/>.
|
||||
* |
||||
* @author Chavoosh Ghasemi <chghasemi@cs.arizona.edu> |
||||
*/ |
||||
|
||||
#ifndef NDN_METADATA_OBJECT_HPP |
||||
#define NDN_METADATA_OBJECT_HPP |
||||
|
||||
#include "ndn-cxx/data.hpp" |
||||
#include "ndn-cxx/interest.hpp" |
||||
#include "ndn-cxx/security/v2/key-chain.hpp" |
||||
|
||||
namespace ndn { |
||||
|
||||
/**
|
||||
* @brief Class for RDR-style metadata encoding/decoding. |
||||
* |
||||
* The interest and data packets dealing with metadata (called "discovery interest" |
||||
* and "metadata", respectively) follow a specific format. |
||||
* @see https://redmine.named-data.net/projects/ndn-tlv/wiki/RDR
|
||||
* |
||||
* Realtime Data Retrieval (RDR) is a protocol for discovering the latest version number |
||||
* of a given data collection. There are two names in an RDR metadata object: |
||||
* @li the **versioned name** is a prefix of the data collection, and generally |
||||
* contains a version component. It appears in the Content element of the metadata object. |
||||
* @li the **metadata name** is the name of the metadata object itself, and includes |
||||
* a keyword name component `32=metadata`, as well as version and segment components. |
||||
*/ |
||||
class MetadataObject |
||||
{ |
||||
public: |
||||
class Error : public tlv::Error |
||||
{ |
||||
public: |
||||
using tlv::Error::Error; |
||||
}; |
||||
|
||||
/**
|
||||
* @brief Create an empty metadata object |
||||
*/ |
||||
MetadataObject(); |
||||
|
||||
/**
|
||||
* @brief Construct a metadata object by decoding of the given Data packet |
||||
* @throw tlv::Error the Data is not a valid metadata packet |
||||
*/ |
||||
explicit |
||||
MetadataObject(const Data& data); |
||||
|
||||
/**
|
||||
* @brief Create a Data packet representing this metadata object |
||||
* |
||||
* @param discoveryInterestName the discovery Interest's name, which must end with |
||||
* a keyword name component `32=metadata` |
||||
* @param keyChain KeyChain to sign the Data |
||||
* @param si signing parameters |
||||
* @param version version number of metadata packet; if nullopt, use current Unix |
||||
* timestamp (in milliseconds) as the version number |
||||
* @param freshnessPeriod freshness period of metadata packet |
||||
* |
||||
* @throw tlv::Error @p discoveryInterestName is not valid |
||||
*/ |
||||
Data |
||||
makeData(Name discoveryInterestName, |
||||
KeyChain& keyChain, |
||||
const ndn::security::SigningInfo& si = KeyChain::getDefaultSigningInfo(), |
||||
optional<uint64_t> version = nullopt, |
||||
time::milliseconds freshnessPeriod = 10_ms) const; |
||||
|
||||
/**
|
||||
* @brief Return the versioned name (i.e., the name inside the content) |
||||
*/ |
||||
const Name& |
||||
getVersionedName() const |
||||
{ |
||||
return m_versionedName; |
||||
} |
||||
|
||||
/**
|
||||
* @brief Set the versioned name |
||||
* |
||||
* Any metadata packet carries a versioned name in its payload where it shows the name |
||||
* and the latest version of a data stream. For instance, `/ndn/test/%FD%97%47%1E%6C` is |
||||
* a versioned name that shows the latest version of `/ndn/test`. |
||||
*/ |
||||
MetadataObject& |
||||
setVersionedName(const Name& name); |
||||
|
||||
public: // static methods
|
||||
/**
|
||||
* @brief Check whether @p name can be a valid metadata name |
||||
*/ |
||||
static bool |
||||
isValidName(const Name& name); |
||||
|
||||
/**
|
||||
* @brief Generate a discovery interest packet based on @p name |
||||
* |
||||
* @param name prefix of data collection |
||||
*/ |
||||
static Interest |
||||
makeDiscoveryInterest(Name name); |
||||
|
||||
private: |
||||
Name m_versionedName; |
||||
}; |
||||
|
||||
} // namespace ndn
|
||||
|
||||
#endif // NDN_METADATA_OBJECT_HPP
|
@ -0,0 +1,152 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
||||
/*
|
||||
* Copyright (c) 2013-2019 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/>.
|
||||
* |
||||
* @author Chavoosh Ghasemi <chghasemi@cs.arizona.edu> |
||||
*/ |
||||
|
||||
#include "ndn-cxx/metadata-object.hpp" |
||||
|
||||
#include "tests/boost-test.hpp" |
||||
#include "tests/identity-management-fixture.hpp" |
||||
|
||||
namespace ndn { |
||||
namespace tests { |
||||
|
||||
class MetadataObjectFixture : public IdentityManagementFixture |
||||
{ |
||||
public: |
||||
MetadataObjectFixture() |
||||
: metadataComponent(32, reinterpret_cast<const uint8_t*>("metadata"), std::strlen("metadata")) |
||||
, versionedContentName(Name(baseContentName) |
||||
.appendVersion(342092199154ULL)) |
||||
, metadataFullName(Name(baseContentName) |
||||
.append(metadataComponent) |
||||
.appendVersion(metadataVerNo) |
||||
.appendSegment(0)) |
||||
{ |
||||
} |
||||
|
||||
protected: |
||||
const name::Component metadataComponent; |
||||
|
||||
// content prefix
|
||||
const Name baseContentName = "/ndn/unit/tests"; |
||||
const Name versionedContentName; |
||||
|
||||
// metadata prefix
|
||||
const uint64_t metadataVerNo = 89400192181ULL; |
||||
const Name metadataFullName; |
||||
}; |
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(TestMetadataObject, MetadataObjectFixture) |
||||
|
||||
BOOST_AUTO_TEST_CASE(EncodeDecode) |
||||
{ |
||||
MetadataObject metadata1; |
||||
metadata1.setVersionedName(versionedContentName); |
||||
|
||||
// pass metadata version number
|
||||
const Data data1 = metadata1.makeData(metadataFullName.getPrefix(-2), m_keyChain, |
||||
KeyChain::getDefaultSigningInfo(), metadataVerNo); |
||||
|
||||
BOOST_CHECK_EQUAL(metadata1.getVersionedName(), versionedContentName); |
||||
BOOST_CHECK_EQUAL(data1.getName(), metadataFullName); |
||||
BOOST_CHECK_EQUAL(data1.getFreshnessPeriod(), 10_ms); |
||||
|
||||
// do not pass metadata version number
|
||||
metadata1.setVersionedName(versionedContentName); |
||||
const Data data2 = metadata1.makeData(metadataFullName.getPrefix(-2), m_keyChain); |
||||
BOOST_CHECK_NE(data2.getName()[-2].toVersion(), metadataVerNo); |
||||
|
||||
// construct a metadata object based on a valid metadata packet
|
||||
MetadataObject metadata2(data1); |
||||
|
||||
BOOST_CHECK_EQUAL(metadata2.getVersionedName(), versionedContentName); |
||||
BOOST_CHECK(baseContentName.isPrefixOf(metadata2.makeData(metadataFullName.getPrefix(-2), |
||||
m_keyChain).getName())); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(InvalidFormat) |
||||
{ |
||||
Data data; |
||||
|
||||
// invalid content type
|
||||
data.setName(Name("/ndn/unit/test").append(metadataComponent)); |
||||
data.setContentType(tlv::ContentType_Key); |
||||
BOOST_CHECK_THROW(MetadataObject metadata(data), tlv::Error); |
||||
|
||||
// invalid metadata name
|
||||
data.setName("/ndn/unit/test"); |
||||
data.setContentType(tlv::ContentType_Blob); |
||||
BOOST_CHECK_THROW(MetadataObject metadata(data), tlv::Error); |
||||
|
||||
// empty content
|
||||
data.setName(Name("ndn/unit/test").append(metadataComponent)); |
||||
BOOST_CHECK_THROW(MetadataObject metadata(data), tlv::Error); |
||||
|
||||
// non-empty content with no name element
|
||||
data.setContent("F000"_block); |
||||
BOOST_CHECK_THROW(MetadataObject metadata(data), tlv::Error); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(IsValidName) |
||||
{ |
||||
// valid name
|
||||
Name name = Name("/ndn/unit/test") |
||||
.append(metadataComponent) |
||||
.appendVersion() |
||||
.appendSegment(0); |
||||
BOOST_CHECK(MetadataObject::isValidName(name)); |
||||
|
||||
// invalid names
|
||||
// segment component is missing
|
||||
BOOST_CHECK_EQUAL(MetadataObject::isValidName(name.getPrefix(-1)), false); |
||||
|
||||
// version component is missing
|
||||
BOOST_CHECK_EQUAL(MetadataObject::isValidName(name.getPrefix(-2)), false); |
||||
|
||||
// keyword name component `32=keyword` is missing
|
||||
BOOST_CHECK_EQUAL(MetadataObject::isValidName(name.getPrefix(-3)), false); |
||||
|
||||
// too short name
|
||||
BOOST_CHECK_EQUAL(MetadataObject::isValidName(name.getPrefix(-4)), false); |
||||
|
||||
// out-of-order segment and version components
|
||||
name = name.getPrefix(-2).appendSegment(0).appendVersion(); |
||||
BOOST_CHECK_EQUAL(MetadataObject::isValidName(name), false); |
||||
|
||||
// invalid name component keyword
|
||||
name = name.getPrefix(-3) |
||||
.append(32, reinterpret_cast<const uint8_t*>("foo"), std::strlen("foo")) |
||||
.appendVersion() |
||||
.appendSegment(0); |
||||
BOOST_CHECK_EQUAL(MetadataObject::isValidName(name), false); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_CASE(MakeDiscoveryInterest) |
||||
{ |
||||
Interest interest = MetadataObject::makeDiscoveryInterest(baseContentName); |
||||
BOOST_CHECK_EQUAL(interest.getName(), Name(baseContentName).append(metadataComponent)); |
||||
BOOST_CHECK(interest.getCanBePrefix()); |
||||
BOOST_CHECK(interest.getMustBeFresh()); |
||||
} |
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // TestMetadataObject
|
||||
|
||||
} // namespace tests
|
||||
} // namespace ndn
|
Loading…
Reference in new issue