chunks: segmented file transfer
refs #3071
Change-Id: I88e4fc1a8e33a0d61a95e2291cccc7b998647489
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