chunks: segmented file transfer
refs #3071
Change-Id: I88e4fc1a8e33a0d61a95e2291cccc7b998647489
diff --git a/AUTHORS.md b/AUTHORS.md
index a4e6eec..93c29e2 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -16,3 +16,6 @@
* Qi Zhao <https://www.linkedin.com/pub/qi-zhao/73/835/9a3>
* Seunghyun Yoo <http://relue2718.com/>
* Seungbae Kim <https://sites.google.com/site/sbkimcv/>
+* Wentao Shang <http://irl.cs.ucla.edu/~wentao/>
+* Steve DiBenedetto <https://dibenede.github.io>
+* Andrea Tosatto <https://linkedin.com/in/tosattoandrea>
diff --git a/README.md b/README.md
index eda9d2b..24de965 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@
Tools in this collection include:
* [peek](tools/peek): transmit a single packet between a consumer and a producer
+* [chunks](tools/chunks): segmented file transfer between a consumer and producer
* [ping](tools/ping): test reachability between two nodes
* [dump](tools/dump): analyze traffic on wire
* [dissect](tools/dissect): inspect TLV structure of NDN packet format
diff --git a/core/common.hpp b/core/common.hpp
index 09d378c..01a30c4 100644
--- a/core/common.hpp
+++ b/core/common.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2015, Regents of the University of California,
+ * Copyright (c) 2014-2016, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -63,6 +63,7 @@
#include <ndn-cxx/security/key-chain.hpp>
#include <ndn-cxx/security/signing-helpers.hpp>
#include <ndn-cxx/security/signing-info.hpp>
+#include <ndn-cxx/util/backports.hpp>
#include <ndn-cxx/util/scheduler.hpp>
#include <ndn-cxx/util/scheduler-scoped-event-id.hpp>
#include <ndn-cxx/util/signal.hpp>
diff --git a/tests/chunks/consumer.t.cpp b/tests/chunks/consumer.t.cpp
new file mode 100644
index 0000000..0ce7290
--- /dev/null
+++ b/tests/chunks/consumer.t.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/consumer.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/security/validator-null.hpp>
+
+#include <boost/test/output_test_stream.hpp>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+using boost::test_tools::output_test_stream;
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestConsumer)
+
+BOOST_AUTO_TEST_CASE(OutputDataSequential)
+{
+ // Test sequential segments in the right order
+ // Segment order: 0 1 2
+
+ std::string name("/ndn/chunks/test");
+
+ std::vector<std::string> testStrings {
+ "",
+
+ "a1b2c3%^&(#$&%^$$/><",
+
+ "123456789123456789123456789123456789123456789123456789123456789"
+ "123456789123456789123456789123456789123456789123456789123456789",
+
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. "
+ "Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur "
+ "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+ "consequat massa Donec pede justo,"
+ };
+
+ util::DummyClientFace face;
+ ValidatorNull validator;
+ output_test_stream output("");
+ Consumer cons(face, validator, false, output);
+
+ auto interest = makeInterest(name);
+
+ for (size_t i = 0; i < testStrings.size(); ++i) {
+ output.flush();
+
+ auto data = makeData(Name(name).appendVersion(1).appendSegment(i));
+ data->setContent(reinterpret_cast<const uint8_t*>(testStrings[i].data()),
+ testStrings[i].size());
+
+ cons.m_bufferedData[i] = data;
+ cons.writeInOrderData();
+
+ BOOST_CHECK(output.is_equal(testStrings[i]));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(OutputDataUnordered)
+{
+ // Test unordered segments
+ // Segment order: 1 0 2
+
+ std::string name("/ndn/chunks/test");
+
+ std::vector<std::string> testStrings {
+ "a1b2c3%^&(#$&%^$$/><",
+
+ "123456789123456789123456789123456789123456789123456789123456789"
+ "123456789123456789123456789123456789123456789123456789123456789",
+
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. "
+ "Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur "
+ "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+ "consequat massa Donec pede justo,"
+ };;
+
+ util::DummyClientFace face;
+ ValidatorNull validator;
+ output_test_stream output("");
+ Consumer cons(face, validator, false, output);
+
+ auto interest = makeInterest(name);
+ std::vector<shared_ptr<Data>> dataStore;
+
+ for (size_t i = 0; i < testStrings.size(); ++i) {
+ auto data = makeData(Name(name).appendVersion(1).appendSegment(i));
+ data->setContent(reinterpret_cast<const uint8_t*>(testStrings[i].data()),
+ testStrings[i].size());
+
+ dataStore.push_back(data);
+ }
+
+ output.flush();
+ cons.m_bufferedData[1] = dataStore[1];
+ cons.writeInOrderData();
+ BOOST_CHECK(output.is_equal(""));
+
+ output.flush();
+ cons.m_bufferedData[0] = dataStore[0];
+ cons.writeInOrderData();
+ BOOST_CHECK(output.is_equal(testStrings[0] + testStrings[1]));
+
+ output.flush();
+ cons.m_bufferedData[2] = dataStore[2];
+ cons.writeInOrderData();
+ BOOST_CHECK(output.is_equal(testStrings[2]));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestConsumer
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/discover-version-fixed.t.cpp b/tests/chunks/discover-version-fixed.t.cpp
new file mode 100644
index 0000000..a57e5c8
--- /dev/null
+++ b/tests/chunks/discover-version-fixed.t.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/discover-version-fixed.hpp"
+
+#include "discover-version-fixture.hpp"
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+class DiscoverVersionFixedFixture : public DiscoverVersionFixture
+{
+public:
+ DiscoverVersionFixedFixture()
+ : DiscoverVersionFixture(makeOptions())
+ , version(1449227841747)
+ {
+ setDiscover(make_unique<DiscoverVersionFixed>(Name(name).appendVersion(version),
+ face, makeOptions()));
+ }
+
+protected:
+ uint64_t version; //Version to find
+};
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestDiscoverVersionFixed)
+
+BOOST_FIXTURE_TEST_CASE(RequestedVersionAvailable, DiscoverVersionFixedFixture)
+{
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+ face.receive(*makeDataWithVersion(version));
+
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ BOOST_CHECK_EQUAL(discoveredVersion, version);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ auto lastInterest = face.sentInterests.back();
+ BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(lastInterest.getMaxSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(lastInterest.getMinSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+}
+
+BOOST_FIXTURE_TEST_CASE(NoVersionsAvailable, DiscoverVersionFixedFixture)
+{
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+ for (int retries = 0; retries < maxRetriesOnTimeoutOrNack; ++retries) {
+ advanceClocks(io, interestLifetime, 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + retries);
+ }
+
+ for (const auto& lastInterest : face.sentInterests) {
+ BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(lastInterest.getMaxSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(lastInterest.getMinSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(Name(name).appendVersion(version)), true);
+ }
+
+ advanceClocks(io, interestLifetime, 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ // check if discovered version is the default value
+ BOOST_CHECK_EQUAL(discoveredVersion, 0);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesOnTimeoutOrNack + 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(DataNotSegment, DiscoverVersionFixedFixture)
+{
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+ std::vector<std::string> randomStrings {
+ "",
+ "abcdefg",
+ "12345",
+ "qr%67a3%4e"
+ };
+
+ Exclude expectedExclude;
+ for (size_t retries = 0; retries < randomStrings.size(); ++retries) {
+ auto data = make_shared<Data>(Name(name).appendVersion(version).append(randomStrings[retries]));
+ data->setFinalBlockId(name::Component::fromSegment(0));
+ data = signData(data);
+
+ face.receive(*data);
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1 + retries);
+ auto lastInterest = face.sentInterests.back();
+ if (randomStrings[retries] != "")
+ expectedExclude.excludeOne(name::Component::fromEscapedString(randomStrings[retries]));
+ BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+ BOOST_CHECK_EQUAL(lastInterest.getMaxSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(lastInterest.getMinSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ }
+
+ advanceClocks(io, interestLifetime, maxRetriesOnTimeoutOrNack + 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ // check if discovered version is the default value
+ BOOST_CHECK_EQUAL(discoveredVersion, 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDiscoverVersionFixed
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/discover-version-fixture.hpp b/tests/chunks/discover-version-fixture.hpp
new file mode 100644
index 0000000..9009301
--- /dev/null
+++ b/tests/chunks/discover-version-fixture.hpp
@@ -0,0 +1,104 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_TESTS_CHUNKS_DISCOVER_VERSION_FIXTURE_HPP
+#define NDN_TOOLS_TESTS_CHUNKS_DISCOVER_VERSION_FIXTURE_HPP
+
+#include "tools/chunks/catchunks/discover-version.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+class DiscoverVersionFixture : public ndn::tests::UnitTestTimeFixture, virtual protected Options
+{
+public:
+ DiscoverVersionFixture(const Options& options)
+ : Options(options)
+ , face(io)
+ , name("/ndn/chunks/test")
+ , isDiscoveryFinished(false)
+ , discoveredVersion(0)
+ {
+ }
+
+protected:
+ void setDiscover(unique_ptr<DiscoverVersion> disc)
+ {
+ discover = std::move(disc);
+ discover->onDiscoverySuccess.connect(bind(&DiscoverVersionFixture::onSuccess, this, _1));
+ discover->onDiscoveryFailure.connect(bind(&DiscoverVersionFixture::onFailure, this, _1));
+ }
+
+ shared_ptr<Data>
+ makeDataWithVersion(uint64_t version)
+ {
+ auto data = make_shared<Data>(Name(name).appendVersion(version).appendSegment(0));
+ data->setFinalBlockId(name::Component::fromSegment(0));
+ return ndn::tests::signData(data);
+ }
+
+ static Options
+ makeOptions()
+ {
+ Options options;
+ options.isVerbose = false;
+ options.interestLifetime = time::seconds(1);
+ options.maxRetriesOnTimeoutOrNack = 3;
+ return options;
+ }
+
+ virtual void
+ onSuccess(const Data& data)
+ {
+ isDiscoveryFinished = true;
+
+ if (data.getName()[name.size()].isVersion())
+ discoveredVersion = data.getName()[name.size()].toVersion();
+ }
+
+ virtual void
+ onFailure(const std::string& reason)
+ {
+ isDiscoveryFinished = true;
+ }
+
+protected:
+ boost::asio::io_service io;
+ util::DummyClientFace face;
+ Name name;
+ unique_ptr<DiscoverVersion> discover;
+ bool isDiscoveryFinished;
+ uint64_t discoveredVersion;
+};
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_TESTS_CHUNKS_DISCOVER_VERSION_FIXTURE_HPP
diff --git a/tests/chunks/discover-version-iterative.t.cpp b/tests/chunks/discover-version-iterative.t.cpp
new file mode 100644
index 0000000..233f36c
--- /dev/null
+++ b/tests/chunks/discover-version-iterative.t.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/discover-version-iterative.hpp"
+
+#include "discover-version-fixture.hpp"
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+class DiscoverVersionIterativeFixture : public DiscoverVersionFixture,
+ protected DiscoverVersionIterativeOptions
+{
+public:
+ typedef DiscoverVersionIterativeOptions Options;
+
+public:
+ explicit
+ DiscoverVersionIterativeFixture(const Options& opt = makeOptionsIterative())
+ : chunks::Options(opt)
+ , DiscoverVersionFixture(opt)
+ , Options(opt)
+ {
+ setDiscover(make_unique<DiscoverVersionIterative>(Name(name), face, opt));
+ }
+
+protected:
+ static Options
+ makeOptionsIterative()
+ {
+ Options options;
+ options.isVerbose = false;
+ options.maxRetriesOnTimeoutOrNack = 3;
+ options.maxRetriesAfterVersionFound = 1;
+ return options;
+ }
+};
+
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestDiscoverVersionIterative)
+
+BOOST_FIXTURE_TEST_CASE(SingleVersionAvailable, DiscoverVersionIterativeFixture)
+{
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ uint64_t version = 1449241767037;
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ auto lastInterest = face.sentInterests.back();
+ BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+ // Send first segment for the right version
+ face.receive(*makeDataWithVersion(version));
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
+ lastInterest = face.sentInterests.back();
+ Exclude expectedExclude;
+ expectedExclude.excludeBefore(name::Component::fromVersion(version));
+ BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+ // Generate the timeout
+ for (int retries = 0; retries < maxRetriesAfterVersionFound; ++retries) {
+ advanceClocks(io, interestLifetime, 1);
+
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), retries + 3);
+ lastInterest = face.sentInterests.back();
+ Exclude expectedExclude;
+ expectedExclude.excludeBefore(name::Component::fromVersion(version));
+ BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+ }
+
+ advanceClocks(io, interestLifetime, 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ BOOST_CHECK_EQUAL(discoveredVersion, version);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesAfterVersionFound + 2);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(NoVersionsAvailable, DiscoverVersionIterativeFixture)
+{
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+ for (int retries = 0; retries < maxRetriesOnTimeoutOrNack; ++retries) {
+ advanceClocks(io, interestLifetime, 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + retries);
+ }
+
+ for (auto& lastInterest : face.sentInterests) {
+ BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+ }
+
+ advanceClocks(io, interestLifetime, 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesOnTimeoutOrNack + 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleVersionsAvailable, DiscoverVersionIterativeFixture)
+{
+ // nVersions must be positive
+ const uint64_t nVersions = 5;
+
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ auto lastInterest = face.sentInterests.back();
+ BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+ for (uint64_t nSentVersions = 0; nSentVersions < nVersions; ++nSentVersions) {
+ face.receive(*makeDataWithVersion(nSentVersions));
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + nSentVersions);
+ lastInterest = face.sentInterests.back();
+ Exclude expectedExclude;
+ expectedExclude.excludeBefore(name::Component::fromVersion(nSentVersions));
+ BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+ }
+
+ for (int retries = 0; retries < maxRetriesAfterVersionFound; ++retries) {
+ advanceClocks(io, interestLifetime, 1);
+
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + retries + nVersions);
+ lastInterest = face.sentInterests.back();
+ Exclude expectedExclude;
+ expectedExclude.excludeBefore(name::Component::fromVersion(nVersions - 1));
+ BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+ }
+
+ advanceClocks(io, interestLifetime, 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ BOOST_CHECK_EQUAL(discoveredVersion, nVersions - 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), nVersions + maxRetriesAfterVersionFound + 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleVersionsAvailableDescendent, DiscoverVersionIterativeFixture)
+{
+ // nVersions must be positive
+ const uint64_t nVersions = 5;
+
+ discover->run();
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ auto lastInterest = face.sentInterests.back();
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+ BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+ for (uint64_t nVersionsToSend = nVersions; nVersionsToSend > 0; --nVersionsToSend) {
+ face.receive(*makeDataWithVersion(nVersionsToSend - 1));
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
+ lastInterest = face.sentInterests.back();
+ Exclude expectedExclude;
+ expectedExclude.excludeBefore(name::Component::fromVersion(nVersions - 1));
+ BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+ BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+ BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+ }
+
+ advanceClocks(io, interestLifetime, maxRetriesAfterVersionFound + 1);
+ BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+ BOOST_CHECK_EQUAL(discoveredVersion, nVersions - 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesAfterVersionFound + 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDiscoverVersionIterative
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/pipeline-interests.t.cpp b/tests/chunks/pipeline-interests.t.cpp
new file mode 100644
index 0000000..98abfa4
--- /dev/null
+++ b/tests/chunks/pipeline-interests.t.cpp
@@ -0,0 +1,378 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/pipeline-interests.hpp"
+#include "tools/chunks/catchunks/data-fetcher.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+class PipelineInterestsFixture : public UnitTestTimeFixture
+{
+public:
+ typedef PipelineInterestsOptions Options;
+
+public:
+ PipelineInterestsFixture()
+ : face(io)
+ , opt(makeOptions())
+ , name("/ndn/chunks/test")
+ , pipeline(face, opt)
+ , nDataSegments(0)
+ , nReceivedSegments(0)
+ , hasFailed(false)
+ {
+ }
+
+protected:
+ shared_ptr<Data>
+ makeDataWithSegment(uint64_t segmentNo, bool setFinalBlockId = true)
+ {
+ auto data = make_shared<Data>(Name(name).appendVersion(0).appendSegment(segmentNo));
+ if (setFinalBlockId)
+ data->setFinalBlockId(name::Component::fromSegment(nDataSegments - 1));
+ return signData(data);
+ }
+
+ void
+ runWithData(const Data& data)
+ {
+ pipeline.runWithExcludedSegment(data,
+ bind(&PipelineInterestsFixture::onData, this, _1, _2),
+ bind(&PipelineInterestsFixture::onFailure, this, _1));
+ }
+
+private:
+ void
+ onData(const Interest& interest, const Data& data)
+ {
+ nReceivedSegments++;
+ }
+
+ void
+ onFailure(const std::string& reason)
+ {
+ hasFailed = true;
+ }
+
+ static Options
+ makeOptions()
+ {
+ Options options;
+ options.isVerbose = false;
+ options.interestLifetime = time::seconds(1);
+ options.maxRetriesOnTimeoutOrNack = 3;
+ options.maxPipelineSize = 5;
+ return options;
+ }
+
+protected:
+ boost::asio::io_service io;
+ util::DummyClientFace face;
+ Options opt;
+ Name name;
+ PipelineInterests pipeline;
+ uint64_t nDataSegments;
+ uint64_t nReceivedSegments;
+ bool hasFailed;
+};
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestPipelineInterests)
+
+BOOST_FIXTURE_TEST_CASE(FewerSegmentsThanPipelineCapacity, PipelineInterestsFixture)
+{
+ nDataSegments = 3;
+ BOOST_ASSERT(nDataSegments <= opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(0));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), nDataSegments - 1);
+
+ for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
+ face.receive(*makeDataWithSegment(i));
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_CHECK_EQUAL(nReceivedSegments, i);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), nDataSegments - 1);
+ // check if the interest for the segment i+1 is well formed
+ auto sentInterest = face.sentInterests[i];
+ BOOST_CHECK_EQUAL(sentInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(sentInterest.getMaxSuffixComponents(), 1);
+ BOOST_CHECK_EQUAL(sentInterest.getMustBeFresh(), opt.mustBeFresh);
+ BOOST_CHECK_EQUAL(Name(name).isPrefixOf(sentInterest.getName()), true);
+ BOOST_CHECK_EQUAL(sentInterest.getName()[-1].toSegment(), i + 1);
+ }
+
+ BOOST_CHECK_EQUAL(hasFailed, false);
+
+ advanceClocks(io, ndn::DEFAULT_INTEREST_LIFETIME, opt.maxRetriesOnTimeoutOrNack + 1);
+ BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(FullPipeline, PipelineInterestsFixture)
+{
+ nDataSegments = 13;
+ BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(nDataSegments - 1));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+ for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
+ face.receive(*makeDataWithSegment(i));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_CHECK_EQUAL(nReceivedSegments, i + 1);
+
+ if (i < nDataSegments - opt.maxPipelineSize - 1) {
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize + i + 1);
+ // check if the interest for the segment i+1 is well formed
+ auto sentInterest = face.sentInterests[i];
+ BOOST_CHECK_EQUAL(sentInterest.getExclude().size(), 0);
+ BOOST_CHECK_EQUAL(sentInterest.getMaxSuffixComponents(), 1);
+ BOOST_CHECK_EQUAL(sentInterest.getMustBeFresh(), opt.mustBeFresh);
+ BOOST_CHECK_EQUAL(Name(name).isPrefixOf(sentInterest.getName()), true);
+ BOOST_CHECK_EQUAL(sentInterest.getName()[-1].toSegment(), i);
+ }
+ else {
+ // all the interests have been sent for all the segments
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), nDataSegments - 1);
+ }
+ }
+
+ BOOST_CHECK_EQUAL(hasFailed, false);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeoutAllSegments, PipelineInterestsFixture)
+{
+ nDataSegments = 13;
+ BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(nDataSegments - 1));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+ for (int i = 0; i < opt.maxRetriesOnTimeoutOrNack; ++i) {
+ advanceClocks(io, opt.interestLifetime, 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * (i + 2));
+ BOOST_CHECK_EQUAL(nReceivedSegments, 0);
+
+ // A single retry for every pipeline element
+ for (size_t j = 0; j < opt.maxPipelineSize; ++j) {
+ auto interest = face.sentInterests[(opt.maxPipelineSize * (i + 1)) + j];
+ BOOST_CHECK_EQUAL(static_cast<size_t>(interest.getName()[-1].toSegment()), j);
+ }
+ }
+
+ advanceClocks(io, opt.interestLifetime, 1);
+ BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeoutAfterFinalBlockIdReceived, PipelineInterestsFixture)
+{
+ // the FinalBlockId is sent with the first segment, after the first segment failure the pipeline
+ // should fail
+
+ nDataSegments = 18;
+ BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(nDataSegments - 1));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+ // send a single segment for each pipeline element but not the first element
+ advanceClocks(io, opt.interestLifetime, 1);
+ for (uint64_t i = 1; i < opt.maxPipelineSize; ++i) {
+ face.receive(*makeDataWithSegment(i));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ }
+
+ // send a single data packet for each pipeline element
+ advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
+ for (uint64_t i = 0; i < opt.maxPipelineSize - 1; ++i) {
+ face.receive(*makeDataWithSegment(opt.maxPipelineSize + i));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ }
+ advanceClocks(io, opt.interestLifetime, 1);
+
+ size_t interestAfterFailure = face.sentInterests.size();
+
+ BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
+ BOOST_CHECK_EQUAL(hasFailed, true);
+
+ // these new segments should not generate new interests
+ advanceClocks(io, opt.interestLifetime, 1);
+ for (uint64_t i = 0; i < opt.maxPipelineSize - 1; ++i) {
+ face.receive(*makeDataWithSegment(opt.maxPipelineSize * 2 + i - 1));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ }
+
+ // no more interests after a failure
+ advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
+ BOOST_CHECK_EQUAL(interestAfterFailure, face.sentInterests.size());
+ BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeoutBeforeFinalBlockIdReceived, PipelineInterestsFixture)
+{
+ // the FinalBlockId is sent only with the last segment, all segments are sent except for the
+ // second one (segment #1); all segments are received correctly until the FinalBlockId is received
+
+ nDataSegments = 22;
+ BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(nDataSegments - 1, false));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+ advanceClocks(io, opt.interestLifetime, 1);
+ for (uint64_t i = 2; i < opt.maxPipelineSize; ++i) {
+ face.receive(*makeDataWithSegment(i, false));
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ auto lastInterest = face.sentInterests.back();
+ BOOST_CHECK_EQUAL(lastInterest.getName()[-1].toSegment(), opt.maxPipelineSize + i - 2);
+ }
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * 3 - 2);
+
+ // nack for the first pipeline element (segment #0)
+ auto nack = make_shared<lp::Nack>(face.sentInterests[opt.maxPipelineSize]);
+ nack->setReason(lp::NackReason::DUPLICATE);
+ face.receive(*nack);
+
+ // all the pipeline elements are two retries near the timeout error, but not the
+ // second (segment #1) that is only one retry near the timeout
+ advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
+ BOOST_CHECK_EQUAL(hasFailed, false);
+
+ // data for the first pipeline element (segment #0)
+ face.receive(*makeDataWithSegment(0, false));
+ BOOST_CHECK_EQUAL(hasFailed, false);
+
+ // data for all the pipeline element, but not the second (segment #1)
+ for (uint64_t i = opt.maxPipelineSize; i < nDataSegments - 1; ++i) {
+ if (i == nDataSegments - 2) {
+ face.receive(*makeDataWithSegment(i, true));
+ }
+ else {
+ face.receive(*makeDataWithSegment(i, false));
+ }
+ advanceClocks(io, time::nanoseconds(1), 1);
+ }
+ // timeout for the second pipeline element (segment #1), this should trigger an error
+ advanceClocks(io, opt.interestLifetime, 1);
+
+ BOOST_CHECK_EQUAL(nReceivedSegments, nDataSegments - 2);
+ BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(SegmentReceivedAfterTimeout, PipelineInterestsFixture)
+{
+ // the FinalBlockId is never sent, all the pipeline elements with a segment number greater than
+ // segment #0 will fail, after this failure also segment #0 fail and this should trigger an error
+
+ nDataSegments = 22;
+ BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(nDataSegments - 1, false));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+ advanceClocks(io, opt.interestLifetime, 1);
+
+ // nack for the first pipeline element (segment #0)
+ auto nack = make_shared<lp::Nack>(face.sentInterests[opt.maxPipelineSize]);
+ nack->setReason(lp::NackReason::DUPLICATE);
+ face.receive(*nack);
+ BOOST_CHECK_EQUAL(hasFailed, false);
+
+ // timeout for all the pipeline elements, but not the first (segment #0)
+ advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
+ BOOST_CHECK_EQUAL(hasFailed, false);
+
+ // data for the first pipeline element (segment #0), this should trigger an error because the
+ // others pipeline elements failed
+ face.receive(*makeDataWithSegment(0, false));
+ advanceClocks(io, time::nanoseconds(1), 1);
+
+ BOOST_CHECK_EQUAL(nReceivedSegments, 1);
+ BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(CongestionAllSegments, PipelineInterestsFixture)
+{
+ nDataSegments = 13;
+ BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+ runWithData(*makeDataWithSegment(nDataSegments - 1));
+ advanceClocks(io, time::nanoseconds(1), 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+ // send nack for all the pipeline elements first interest
+ for (size_t j = 0; j < opt.maxPipelineSize; j++) {
+ auto nack = make_shared<lp::Nack>(face.sentInterests[j]);
+ nack->setReason(lp::NackReason::CONGESTION);
+ face.receive(*nack);
+ advanceClocks(io, time::nanoseconds(1), 1);
+ }
+
+ // send nack for all the pipeline elements interests after the first
+ for (int i = 1; i <= opt.maxRetriesOnTimeoutOrNack; ++i) {
+ time::milliseconds backoffTime(static_cast<uint64_t>(std::pow(2, i)));
+ if (backoffTime > DataFetcher::MAX_CONGESTION_BACKOFF_TIME)
+ backoffTime = DataFetcher::MAX_CONGESTION_BACKOFF_TIME;
+
+ advanceClocks(io, backoffTime, 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * (i +1));
+
+ // A single retry for every pipeline element
+ for (size_t j = 0; j < opt.maxPipelineSize; ++j) {
+ auto interest = face.sentInterests[(opt.maxPipelineSize * i) + j];
+ BOOST_CHECK_LT(static_cast<size_t>(interest.getName()[-1].toSegment()), opt.maxPipelineSize);
+ }
+
+ for (size_t j = 0; j < opt.maxPipelineSize; j++) {
+ auto nack = make_shared<lp::Nack>(face.sentInterests[(opt.maxPipelineSize * i) + j]);
+ nack->setReason(lp::NackReason::CONGESTION);
+ face.receive(*nack);
+ advanceClocks(io, time::nanoseconds(1), 1);
+ }
+ }
+
+ BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterests
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/producer.t.cpp b/tests/chunks/producer.t.cpp
new file mode 100644
index 0000000..dc5346f
--- /dev/null
+++ b/tests/chunks/producer.t.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#include "tools/chunks/putchunks/producer.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/security/validator-null.hpp>
+
+#include <cmath>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestProducer)
+
+BOOST_AUTO_TEST_CASE(InputData)
+{
+ util::DummyClientFace face;
+ KeyChain keyChain;
+ security::SigningInfo signingInfo;
+ Name prefix("/ndn/chunks/test");
+ int maxSegmentSize = 40;
+ std::vector<std::string> testStrings {
+ "",
+
+ "a1b2c3%^&(#$&%^$$/><",
+
+ "123456789123456789123456789123456789123456789123456789123456789"
+ "123456789123456789123456789123456789123456789123456789123456789",
+
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. "
+ "Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur "
+ "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+ "consequat massa Donec pede justo,"
+ };
+
+ for (size_t i = 0; i < testStrings.size(); ++i) {
+ std::istringstream str(testStrings[i]);
+ Producer prod(prefix, face, keyChain, signingInfo, time::seconds(4), maxSegmentSize, false,
+ false, str);
+
+ size_t expectedSize = std::ceil(static_cast<double>(testStrings[i].size()) / maxSegmentSize);
+ if (testStrings[i].size() == 0)
+ expectedSize = 1;
+
+ BOOST_CHECK_EQUAL(prod.m_store.size(), expectedSize);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(RequestSegmentUnspecifiedVersion)
+{
+ boost::asio::io_service io;
+ util::DummyClientFace face(io, {true, true});
+ KeyChain keyChain;
+ security::SigningInfo signingInfo;
+ Name prefix("/ndn/chunks/test");
+ time::milliseconds freshnessPeriod(time::seconds(10));
+ size_t maxSegmentSize(40);
+ std::istringstream testString(std::string(
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget "
+ "dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, "
+ "nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, "
+ "sem. Nulla consequat massa Donec pede justo,"));
+
+ Name keyLocatorName = keyChain.getDefaultCertificateName().getPrefix(-1);
+
+ Producer producer(prefix, face, keyChain, signingInfo, freshnessPeriod, maxSegmentSize,
+ false, false, testString);
+ io.poll();
+
+ size_t nSegments = std::ceil(static_cast<double>(testString.str().size()) / maxSegmentSize);
+
+ // version request
+ face.receive(*makeInterest(prefix));
+ face.processEvents();
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+ auto lastData = face.sentData.back();
+ BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 2);
+ BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), 0);
+ BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+ BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+ BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+
+ // segment request
+ Name nameWithVersion(prefix);
+ nameWithVersion.append(lastData.getName()[-2]);
+ size_t requestSegmentNo = 1;
+
+ face.receive(*makeInterest(nameWithVersion.appendSegment(requestSegmentNo)));
+ face.processEvents();
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 2);
+ lastData = face.sentData.back();
+ BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 2);
+ BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), requestSegmentNo);
+ BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+ BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+ BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+}
+
+BOOST_AUTO_TEST_CASE(RequestSegmentSpecifiedVersion)
+{
+ boost::asio::io_service io;
+ util::DummyClientFace face(io, {true, true});
+ KeyChain keyChain;
+ security::SigningInfo signingInfo;
+ Name prefix("/ndn/chunks/test");
+ time::milliseconds freshnessPeriod(time::seconds(10));
+ size_t maxSegmentSize(40);
+ std::istringstream testString(std::string(
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget "
+ "dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, "
+ "nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, "
+ "sem. Nulla consequat massa Donec pede justo,"));
+
+ uint64_t version = 1449227841747;
+ Name keyLocatorName = keyChain.getDefaultCertificateName().getPrefix(-1);
+
+ Producer producer(prefix.appendVersion(version), face, keyChain, signingInfo, freshnessPeriod,
+ maxSegmentSize, false, false, testString);
+ io.poll();
+
+ size_t nSegments = std::ceil(static_cast<double>(testString.str().size()) / maxSegmentSize);
+
+ // version request
+ face.receive(*makeInterest(prefix));
+ face.processEvents();
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+ auto lastData = face.sentData.back();
+ BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 1);
+ BOOST_CHECK_EQUAL(lastData.getName()[-2].toVersion(), version);
+ BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), 0);
+ BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+ BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+ BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+
+ // segment request
+ Name nameWithVersion(prefix);
+ size_t requestSegmentNo = 1;
+
+ face.receive(*makeInterest(nameWithVersion.appendSegment(requestSegmentNo)));
+ face.processEvents();
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 2);
+ lastData = face.sentData.back();
+ BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 1);
+ BOOST_CHECK_EQUAL(lastData.getName()[-2].toVersion(), version);
+ BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), requestSegmentNo);
+ BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+ BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+ BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+}
+
+BOOST_AUTO_TEST_CASE(RequestNotExistingSegment)
+{
+ boost::asio::io_service io;
+ util::DummyClientFace face(io, {true, true});
+ KeyChain keyChain;
+ security::SigningInfo signingInfo;
+ Name prefix("/ndn/chunks/test");
+ time::milliseconds freshnessPeriod(time::seconds(10));
+ size_t maxSegmentSize(40);
+ std::istringstream testString(std::string(
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget "
+ "dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, "
+ "nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, "
+ "sem. Nulla consequat massa Donec pede justo,"));
+
+ Name keyLocatorName = keyChain.getDefaultCertificateName().getPrefix(-1);
+
+ Producer producer(prefix, face, keyChain, signingInfo, freshnessPeriod, maxSegmentSize,
+ false, false, testString);
+ io.poll();
+
+ size_t nSegments = std::ceil(static_cast<double>(testString.str().size()) / maxSegmentSize);
+
+ // version request
+ face.receive(*makeInterest(prefix));
+ face.processEvents();
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+ auto lastData = face.sentData.back();
+ BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 2);
+ BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), 0);
+ BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+ BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+ BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+
+ // segment request
+ Name nameWithVersion(prefix);
+ nameWithVersion.append(lastData.getName()[-2]);
+ face.receive(*makeInterest(nameWithVersion.appendSegment(nSegments)));
+ face.processEvents();
+
+ // no new data
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestProducer
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/README.md b/tools/chunks/README.md
new file mode 100644
index 0000000..3f0ffd6
--- /dev/null
+++ b/tools/chunks/README.md
@@ -0,0 +1,73 @@
+# ndncatchunks and ndnputchunks
+
+**ndncatchunks** and **ndnputchunks** are a pair of programs to transfer a file as Data segments.
+
+* **ndnputchunks** is a producer program that reads a file from the standard input, and makes
+ it available as NDN Data segments. It appends version and segment number components
+ to the specified name, according to the
+ [NDN naming conventions](http://named-data.net/publications/techreports/ndn-tr-22-ndn-memo-naming-conventions/).
+
+* **ndncatchunks** is a consumer program that fetches Data segments of a file, optionally
+ discovering the latest version of the file, and writes the content of the retrieved file to
+ the standard output.
+
+## Version discovery methods
+
+* `fixed` : ndncatchunks will send an interest attempting to find a data packet with the
+ specified prefix and version number. A version component must be present at the
+ end of the user-specified NDN name.
+
+* `iterative`: ndncatchunks will send a series of interests with ChildSelector set to prefer the
+ rightmost child and Exclude selectors, attempting to find a data packet with the
+ specified prefix and the latest (the largest in the NDN canonical ordering)
+ version number. The version is declared "latest" after a predefined number of
+ data retrieval timeouts (default: 1).
+
+The default discovery method is `fixed`. Other methods will be implemented in future versions
+of the tool.
+
+
+## Usage examples
+
+### Publishing
+
+The following command will publish the text of the GPL-3 license under the `/localhost/demo/gpl3`
+prefix:
+
+ ndnputchunks ndn:/localhost/demo/gpl3 < /usr/share/common-licenses/GPL-3
+
+To find the published version you have to start ndnputchunks with the `-p` command line option,
+for example:
+
+ ndnputchunks -p ndn:/localhost/demo/gpl3 < /usr/share/common-licenses/GPL-3
+
+This command will print the published version to the standard output.
+
+To publish data with a specific version, you must append a version component to the end of the
+prefix. The version component must follow the aforementioned NDN naming conventions.
+For example, the following command will publish the version `%FD%00%00%01Qc%CF%17v` of the
+`/localhost/demo/gpl3` prefix:
+
+ ndnputchunks ndn:/localhost/demo/gpl3/%FD%00%00%01Qc%CF%17v < /usr/share/common-licenses/GPL-3
+
+If the version component is not valid, a new well-formed version will be generated and appended
+to the supplied NDN name.
+
+
+### Retrieval
+
+To retrieve the latest version of a published file, the following command can be used:
+
+ ndncatchunks -d iterative ndn:/localhost/demo/gpl3
+
+This command will use the iterative method to discover the latest version of the file.
+
+To fetch a specific version of a published file, you can use the `fixed` version discovery method
+(the default). In this case the version needs to be supplied as part of the name. For example,
+if the version is known to be `%FD%00%00%01Qc%CF%17v`, the following command will fetch that
+exact version of the file:
+
+ ndncatchunks ndn:/localhost/demo/gpl3/%FD%00%00%01Qc%CF%17v
+
+
+For more information, run the programs with `--help` as argument.
diff --git a/tools/chunks/catchunks/consumer.cpp b/tools/chunks/catchunks/consumer.cpp
new file mode 100644
index 0000000..8976f6a
--- /dev/null
+++ b/tools/chunks/catchunks/consumer.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "consumer.hpp"
+#include "discover-version.hpp"
+
+namespace ndn {
+namespace chunks {
+
+Consumer::Consumer(Face& face, Validator& validator, bool isVerbose, std::ostream& os)
+ : m_face(face)
+ , m_validator(validator)
+ , m_pipeline(nullptr)
+ , m_nextToPrint(0)
+ , m_outputStream(os)
+ , m_isVerbose(isVerbose)
+{
+}
+
+void Consumer::run(DiscoverVersion& discover, PipelineInterests& pipeline)
+{
+ m_pipeline = &pipeline;
+ m_nextToPrint = 0;
+
+ discover.onDiscoverySuccess.connect(bind(&Consumer::runWithData, this, _1));
+ discover.onDiscoveryFailure.connect(bind(&Consumer::onFailure, this, _1));
+
+ discover.run();
+ m_face.processEvents();
+}
+
+void Consumer::runWithData(const Data& data)
+{
+ m_validator.validate(data,
+ bind(&Consumer::onDataValidated, this, _1),
+ bind(&Consumer::onFailure, this, _2));
+
+ m_pipeline->runWithExcludedSegment(data,
+ bind(&Consumer::onData, this, _1, _2),
+ bind(&Consumer::onFailure, this, _1));
+
+}
+
+void
+Consumer::onData(const Interest& interest, const Data& data)
+{
+ m_validator.validate(data,
+ bind(&Consumer::onDataValidated, this, _1),
+ bind(&Consumer::onFailure, this, _2));
+}
+
+void
+Consumer::onDataValidated(shared_ptr<const Data> data)
+{
+ if (data->getContentType() == ndn::tlv::ContentType_Nack) {
+ if (m_isVerbose)
+ std::cerr << "Application level NACK: " << *data << std::endl;
+
+ m_pipeline->cancel();
+ throw ApplicationNackError(*data);
+ }
+
+ m_bufferedData[data->getName()[-1].toSegment()] = data;
+ writeInOrderData();
+}
+
+void
+Consumer::onFailure(const std::string& reason)
+{
+ throw std::runtime_error(reason);
+}
+
+void
+Consumer::writeInOrderData()
+{
+ for (auto it = m_bufferedData.begin();
+ it != m_bufferedData.end() && it->first == m_nextToPrint;
+ it = m_bufferedData.erase(it), ++m_nextToPrint) {
+
+ const Block& content = it->second->getContent();
+ m_outputStream.write(reinterpret_cast<const char*>(content.value()), content.value_size());
+ }
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/consumer.hpp b/tools/chunks/catchunks/consumer.hpp
new file mode 100644
index 0000000..9428a06
--- /dev/null
+++ b/tools/chunks/catchunks/consumer.hpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_CONSUMER_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_CONSUMER_HPP
+
+#include "pipeline-interests.hpp"
+#include "discover-version.hpp"
+
+#include <ndn-cxx/security/validator.hpp>
+
+namespace ndn {
+namespace chunks {
+
+/**
+ * @brief Segmented version consumer
+ *
+ * Discover the latest version of the data published under a specified prefix, and retrieve all the
+ * segments associated to that version. The segments are fetched in order and written to a
+ * user-specified stream in the same order.
+ */
+class Consumer : noncopyable
+{
+public:
+ class ApplicationNackError : public std::runtime_error
+ {
+ public:
+ explicit
+ ApplicationNackError(const Data& data)
+ : std::runtime_error("Application generated Nack: " + boost::lexical_cast<std::string>(data))
+ {
+ }
+ };
+
+ /**
+ * @brief Create the consumer
+ */
+ Consumer(Face& face, Validator& validator, bool isVerbose, std::ostream& os = std::cout);
+
+ /**
+ * @brief Run the consumer
+ */
+ void
+ run(DiscoverVersion& discover, PipelineInterests& pipeline);
+
+private:
+ void
+ runWithData(const Data& data);
+
+ void
+ onData(const Interest& interest, const Data& data);
+
+ void
+ onDataValidated(shared_ptr<const Data> data);
+
+ void
+ onFailure(const std::string& reason);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ void
+ writeInOrderData();
+
+private:
+ Face& m_face;
+ Validator& m_validator;
+ PipelineInterests* m_pipeline;
+ uint64_t m_nextToPrint;
+ std::ostream& m_outputStream;
+ bool m_isVerbose;
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ std::map<uint64_t, shared_ptr<const Data>> m_bufferedData;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_CONSUMER_HPP
diff --git a/tools/chunks/catchunks/data-fetcher.cpp b/tools/chunks/catchunks/data-fetcher.cpp
new file mode 100644
index 0000000..313334f
--- /dev/null
+++ b/tools/chunks/catchunks/data-fetcher.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ * @author Davide Pesavento
+ */
+
+#include "data-fetcher.hpp"
+
+#include <cmath>
+
+namespace ndn {
+namespace chunks {
+
+const int DataFetcher::MAX_RETRIES_INFINITE = -1;
+const time::milliseconds DataFetcher::MAX_CONGESTION_BACKOFF_TIME = time::seconds(10);
+
+shared_ptr<DataFetcher>
+DataFetcher::fetch(Face& face, const Interest& interest, int maxNackRetries, int maxTimeoutRetries,
+ DataCallback onData, FailureCallback onNack, FailureCallback onTimeout,
+ bool isVerbose)
+{
+ auto dataFetcher = shared_ptr<DataFetcher>(new DataFetcher(face,
+ maxNackRetries,
+ maxTimeoutRetries,
+ std::move(onData),
+ std::move(onNack),
+ std::move(onTimeout),
+ isVerbose));
+ dataFetcher->expressInterest(interest, dataFetcher);
+ return dataFetcher;
+}
+
+DataFetcher::DataFetcher(Face& face, int maxNackRetries, int maxTimeoutRetries,
+ DataCallback onData, FailureCallback onNack, FailureCallback onTimeout,
+ bool isVerbose)
+ : m_face(face)
+ , m_scheduler(m_face.getIoService())
+ , m_onData(std::move(onData))
+ , m_onNack(std::move(onNack))
+ , m_onTimeout(std::move(onTimeout))
+ , m_maxNackRetries(maxNackRetries)
+ , m_maxTimeoutRetries(maxTimeoutRetries)
+ , m_nNacks(0)
+ , m_nTimeouts(0)
+ , m_nCongestionRetries(0)
+ , m_isVerbose(isVerbose)
+ , m_isStopped(false)
+ , m_hasError(false)
+{
+ BOOST_ASSERT(m_onData != nullptr);
+}
+
+void
+DataFetcher::cancel()
+{
+ if (isRunning()) {
+ m_isStopped = true;
+ m_face.removePendingInterest(m_interestId);
+ m_scheduler.cancelAllEvents();
+ }
+}
+
+void
+DataFetcher::expressInterest(const Interest& interest, const shared_ptr<DataFetcher>& self)
+{
+ m_nCongestionRetries = 0;
+ m_interestId = m_face.expressInterest(interest,
+ bind(&DataFetcher::handleData, this, _1, _2, self),
+ bind(&DataFetcher::handleNack, this, _1, _2, self),
+ bind(&DataFetcher::handleTimeout, this, _1, self));
+}
+
+void
+DataFetcher::handleData(const Interest& interest, const Data& data,
+ const shared_ptr<DataFetcher>& self)
+{
+ if (!isRunning())
+ return;
+
+ m_isStopped = true;
+ m_onData(interest, data);
+}
+
+void
+DataFetcher::handleNack(const Interest& interest, const lp::Nack& nack,
+ const shared_ptr<DataFetcher>& self)
+{
+ if (!isRunning())
+ return;
+
+ if (m_maxNackRetries != MAX_RETRIES_INFINITE)
+ ++m_nNacks;
+
+ if (m_isVerbose)
+ std::cerr << "Received Nack with reason " << nack.getReason()
+ << " for Interest " << interest << std::endl;
+
+ if (m_nNacks <= m_maxNackRetries || m_maxNackRetries == MAX_RETRIES_INFINITE) {
+ Interest newInterest(interest);
+ newInterest.refreshNonce();
+
+ switch (nack.getReason()) {
+ case lp::NackReason::DUPLICATE: {
+ expressInterest(newInterest, self);
+ break;
+ }
+ case lp::NackReason::CONGESTION: {
+ time::milliseconds backoffTime(static_cast<uint64_t>(std::pow(2, m_nCongestionRetries)));
+ if (backoffTime > MAX_CONGESTION_BACKOFF_TIME)
+ backoffTime = MAX_CONGESTION_BACKOFF_TIME;
+ else
+ m_nCongestionRetries++;
+
+ m_scheduler.scheduleEvent(backoffTime, bind(&DataFetcher::expressInterest, this,
+ newInterest, self));
+ break;
+ }
+ default: {
+ m_hasError = true;
+ if (m_onNack)
+ m_onNack(interest, "Could not retrieve data for " + interest.getName().toUri() +
+ ", reason: " + boost::lexical_cast<std::string>(nack.getReason()));
+ break;
+ }
+ }
+ }
+ else {
+ m_hasError = true;
+ if (m_onNack)
+ m_onNack(interest, "Reached the maximum number of nack retries (" + to_string(m_maxNackRetries) +
+ ") while retrieving data for " + interest.getName().toUri());
+ }
+}
+
+void
+DataFetcher::handleTimeout(const Interest& interest, const shared_ptr<DataFetcher>& self)
+{
+ if (!isRunning())
+ return;
+
+ if (m_maxTimeoutRetries != MAX_RETRIES_INFINITE)
+ ++m_nTimeouts;
+
+ if (m_isVerbose)
+ std::cerr << "Timeout for Interest " << interest << std::endl;
+
+ if (m_nTimeouts <= m_maxTimeoutRetries || m_maxTimeoutRetries == MAX_RETRIES_INFINITE) {
+ Interest newInterest(interest);
+ newInterest.refreshNonce();
+ expressInterest(newInterest, self);
+ }
+ else {
+ m_hasError = true;
+ if (m_onTimeout)
+ m_onTimeout(interest, "Reached the maximum number of timeout retries (" + to_string(m_maxTimeoutRetries) +
+ ") while retrieving data for " + interest.getName().toUri());
+ }
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/data-fetcher.hpp b/tools/chunks/catchunks/data-fetcher.hpp
new file mode 100644
index 0000000..4335ab2
--- /dev/null
+++ b/tools/chunks/catchunks/data-fetcher.hpp
@@ -0,0 +1,132 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ * @author Davide Pesavento
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_DATA_FETCHER_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_DATA_FETCHER_HPP
+
+#include "core/common.hpp"
+
+namespace ndn {
+namespace chunks {
+
+/**
+ * @brief fetch data for a given interest and handle timeout or nack error with retries
+ *
+ * To instantiate a DataFetcher you need to use the static method fetch, this will also express the
+ * interest. After a timeout or nack is received, the same interest with a different nonce will be
+ * requested for a maximum number of time specified by the class user. There are separate retry
+ * counters for timeouts and nacks.
+ *
+ * A specified callback is called after the data matching the expressed interest is received. A
+ * different callback is called in case one of the retries counter reach the maximum. This callback
+ * can be different for timeout and nack. The data callback must be defined but the others callback
+ * are optional.
+ *
+ */
+class DataFetcher
+{
+public:
+ /**
+ * @brief means that there is no maximum number of retries,
+ * i.e. fetching must be retried indefinitely
+ */
+ static const int MAX_RETRIES_INFINITE;
+
+ /**
+ * @brief ceiling value for backoff time used in congestion handling
+ */
+ static const time::milliseconds MAX_CONGESTION_BACKOFF_TIME;
+
+ typedef function<void(const Interest& interest, const std::string& reason)> FailureCallback;
+
+ /**
+ * @brief instantiate a DataFetcher object and start fetching data
+ *
+ * @param onData callback for segment correctly received, must not be empty
+ */
+ static shared_ptr<DataFetcher>
+ fetch(Face& face, const Interest& interest, int maxNackRetries, int maxTimeoutRetries,
+ DataCallback onData, FailureCallback onTimeout, FailureCallback onNack,
+ bool isVerbose);
+
+ /**
+ * @brief stop data fetching without error and calling any callback
+ */
+ void
+ cancel();
+
+ bool
+ isRunning() const
+ {
+ return !m_isStopped && !m_hasError;
+ }
+
+ bool
+ hasError() const
+ {
+ return m_hasError;
+ }
+
+private:
+ DataFetcher(Face& face, int maxNackRetries, int maxTimeoutRetries,
+ DataCallback onData, FailureCallback onNack, FailureCallback onTimeout,
+ bool isVerbose);
+
+ void
+ expressInterest(const Interest& interest, const shared_ptr<DataFetcher>& self);
+
+ void
+ handleData(const Interest& interest, const Data& data, const shared_ptr<DataFetcher>& self);
+
+ void
+ handleNack(const Interest& interest, const lp::Nack& nack, const shared_ptr<DataFetcher>& self);
+
+ void
+ handleTimeout(const Interest& interest, const shared_ptr<DataFetcher>& self);
+
+private:
+ Face& m_face;
+ Scheduler m_scheduler;
+ const PendingInterestId* m_interestId;
+ DataCallback m_onData;
+ FailureCallback m_onNack;
+ FailureCallback m_onTimeout;
+
+ int m_maxNackRetries;
+ int m_maxTimeoutRetries;
+ int m_nNacks;
+ int m_nTimeouts;
+ uint32_t m_nCongestionRetries;
+
+ bool m_isVerbose;
+ bool m_isStopped;
+ bool m_hasError;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_DATA_FETCHER_HPP
diff --git a/tools/chunks/catchunks/discover-version-fixed.cpp b/tools/chunks/catchunks/discover-version-fixed.cpp
new file mode 100644
index 0000000..6b763de
--- /dev/null
+++ b/tools/chunks/catchunks/discover-version-fixed.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#include "discover-version-fixed.hpp"
+
+#include <cmath>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace chunks {
+
+DiscoverVersionFixed::DiscoverVersionFixed(const Name& prefix, Face& face, const Options& options)
+ : Options(options)
+ , DiscoverVersion(prefix, face, options)
+ , m_strayExcludes()
+{
+}
+
+void
+DiscoverVersionFixed::run()
+{
+ Interest interest(m_prefix);
+ interest.setInterestLifetime(interestLifetime);
+ interest.setMustBeFresh(mustBeFresh);
+ interest.setMaxSuffixComponents(2);
+ interest.setMinSuffixComponents(2);
+
+ expressInterest(interest, maxRetriesOnTimeoutOrNack, maxRetriesOnTimeoutOrNack);
+}
+
+void
+DiscoverVersionFixed::handleData(const Interest& interest, const Data& data)
+{
+ if (isVerbose)
+ std::cerr << "Data: " << data << std::endl;
+
+ size_t segmentIndex = interest.getName().size();
+ if (data.getName()[segmentIndex].isSegment()) {
+ if (isVerbose)
+ std::cerr << "Found data with the requested version: " << m_prefix[-1] << std::endl;
+
+ this->emitSignal(onDiscoverySuccess, data);
+ }
+ else {
+ // data isn't a valid segment, add to the exclude list
+ m_strayExcludes.excludeOne(data.getName()[segmentIndex]);
+ Interest newInterest(interest);
+ newInterest.refreshNonce();
+ newInterest.setExclude(m_strayExcludes);
+
+ expressInterest(newInterest, maxRetriesOnTimeoutOrNack, maxRetriesOnTimeoutOrNack);
+ }
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/discover-version-fixed.hpp b/tools/chunks/catchunks/discover-version-fixed.hpp
new file mode 100644
index 0000000..31c77db
--- /dev/null
+++ b/tools/chunks/catchunks/discover-version-fixed.hpp
@@ -0,0 +1,73 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_FIXED_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_FIXED_HPP
+
+#include "discover-version.hpp"
+
+namespace ndn {
+namespace chunks {
+
+/**
+ * @brief Service to retrieve a specific version segment. The version is specified in the prefix
+ *
+ * Send a request of a specific version and expect to be answered with one segment.
+ *
+ * The received name component after version can be an invalid segment number, this component will
+ * be excluded in the next interests. In the unlikely case that there are too many excluded
+ * components such that the Interest cannot fit in ndn::MAX_NDN_PACKET_SIZE, the discovery
+ * procedure will throw Face::Error.
+ *
+ * DiscoverVersionFixed's user is notified once after one segment with the user specified version
+ * is found or on failure to find any Data version.
+ */
+class DiscoverVersionFixed : public DiscoverVersion
+{
+
+public:
+ /**
+ * @brief create a DiscoverVersionSpecified service
+ */
+ DiscoverVersionFixed(const Name& prefix, Face& face, const Options& options);
+
+ /**
+ * @brief identify the latest Data version published.
+ */
+ void
+ run() NDN_CXX_DECL_FINAL;
+
+private:
+ void
+ handleData(const Interest& interest, const Data& data) NDN_CXX_DECL_FINAL;
+
+private:
+ Exclude m_strayExcludes;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_FIXED_HPP
diff --git a/tools/chunks/catchunks/discover-version-iterative.cpp b/tools/chunks/catchunks/discover-version-iterative.cpp
new file mode 100644
index 0000000..4f2f543
--- /dev/null
+++ b/tools/chunks/catchunks/discover-version-iterative.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "discover-version-iterative.hpp"
+
+namespace ndn {
+namespace chunks {
+
+DiscoverVersionIterative::DiscoverVersionIterative(const Name& prefix, Face& face,
+ const Options& options)
+ : chunks::Options(options)
+ , DiscoverVersion(prefix, face, options)
+ , Options(options)
+ , m_latestVersion(0)
+ , m_latestVersionData(nullptr)
+ , m_foundVersion(false)
+{
+}
+
+void
+DiscoverVersionIterative::run()
+{
+ m_latestVersion = 0;
+ m_foundVersion = false;
+
+ Interest interest(m_prefix);
+ interest.setInterestLifetime(interestLifetime);
+ interest.setMustBeFresh(mustBeFresh);
+ interest.setMinSuffixComponents(3);
+ interest.setMaxSuffixComponents(3);
+ interest.setChildSelector(1);
+
+ expressInterest(interest, maxRetriesOnTimeoutOrNack, maxRetriesOnTimeoutOrNack);
+}
+
+void
+DiscoverVersionIterative::handleData(const Interest& interest, const Data& data)
+{
+ size_t versionindex = m_prefix.size();
+
+ const Name& name = data.getName();
+ Exclude exclude;
+
+ if (isVerbose)
+ std::cerr << "Data: " << data << std::endl;
+
+ BOOST_ASSERT(name.size() > m_prefix.size());
+ if (name[versionindex].isVersion()) {
+ m_latestVersion = name[versionindex].toVersion();
+ m_latestVersionData = make_shared<Data>(data);
+ m_foundVersion = true;
+
+ exclude.excludeBefore(name[versionindex]);
+
+ if (isVerbose)
+ std::cerr << "Discovered version = " << m_latestVersion << std::endl;
+ }
+ else {
+ // didn't find a version number at expected index.
+ m_strayExcludes.excludeOne(name[versionindex]);
+ }
+
+ for (const auto& i : m_strayExcludes) {
+ exclude.excludeOne(i.first);
+ }
+
+ Interest newInterest(interest);
+ newInterest.refreshNonce();
+ newInterest.setExclude(exclude);
+
+ if (m_foundVersion)
+ expressInterest(newInterest, maxRetriesOnTimeoutOrNack, maxRetriesAfterVersionFound);
+ else
+ expressInterest(interest, maxRetriesOnTimeoutOrNack, maxRetriesOnTimeoutOrNack);
+}
+
+void
+DiscoverVersionIterative::handleTimeout(const Interest& interest, const std::string& reason)
+{
+ if (m_foundVersion) {
+ // a version has been found and after a timeout error this version can be used as the latest.
+ if (isVerbose)
+ std::cerr << "Found data with the latest version: " << m_latestVersion << std::endl;
+
+ // we discovered at least one version. assume what we have is the latest.
+ this->emitSignal(onDiscoverySuccess, *m_latestVersionData);
+ }
+ else {
+ DiscoverVersion::handleTimeout(interest, reason);
+ }
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/discover-version-iterative.hpp b/tools/chunks/catchunks/discover-version-iterative.hpp
new file mode 100644
index 0000000..f772c8f
--- /dev/null
+++ b/tools/chunks/catchunks/discover-version-iterative.hpp
@@ -0,0 +1,107 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_ITERATIVE_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_ITERATIVE_HPP
+
+#include "discover-version.hpp"
+
+namespace ndn {
+namespace chunks {
+
+/**
+ * @brief Options for discover version iterative DiscoverVersionIterative
+ *
+ * The canonical name to use is DiscoverVersionIterative::Options
+ */
+class DiscoverVersionIterativeOptions : public virtual Options
+{
+public:
+ explicit
+ DiscoverVersionIterativeOptions(const Options& opt = Options())
+ : Options(opt)
+ , maxRetriesAfterVersionFound(1)
+ {
+ }
+
+public:
+ int maxRetriesAfterVersionFound; // used only in timeout handling
+};
+
+/**
+ * @brief Service for discovering the latest Data version in the iterative way
+ *
+ * Identifies the latest retrievable version published under the specified namespace
+ * (as specified by the Version marker).
+ *
+ * DiscoverVersionIterative declares the largest discovered version to be the latest after some
+ * Interest timeouts (i.e. failed retrieval after exclusion and retransmission). The number of
+ * timeouts are specified by the value of maxRetriesAfterVersionFound inside the iterative options.
+ *
+ * The received name component after version can be an invalid segment number, this component will
+ * be excluded in the next interests. In the unlikely case that there are too many excluded
+ * components such that the Interest cannot fit in ndn::MAX_NDN_PACKET_SIZE, the discovery
+ * procedure will throw Face::Error.
+ *
+ * DiscoverVersionIterative's user is notified once after identifying the latest retrievable
+ * version or on failure to find any version Data.
+ */
+class DiscoverVersionIterative : public DiscoverVersion, protected DiscoverVersionIterativeOptions
+{
+public:
+ typedef DiscoverVersionIterativeOptions Options;
+
+public:
+ /**
+ * @brief create a DiscoverVersionIterative service
+ */
+ DiscoverVersionIterative(const Name& prefix, Face& face, const Options& options);
+
+ /**
+ * @brief identify the latest Data version published.
+ */
+ void
+ run() NDN_CXX_DECL_FINAL;
+
+private:
+ void
+ handleData(const Interest& interest, const Data& data) NDN_CXX_DECL_FINAL;
+
+ void
+ handleTimeout(const Interest& interest, const std::string& reason) NDN_CXX_DECL_FINAL;
+
+private:
+ uint64_t m_latestVersion;
+ shared_ptr<const Data> m_latestVersionData;
+ Exclude m_strayExcludes;
+ bool m_foundVersion;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_ITERATIVE_HPP
diff --git a/tools/chunks/catchunks/discover-version.cpp b/tools/chunks/catchunks/discover-version.cpp
new file mode 100644
index 0000000..1d3298e
--- /dev/null
+++ b/tools/chunks/catchunks/discover-version.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "discover-version.hpp"
+#include "data-fetcher.hpp"
+
+namespace ndn {
+namespace chunks {
+
+DiscoverVersion::DiscoverVersion(const Name& prefix, Face& face, const Options& options)
+ : Options(options)
+ , m_prefix(prefix)
+ , m_face(face)
+{
+}
+
+void
+DiscoverVersion::expressInterest(const Interest& interest, int maxRetriesNack,
+ int maxRetriesTimeout)
+{
+ fetcher = DataFetcher::fetch(m_face, interest, maxRetriesNack, maxRetriesTimeout,
+ bind(&DiscoverVersion::handleData, this, _1, _2),
+ bind(&DiscoverVersion::handleNack, this, _1, _2),
+ bind(&DiscoverVersion::handleTimeout, this, _1, _2),
+ isVerbose);
+}
+
+void
+DiscoverVersion::handleData(const Interest& interest, const Data& data)
+{
+ onDiscoverySuccess(data);
+}
+
+void
+DiscoverVersion::handleNack(const Interest& interest, const std::string& reason)
+{
+ onDiscoveryFailure(reason);
+}
+
+void
+DiscoverVersion::handleTimeout(const Interest& interest, const std::string& reason)
+{
+ onDiscoveryFailure(reason);
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/discover-version.hpp b/tools/chunks/catchunks/discover-version.hpp
new file mode 100644
index 0000000..bb6a477
--- /dev/null
+++ b/tools/chunks/catchunks/discover-version.hpp
@@ -0,0 +1,95 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_HPP
+
+#include "core/common.hpp"
+#include "options.hpp"
+
+namespace ndn {
+namespace chunks {
+
+class DataFetcher;
+
+/**
+ * @brief Base class of services for discovering the latest Data version
+ *
+ * DiscoverVersion's user is notified once after identifying the latest retrievable version or
+ * on failure to find any Data version.
+ */
+class DiscoverVersion : virtual protected Options, noncopyable
+{
+public: // signals
+ /**
+ * @brief Signal emited when the first segment of a specific version is found.
+ */
+ signal::Signal<DiscoverVersion, const Data&> onDiscoverySuccess;
+
+ /**
+ * @brief Signal emitted when a failure occurs.
+ */
+ signal::Signal<DiscoverVersion, const std::string&> onDiscoveryFailure;
+
+ DECLARE_SIGNAL_EMIT(onDiscoverySuccess)
+ DECLARE_SIGNAL_EMIT(onDiscoveryFailure)
+
+public:
+ /**
+ * @brief create a DiscoverVersion service
+ */
+ DiscoverVersion(const Name& prefix, Face& face, const Options& options);
+
+ /**
+ * @brief identify the latest Data version published.
+ */
+ virtual void
+ run() = 0;
+
+protected:
+ void
+ expressInterest(const Interest& interest, int maxRetriesNack, int maxRetriesTimeout);
+
+ virtual void
+ handleData(const Interest& interest, const Data& data);
+
+ virtual void
+ handleNack(const Interest& interest, const std::string& reason);
+
+ virtual void
+ handleTimeout(const Interest& interest, const std::string& reason);
+
+protected:
+ const Name m_prefix;
+ Face& m_face;
+ shared_ptr<DataFetcher> fetcher;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_DISCOVER_VERSION_HPP
diff --git a/tools/chunks/catchunks/ndncatchunks.cpp b/tools/chunks/catchunks/ndncatchunks.cpp
new file mode 100644
index 0000000..d2b679f
--- /dev/null
+++ b/tools/chunks/catchunks/ndncatchunks.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "core/version.hpp"
+#include "options.hpp"
+#include "consumer.hpp"
+#include "discover-version-fixed.hpp"
+#include "discover-version-iterative.hpp"
+
+#include <ndn-cxx/security/validator-null.hpp>
+
+namespace ndn {
+namespace chunks {
+
+static int
+main(int argc, char** argv)
+{
+ std::string programName(argv[0]);
+ Options options;
+ std::string discoverType("fixed");
+ size_t maxPipelineSize(1);
+ int maxRetriesAfterVersionFound(1);
+ std::string uri;
+
+ namespace po = boost::program_options;
+ po::options_description visibleDesc("Options");
+ visibleDesc.add_options()
+ ("help,h", "print this help message and exit")
+ ("discover-version,d", po::value<std::string>(&discoverType)->default_value(discoverType),
+ "version discovery algorithm to use; valid values are: 'fixed', 'iterative'")
+ ("fresh,f", po::bool_switch(&options.mustBeFresh), "only return fresh content")
+ ("lifetime,l", po::value<uint64_t>()->default_value(options.interestLifetime.count()),
+ "lifetime of expressed Interests, in milliseconds")
+ ("pipeline,p", po::value<size_t>(&maxPipelineSize)->default_value(maxPipelineSize),
+ "maximum size of the Interest pipeline")
+ ("retries,r", po::value<int>(&options.maxRetriesOnTimeoutOrNack)->default_value(options.maxRetriesOnTimeoutOrNack),
+ "maximum number of retries in case of Nack or timeout (-1 = no limit)")
+ ("retries-iterative,i", po::value<int>(&maxRetriesAfterVersionFound)->default_value(maxRetriesAfterVersionFound),
+ "number of timeouts that have to occur in order to confirm a discovered Data "
+ "version as the latest one (only applicable to 'iterative' version discovery)")
+ ("verbose,v", po::bool_switch(&options.isVerbose), "turn on verbose output")
+ ("version,V", "print program version and exit")
+ ;
+
+ po::options_description hiddenDesc("Hidden options");
+ hiddenDesc.add_options()
+ ("ndn-name,n", po::value<std::string>(&uri), "NDN name of the requested content");
+
+ po::positional_options_description p;
+ p.add("ndn-name", -1);
+
+ po::options_description optDesc("Allowed options");
+ optDesc.add(visibleDesc).add(hiddenDesc);
+
+ po::variables_map vm;
+ try {
+ po::store(po::command_line_parser(argc, argv).options(optDesc).positional(p).run(), vm);
+ po::notify(vm);
+ }
+ catch (const po::error& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+ catch (const boost::bad_any_cast& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+
+ if (vm.count("help") > 0) {
+ std::cout << "Usage: " << programName << " [options] ndn:/name" << std::endl;
+ std::cout << visibleDesc;
+ return 0;
+ }
+
+ if (vm.count("version") > 0) {
+ std::cout << "ndncatchunks " << tools::VERSION << std::endl;
+ return 0;
+ }
+
+ if (vm.count("ndn-name") == 0) {
+ std::cerr << "Usage: " << programName << " [options] ndn:/name" << std::endl;
+ std::cerr << visibleDesc;
+ return 2;
+ }
+
+ Name prefix(uri);
+ if (discoverType == "fixed" && (prefix.empty() || !prefix[-1].isVersion())) {
+ std::cerr << "ERROR: The specified name must contain a version component when using "
+ "fixed version discovery" << std::endl;
+ return 2;
+ }
+
+ if (maxPipelineSize < 1 || maxPipelineSize > 1024) {
+ std::cerr << "ERROR: pipeline size must be between 1 and 1024" << std::endl;
+ return 2;
+ }
+
+ if (options.maxRetriesOnTimeoutOrNack < -1 || options.maxRetriesOnTimeoutOrNack > 1024) {
+ std::cerr << "ERROR: retries value must be between -1 and 1024" << std::endl;
+ return 2;
+ }
+
+ if (maxRetriesAfterVersionFound < 0 || maxRetriesAfterVersionFound > 1024) {
+ std::cerr << "ERROR: retries iterative value must be between 0 and 1024" << std::endl;
+ return 2;
+ }
+
+ options.interestLifetime = time::milliseconds(vm["lifetime"].as<uint64_t>());
+
+ try {
+ Face face;
+
+ unique_ptr<DiscoverVersion> discover;
+ if (discoverType == "fixed") {
+ discover = make_unique<DiscoverVersionFixed>(prefix, face, options);
+ }
+ else if (discoverType == "iterative") {
+ DiscoverVersionIterative::Options optionsIterative(options);
+ optionsIterative.maxRetriesAfterVersionFound = maxRetriesAfterVersionFound;
+ discover = make_unique<DiscoverVersionIterative>(prefix, face, optionsIterative);
+ }
+ else {
+ std::cerr << "ERROR: discover version type not valid" << std::endl;
+ return 2;
+ }
+
+ ValidatorNull validator;
+ Consumer consumer(face, validator, options.isVerbose);
+
+ PipelineInterests::Options optionsPipeline(options);
+ optionsPipeline.maxPipelineSize = maxPipelineSize;
+ PipelineInterests pipeline(face, optionsPipeline);
+
+ BOOST_ASSERT(discover != nullptr);
+ consumer.run(*discover, pipeline);
+ }
+ catch (const Consumer::ApplicationNackError& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 3;
+ }
+ catch (const std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+} // namespace chunks
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+ return ndn::chunks::main(argc, argv);
+}
diff --git a/tools/chunks/catchunks/options.cpp b/tools/chunks/catchunks/options.cpp
new file mode 100644
index 0000000..4f5f586
--- /dev/null
+++ b/tools/chunks/catchunks/options.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ * @author Davide Pesavento
+ */
+
+#include "options.hpp"
+
+#include <ndn-cxx/interest.hpp>
+
+namespace ndn {
+namespace chunks {
+
+Options::Options()
+ : interestLifetime(ndn::DEFAULT_INTEREST_LIFETIME)
+ , maxRetriesOnTimeoutOrNack(3)
+ , mustBeFresh(false)
+ , isVerbose(false)
+{
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/options.hpp b/tools/chunks/catchunks/options.hpp
new file mode 100644
index 0000000..09a914b
--- /dev/null
+++ b/tools/chunks/catchunks/options.hpp
@@ -0,0 +1,50 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Andrea Tosatto
+ * @author Davide Pesavento
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_OPTIONS_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_OPTIONS_HPP
+
+#include <ndn-cxx/util/time.hpp>
+
+namespace ndn {
+namespace chunks {
+
+class Options
+{
+public:
+ Options();
+
+public:
+ time::milliseconds interestLifetime;
+ int maxRetriesOnTimeoutOrNack;
+ bool mustBeFresh;
+ bool isVerbose;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_OPTIONS_HPP
diff --git a/tools/chunks/catchunks/pipeline-interests.cpp b/tools/chunks/catchunks/pipeline-interests.cpp
new file mode 100644
index 0000000..b07c959
--- /dev/null
+++ b/tools/chunks/catchunks/pipeline-interests.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "pipeline-interests.hpp"
+#include "data-fetcher.hpp"
+
+namespace ndn {
+namespace chunks {
+
+PipelineInterests::PipelineInterests(Face& face, const Options& options)
+ : m_face(face)
+ , m_nextSegmentNo(0)
+ , m_lastSegmentNo(0)
+ , m_excludeSegmentNo(0)
+ , m_options(options)
+ , m_hasFinalBlockId(false)
+ , m_hasError(false)
+ , m_hasFailure(false)
+{
+ m_segmentFetchers.resize(m_options.maxPipelineSize);
+}
+
+PipelineInterests::~PipelineInterests()
+{
+ cancel();
+}
+
+void
+PipelineInterests::runWithExcludedSegment(const Data& data, DataCallback onData,
+ FailureCallback onFailure)
+{
+ BOOST_ASSERT(onData != nullptr);
+ m_onData = std::move(onData);
+ m_onFailure = std::move(onFailure);
+
+ Name dataName = data.getName();
+ m_prefix = dataName.getPrefix(-1);
+ m_excludeSegmentNo = dataName[-1].toSegment();
+
+ if (!data.getFinalBlockId().empty()) {
+ m_hasFinalBlockId = true;
+ m_lastSegmentNo = data.getFinalBlockId().toSegment();
+ }
+
+ // if the FinalBlockId is unknown, this could potentially request non-existent segments
+ for (size_t nRequestedSegments = 0; nRequestedSegments < m_options.maxPipelineSize;
+ nRequestedSegments++) {
+ if (!fetchNextSegment(nRequestedSegments)) // all segments have been requested
+ break;
+ }
+}
+
+bool
+PipelineInterests::fetchNextSegment(std::size_t pipeNo)
+{
+ if (m_hasFailure) {
+ fail("Fetching terminated but no final segment number has been found");
+ return false;
+ }
+
+ if (m_nextSegmentNo == m_excludeSegmentNo)
+ m_nextSegmentNo++;
+
+ if (m_hasFinalBlockId && m_nextSegmentNo > m_lastSegmentNo)
+ return false;
+
+ // Send interest for next segment
+ if (m_options.isVerbose)
+ std::cerr << "Requesting segment #" << m_nextSegmentNo << std::endl;
+
+ Interest interest(Name(m_prefix).appendSegment(m_nextSegmentNo));
+ interest.setInterestLifetime(m_options.interestLifetime);
+ interest.setMustBeFresh(m_options.mustBeFresh);
+ interest.setMaxSuffixComponents(1);
+
+ BOOST_ASSERT(!m_segmentFetchers[pipeNo].first || !m_segmentFetchers[pipeNo].first->isRunning());
+
+ auto fetcher = DataFetcher::fetch(m_face, interest,
+ m_options.maxRetriesOnTimeoutOrNack,
+ m_options.maxRetriesOnTimeoutOrNack,
+ bind(&PipelineInterests::handleData, this, _1, _2, pipeNo),
+ bind(&PipelineInterests::handleFail, this, _2, pipeNo),
+ bind(&PipelineInterests::handleFail, this, _2, pipeNo),
+ m_options.isVerbose);
+
+ m_segmentFetchers[pipeNo] = make_pair(fetcher, m_nextSegmentNo);
+
+ m_nextSegmentNo++;
+ return true;
+}
+
+void
+PipelineInterests::cancel()
+{
+ for (auto& fetcher : m_segmentFetchers)
+ if (fetcher.first)
+ fetcher.first->cancel();
+
+ m_segmentFetchers.clear();
+}
+
+void
+PipelineInterests::fail(const std::string& reason)
+{
+ if (!m_hasError) {
+ cancel();
+ m_hasError = true;
+ m_hasFailure = true;
+ if (m_onFailure)
+ m_face.getIoService().post([this, reason] { m_onFailure(reason); });
+ }
+}
+
+void
+PipelineInterests::handleData(const Interest& interest, const Data& data, size_t pipeNo)
+{
+ if (m_hasError)
+ return;
+
+ BOOST_ASSERT(data.getName().equals(interest.getName()));
+
+ if (m_options.isVerbose)
+ std::cerr << "Received segment #" << data.getName()[-1].toSegment() << std::endl;
+
+ m_onData(interest, data);
+
+ if (!m_hasFinalBlockId && !data.getFinalBlockId().empty()) {
+ m_lastSegmentNo = data.getFinalBlockId().toSegment();
+ m_hasFinalBlockId = true;
+
+ for (auto& fetcher : m_segmentFetchers) {
+ if (fetcher.first && fetcher.second > m_lastSegmentNo) {
+ // Stop trying to fetch segments that are not part of the content
+ fetcher.first->cancel();
+ }
+ else if (fetcher.first && fetcher.first->hasError()) { // fetcher.second <= m_lastSegmentNo
+ // there was an error while fetching a segment that is part of the content
+ fail("Failure retriving segment #" + to_string(fetcher.second));
+ return;
+ }
+ }
+ }
+
+ fetchNextSegment(pipeNo);
+}
+
+void PipelineInterests::handleFail(const std::string& reason, std::size_t pipeNo)
+{
+ if (m_hasError)
+ return;
+
+ if (m_hasFinalBlockId && m_segmentFetchers[pipeNo].second <= m_lastSegmentNo) {
+ fail(reason);
+ }
+ else if (!m_hasFinalBlockId) {
+ // don't fetch the following segments
+ bool areAllFetchersStopped = true;
+ for (auto& fetcher : m_segmentFetchers) {
+ if (fetcher.first && fetcher.second > m_segmentFetchers[pipeNo].second) {
+ fetcher.first->cancel();
+ }
+ else if (fetcher.first && fetcher.first->isRunning()) {
+ // fetcher.second <= m_segmentFetchers[pipeNo].second
+ areAllFetchersStopped = false;
+ }
+ }
+ if (areAllFetchersStopped) {
+ if (m_onFailure)
+ fail("Fetching terminated but no final segment number has been found");
+ }
+ else {
+ m_hasFailure = true;
+ }
+ }
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/catchunks/pipeline-interests.hpp b/tools/chunks/catchunks/pipeline-interests.hpp
new file mode 100644
index 0000000..32d5b79
--- /dev/null
+++ b/tools/chunks/catchunks/pipeline-interests.hpp
@@ -0,0 +1,143 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_HPP
+#define NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_HPP
+
+#include "core/common.hpp"
+#include "options.hpp"
+
+namespace ndn {
+namespace chunks {
+
+class DataFetcher;
+
+class PipelineInterestsOptions : public Options
+{
+public:
+ explicit
+ PipelineInterestsOptions(const Options& options = Options())
+ : Options(options)
+ , maxPipelineSize(1)
+ {
+ }
+
+public:
+ size_t maxPipelineSize;
+};
+
+/**
+ * @brief Service for retrieving Data via an Interest pipeline
+ *
+ * Retrieves all segmented Data under the specified prefix by maintaining a pipeline of N Interests
+ * in flight.
+ *
+ * Provides retrieved Data on arrival with no ordering guarantees. Data is delivered to the
+ * PipelineInterests' user via callback immediately upon arrival.
+ */
+class PipelineInterests
+{
+public:
+ typedef PipelineInterestsOptions Options;
+ typedef function<void(const std::string& reason)> FailureCallback;
+
+public:
+ /**
+ * @brief create a PipelineInterests service
+ *
+ * Configures the pipelining service without specifying the retrieval namespace. After this
+ * configuration the method runWithExcludedSegment must be called to run the Pipeline.
+ */
+ explicit
+ PipelineInterests(Face& face, const Options& options = Options());
+
+ ~PipelineInterests();
+
+ /**
+ * @brief fetch all the segments between 0 and lastSegment of the specified prefix
+ *
+ * Starts the pipeline of size defined inside the options. The pipeline retrieves all the segments
+ * until the last segment is received, @p data is excluded from the retrieving.
+ *
+ * @param data a segment of the segmented Data to retrive; data.getName() must end with a segment
+ * number
+ * @param onData callback for every segment correctly received, must not be empty
+ * @param onfailure callback called if an error occurs
+ */
+ void
+ runWithExcludedSegment(const Data& data, DataCallback onData, FailureCallback onFailure);
+
+ /**
+ * @brief stop all fetch operations
+ */
+ void
+ cancel();
+
+private:
+ /**
+ * @brief fetch the next segment that has not been requested yet
+ *
+ * @return false if there is an error or all the segments have been fetched, true otherwise
+ */
+ bool
+ fetchNextSegment(size_t pipeNo);
+
+ void
+ fail(const std::string& reason);
+
+ void
+ handleData(const Interest& interest, const Data& data, size_t pipeNo);
+
+ void
+ handleFail(const std::string& reason, size_t pipeNo);
+
+private:
+ Name m_prefix;
+ Face& m_face;
+ uint64_t m_nextSegmentNo;
+ uint64_t m_lastSegmentNo;
+ uint64_t m_excludeSegmentNo;
+ DataCallback m_onData;
+ FailureCallback m_onFailure;
+ const Options m_options;
+ std::vector<std::pair<shared_ptr<DataFetcher>, uint64_t>> m_segmentFetchers;
+ bool m_hasFinalBlockId;
+ /**
+ * true if there's a critical error
+ */
+ bool m_hasError;
+ /**
+ * true if one or more segmentFetcher failed, if lastSegmentNo is not set this is usually not a
+ * fatal error for the pipeline
+ */
+ bool m_hasFailure;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_CATCHUNKS_PIPELINE_INTERESTS_HPP
diff --git a/tools/chunks/putchunks/ndnputchunks.cpp b/tools/chunks/putchunks/ndnputchunks.cpp
new file mode 100644
index 0000000..202cd83
--- /dev/null
+++ b/tools/chunks/putchunks/ndnputchunks.cpp
@@ -0,0 +1,137 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "core/version.hpp"
+#include "producer.hpp"
+
+namespace ndn {
+namespace chunks {
+
+static int
+main(int argc, char** argv)
+{
+ std::string programName = argv[0];
+ uint64_t freshnessPeriod = 10000;
+ bool printVersion = false;
+ size_t maxChunkSize = MAX_NDN_PACKET_SIZE >> 1;
+ std::string signingStr;
+ bool isVerbose = false;
+ std::string prefix;
+
+ namespace po = boost::program_options;
+ po::options_description visibleDesc("Options");
+ visibleDesc.add_options()
+ ("help,h", "print this help message and exit")
+ ("freshness,f", po::value<uint64_t>(&freshnessPeriod)->default_value(freshnessPeriod),
+ "specify FreshnessPeriod, in milliseconds")
+ ("print-data-version,p", po::bool_switch(&printVersion), "print Data version to the standard output")
+ ("size,s", po::value<size_t>(&maxChunkSize)->default_value(maxChunkSize),
+ "maximum chunk size, in bytes")
+ ("signing-info,S", po::value<std::string>(&signingStr)->default_value(signingStr),
+ "set signing information")
+ ("verbose,v", po::bool_switch(&isVerbose), "turn on verbose output")
+ ("version,V", "print program version and exit")
+ ;
+
+ po::options_description hiddenDesc("Hidden options");
+ hiddenDesc.add_options()
+ ("ndn-name,n", po::value<std::string>(&prefix), "NDN name for the served content");
+
+ po::positional_options_description p;
+ p.add("ndn-name", -1);
+
+ po::options_description optDesc("Allowed options");
+ optDesc.add(visibleDesc).add(hiddenDesc);
+
+ po::variables_map vm;
+ try {
+ po::store(po::command_line_parser(argc, argv).options(optDesc).positional(p).run(), vm);
+ po::notify(vm);
+ }
+ catch (const po::error& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+ catch (const boost::bad_any_cast& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+
+ if (vm.count("help") > 0) {
+ std::cout << "Usage: " << programName << " [options] ndn:/name" << std::endl;
+ std::cout << visibleDesc;
+ return 0;
+ }
+
+ if (vm.count("version") > 0) {
+ std::cout << "ndnputchunks " << tools::VERSION << std::endl;
+ return 0;
+ }
+
+ if (prefix.empty()) {
+ std::cerr << "Usage: " << programName << " [options] ndn:/name" << std::endl;
+ std::cerr << visibleDesc;
+ return 2;
+ }
+
+ if (maxChunkSize < 1 || maxChunkSize > MAX_NDN_PACKET_SIZE) {
+ std::cerr << "ERROR: Maximum chunk size must be between 1 and " << MAX_NDN_PACKET_SIZE << std::endl;
+ return 2;
+ }
+
+ security::SigningInfo signingInfo;
+ try {
+ signingInfo = security::SigningInfo(signingStr);
+ }
+ catch (const std::invalid_argument& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 2;
+ }
+
+ try {
+ Face face;
+ KeyChain keyChain;
+ Producer producer(prefix, face, keyChain, signingInfo, time::milliseconds(freshnessPeriod),
+ maxChunkSize, isVerbose, printVersion, std::cin);
+ producer.run();
+ }
+ catch (const std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+
+} // namespace chunks
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+ return ndn::chunks::main(argc, argv);
+}
diff --git a/tools/chunks/putchunks/producer.cpp b/tools/chunks/putchunks/producer.cpp
new file mode 100644
index 0000000..141be5b
--- /dev/null
+++ b/tools/chunks/putchunks/producer.cpp
@@ -0,0 +1,157 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#include "producer.hpp"
+
+namespace ndn {
+namespace chunks {
+
+Producer::Producer(const Name& prefix,
+ Face& face,
+ KeyChain& keyChain,
+ const security::SigningInfo& signingInfo,
+ time::milliseconds freshnessPeriod,
+ size_t maxSegmentSize,
+ bool isVerbose,
+ bool needToPrintVersion,
+ std::istream& is)
+ : m_face(face)
+ , m_keyChain(keyChain)
+ , m_signingInfo(signingInfo)
+ , m_freshnessPeriod(freshnessPeriod)
+ , m_maxSegmentSize(maxSegmentSize)
+ , m_isVerbose(isVerbose)
+{
+ if (prefix.size() > 0 && prefix[-1].isVersion()) {
+ m_prefix = prefix.getPrefix(-1);
+ m_versionedPrefix = prefix;
+ }
+ else {
+ m_prefix = prefix;
+ m_versionedPrefix = Name(m_prefix).appendVersion();
+ }
+
+ populateStore(is);
+
+ if (needToPrintVersion)
+ std::cout << m_versionedPrefix[-1] << std::endl;
+
+ m_face.setInterestFilter(m_prefix,
+ bind(&Producer::onInterest, this, _2),
+ RegisterPrefixSuccessCallback(),
+ bind(&Producer::onRegisterFailed, this, _1, _2));
+
+ if (m_isVerbose)
+ std::cerr << "Data published with name: " << m_versionedPrefix << std::endl;
+}
+
+void
+Producer::run()
+{
+ m_face.processEvents();
+}
+
+void
+Producer::onInterest(const Interest& interest)
+{
+ BOOST_ASSERT(m_store.size() > 0);
+
+ if (m_isVerbose)
+ std::cerr << "Interest: " << interest << std::endl;
+
+ const Name& name = interest.getName();
+ shared_ptr<Data> data;
+
+ // is this a discovery Interest or a sequence retrieval?
+ if (name.size() == m_versionedPrefix.size() + 1 && m_versionedPrefix.isPrefixOf(name) &&
+ name[-1].isSegment()) {
+ const auto segmentNo = static_cast<size_t>(interest.getName()[-1].toSegment());
+ // specific segment retrieval
+ if (segmentNo < m_store.size()) {
+ data = m_store[segmentNo];
+ }
+ }
+ else if (interest.matchesData(*m_store[0])) {
+ // Interest has version and is looking for the first segment or has no version
+ data = m_store[0];
+ }
+
+ if (data != nullptr) {
+ if (m_isVerbose)
+ std::cerr << "Data: " << *data << std::endl;
+
+ m_face.put(*data);
+ }
+}
+
+void
+Producer::populateStore(std::istream& is)
+{
+ BOOST_ASSERT(m_store.size() == 0);
+
+ if (m_isVerbose)
+ std::cerr << "Loading input ..." << std::endl;
+
+ std::vector<uint8_t> buffer(m_maxSegmentSize);
+ while (is.good()) {
+ is.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
+ const auto nCharsRead = is.gcount();
+ if (nCharsRead > 0) {
+ auto data = make_shared<Data>(Name(m_versionedPrefix).appendSegment(m_store.size()));
+ data->setFreshnessPeriod(m_freshnessPeriod);
+ data->setContent(&buffer[0], nCharsRead);
+
+ m_store.push_back(data);
+ }
+ }
+
+ if (m_store.empty()) {
+ auto data = make_shared<Data>(Name(m_versionedPrefix).appendSegment(0));
+ data->setFreshnessPeriod(m_freshnessPeriod);
+ m_store.push_back(data);
+ }
+
+ auto finalBlockId = name::Component::fromSegment(m_store.size() - 1);
+ for (const auto& data : m_store) {
+ data->setFinalBlockId(finalBlockId);
+ m_keyChain.sign(*data, m_signingInfo);
+ }
+
+ if (m_isVerbose)
+ std::cerr << "Created " << m_store.size() << " chunks for prefix " << m_prefix << std::endl;
+}
+
+void
+Producer::onRegisterFailed(const Name& prefix, const std::string& reason)
+{
+ std::cerr << "ERROR: Failed to register prefix '"
+ << prefix << "' (" << reason << ")" << std::endl;
+ m_face.shutdown();
+}
+
+} // namespace chunks
+} // namespace ndn
diff --git a/tools/chunks/putchunks/producer.hpp b/tools/chunks/putchunks/producer.hpp
new file mode 100644
index 0000000..061a2bf
--- /dev/null
+++ b/tools/chunks/putchunks/producer.hpp
@@ -0,0 +1,100 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016, Regents of the University of California,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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.
+ *
+ * @author Wentao Shang
+ * @author Steve DiBenedetto
+ * @author Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_CHUNKS_PUTCHUNKS_PRODUCER_HPP
+#define NDN_TOOLS_CHUNKS_PUTCHUNKS_PRODUCER_HPP
+
+#include "core/common.hpp"
+
+namespace ndn {
+namespace chunks {
+
+/**
+ * @brief Segmented version Producer
+ *
+ * Packetizes and publishes data from an input stream under /prefix/<version>/<segment number>.
+ * The current time is used as the version number. The store has always at least one element (also
+ * with empty input stream).
+ */
+class Producer : noncopyable
+{
+public:
+ /**
+ * @brief Create the Producer
+ *
+ * @prefix prefix used to publish data, if the last component of prefix is not a version number
+ * the current time is used as version number.
+ */
+ Producer(const Name& prefix, Face& face, KeyChain& keyChain,
+ const security::SigningInfo& signingInfo, time::milliseconds freshnessPeriod,
+ size_t maxSegmentSize, bool isVerbose = false, bool needToPrintVersion = false,
+ std::istream& is = std::cin);
+
+ /**
+ * @brief Run the Producer
+ */
+ void
+ run();
+
+private:
+ void
+ onInterest(const Interest& interest);
+
+ /**
+ * @brief Split the input stream in data packets and save them to the store
+ *
+ * Create data packets reading all the characters from the input stream until EOF, or an
+ * error occurs. Each data packet has a maximum payload size of m_maxSegmentSize value and is
+ * stored inside the vector m_store. An empty data packet is created and stored if the input
+ * stream is empty.
+ *
+ * @return Number of data packets contained in the store after the operation
+ */
+ void
+ populateStore(std::istream& is);
+
+ void
+ onRegisterFailed(const Name& prefix, const std::string& reason);
+
+PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+ std::vector<shared_ptr<Data>> m_store;
+
+private:
+ Name m_prefix;
+ Name m_versionedPrefix;
+ Face& m_face;
+ KeyChain& m_keyChain;
+ security::SigningInfo m_signingInfo;
+ time::milliseconds m_freshnessPeriod;
+ size_t m_maxSegmentSize;
+ bool m_isVerbose;
+};
+
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_CHUNKS_PUTCHUNKS_PRODUCER_HPP
diff --git a/tools/chunks/wscript b/tools/chunks/wscript
new file mode 100644
index 0000000..56ff392
--- /dev/null
+++ b/tools/chunks/wscript
@@ -0,0 +1,30 @@
+# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+top = '../..'
+
+def build(bld):
+
+ bld(features='cxx',
+ name='ndncatchunks-objects',
+ source=bld.path.ant_glob('catchunks/*.cpp', excl='catchunks/ndncatchunks.cpp'),
+ use='core-objects')
+
+ bld(features='cxx cxxprogram',
+ target='../../bin/ndncatchunks',
+ source='catchunks/ndncatchunks.cpp',
+ use='ndncatchunks-objects')
+
+ bld(features='cxx',
+ name='ndnputchunks-objects',
+ source=bld.path.ant_glob('putchunks/*.cpp', excl='putchunks/ndnputchunks.cpp'),
+ use='core-objects')
+
+ bld(features='cxx cxxprogram',
+ target='../../bin/ndnputchunks',
+ source='putchunks/ndnputchunks.cpp',
+ use='ndnputchunks-objects')
+
+ ## (for unit tests)
+
+ bld(name='chunks-objects',
+ use='ndncatchunks-objects ndnputchunks-objects')
+