Rename ndncatchunks to ndnget
Change-Id: I260e552746e900a73c2ce773bd91d9b6fa384734
diff --git a/tools/get/data-fetcher.cpp b/tools/get/data-fetcher.cpp
new file mode 100644
index 0000000..1f3506a
--- /dev/null
+++ b/tools/get/data-fetcher.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2016-2025, 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 <boost/lexical_cast.hpp>
+
+#include <cmath>
+#include <iostream>
+
+namespace ndn::get {
+
+std::shared_ptr<DataFetcher>
+DataFetcher::fetch(Face& face, const Interest& interest, int maxNackRetries, int maxTimeoutRetries,
+ DataCallback onData, FailureCallback onNack, FailureCallback onTimeout,
+ bool isVerbose)
+{
+ auto dataFetcher = std::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.getIoContext())
+ , m_onData(std::move(onData))
+ , m_onNack(std::move(onNack))
+ , m_onTimeout(std::move(onTimeout))
+ , m_maxNackRetries(maxNackRetries)
+ , m_maxTimeoutRetries(maxTimeoutRetries)
+ , m_isVerbose(isVerbose)
+{
+ BOOST_ASSERT(m_onData != nullptr);
+}
+
+void
+DataFetcher::cancel()
+{
+ if (isRunning()) {
+ m_isStopped = true;
+ m_pendingInterest.cancel();
+ m_scheduler.cancelAllEvents();
+ }
+}
+
+void
+DataFetcher::expressInterest(const Interest& interest, const std::shared_ptr<DataFetcher>& self)
+{
+ m_nCongestionRetries = 0;
+ m_pendingInterest = m_face.expressInterest(interest,
+ [this, self] (auto&&... args) { handleData(std::forward<decltype(args)>(args)..., self); },
+ [this, self] (auto&&... args) { handleNack(std::forward<decltype(args)>(args)..., self); },
+ [this, self] (auto&&... args) { handleTimeout(std::forward<decltype(args)>(args)..., self); });
+}
+
+void
+DataFetcher::handleData(const Interest& interest, const Data& data,
+ const std::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 std::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 << "\n";
+
+ 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.schedule(backoffTime, [this, newInterest, self] {
+ expressInterest(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 (" + std::to_string(m_maxNackRetries) +
+ ") while retrieving data for " + interest.getName().toUri());
+ }
+}
+
+void
+DataFetcher::handleTimeout(const Interest& interest, const std::shared_ptr<DataFetcher>& self)
+{
+ if (!isRunning())
+ return;
+
+ if (m_maxTimeoutRetries != MAX_RETRIES_INFINITE)
+ ++m_nTimeouts;
+
+ if (m_isVerbose)
+ std::cerr << "Timeout for Interest " << interest << "\n";
+
+ 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 (" + std::to_string(m_maxTimeoutRetries) +
+ ") while retrieving data for " + interest.getName().toUri());
+ }
+}
+
+} // namespace ndn::get