blob: 313334fb488ae6f6e570598a556f12cb8e21e7b8 [file] [log] [blame]
Andrea Tosatto672b9a72016-01-05 16:18:20 +01001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2016, Regents of the University of California,
4 * Colorado State University,
5 * University Pierre & Marie Curie, Sorbonne University.
6 *
7 * This file is part of ndn-tools (Named Data Networking Essential Tools).
8 * See AUTHORS.md for complete list of ndn-tools authors and contributors.
9 *
10 * ndn-tools is free software: you can redistribute it and/or modify it under the terms
11 * of the GNU General Public License as published by the Free Software Foundation,
12 * either version 3 of the License, or (at your option) any later version.
13 *
14 * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
22 *
23 * @author Andrea Tosatto
24 * @author Davide Pesavento
25 */
26
27#include "data-fetcher.hpp"
28
29#include <cmath>
30
31namespace ndn {
32namespace chunks {
33
34const int DataFetcher::MAX_RETRIES_INFINITE = -1;
35const time::milliseconds DataFetcher::MAX_CONGESTION_BACKOFF_TIME = time::seconds(10);
36
37shared_ptr<DataFetcher>
38DataFetcher::fetch(Face& face, const Interest& interest, int maxNackRetries, int maxTimeoutRetries,
39 DataCallback onData, FailureCallback onNack, FailureCallback onTimeout,
40 bool isVerbose)
41{
42 auto dataFetcher = shared_ptr<DataFetcher>(new DataFetcher(face,
43 maxNackRetries,
44 maxTimeoutRetries,
45 std::move(onData),
46 std::move(onNack),
47 std::move(onTimeout),
48 isVerbose));
49 dataFetcher->expressInterest(interest, dataFetcher);
50 return dataFetcher;
51}
52
53DataFetcher::DataFetcher(Face& face, int maxNackRetries, int maxTimeoutRetries,
54 DataCallback onData, FailureCallback onNack, FailureCallback onTimeout,
55 bool isVerbose)
56 : m_face(face)
57 , m_scheduler(m_face.getIoService())
58 , m_onData(std::move(onData))
59 , m_onNack(std::move(onNack))
60 , m_onTimeout(std::move(onTimeout))
61 , m_maxNackRetries(maxNackRetries)
62 , m_maxTimeoutRetries(maxTimeoutRetries)
63 , m_nNacks(0)
64 , m_nTimeouts(0)
65 , m_nCongestionRetries(0)
66 , m_isVerbose(isVerbose)
67 , m_isStopped(false)
68 , m_hasError(false)
69{
70 BOOST_ASSERT(m_onData != nullptr);
71}
72
73void
74DataFetcher::cancel()
75{
76 if (isRunning()) {
77 m_isStopped = true;
78 m_face.removePendingInterest(m_interestId);
79 m_scheduler.cancelAllEvents();
80 }
81}
82
83void
84DataFetcher::expressInterest(const Interest& interest, const shared_ptr<DataFetcher>& self)
85{
86 m_nCongestionRetries = 0;
87 m_interestId = m_face.expressInterest(interest,
88 bind(&DataFetcher::handleData, this, _1, _2, self),
89 bind(&DataFetcher::handleNack, this, _1, _2, self),
90 bind(&DataFetcher::handleTimeout, this, _1, self));
91}
92
93void
94DataFetcher::handleData(const Interest& interest, const Data& data,
95 const shared_ptr<DataFetcher>& self)
96{
97 if (!isRunning())
98 return;
99
100 m_isStopped = true;
101 m_onData(interest, data);
102}
103
104void
105DataFetcher::handleNack(const Interest& interest, const lp::Nack& nack,
106 const shared_ptr<DataFetcher>& self)
107{
108 if (!isRunning())
109 return;
110
111 if (m_maxNackRetries != MAX_RETRIES_INFINITE)
112 ++m_nNacks;
113
114 if (m_isVerbose)
115 std::cerr << "Received Nack with reason " << nack.getReason()
116 << " for Interest " << interest << std::endl;
117
118 if (m_nNacks <= m_maxNackRetries || m_maxNackRetries == MAX_RETRIES_INFINITE) {
119 Interest newInterest(interest);
120 newInterest.refreshNonce();
121
122 switch (nack.getReason()) {
123 case lp::NackReason::DUPLICATE: {
124 expressInterest(newInterest, self);
125 break;
126 }
127 case lp::NackReason::CONGESTION: {
128 time::milliseconds backoffTime(static_cast<uint64_t>(std::pow(2, m_nCongestionRetries)));
129 if (backoffTime > MAX_CONGESTION_BACKOFF_TIME)
130 backoffTime = MAX_CONGESTION_BACKOFF_TIME;
131 else
132 m_nCongestionRetries++;
133
134 m_scheduler.scheduleEvent(backoffTime, bind(&DataFetcher::expressInterest, this,
135 newInterest, self));
136 break;
137 }
138 default: {
139 m_hasError = true;
140 if (m_onNack)
141 m_onNack(interest, "Could not retrieve data for " + interest.getName().toUri() +
142 ", reason: " + boost::lexical_cast<std::string>(nack.getReason()));
143 break;
144 }
145 }
146 }
147 else {
148 m_hasError = true;
149 if (m_onNack)
150 m_onNack(interest, "Reached the maximum number of nack retries (" + to_string(m_maxNackRetries) +
151 ") while retrieving data for " + interest.getName().toUri());
152 }
153}
154
155void
156DataFetcher::handleTimeout(const Interest& interest, const shared_ptr<DataFetcher>& self)
157{
158 if (!isRunning())
159 return;
160
161 if (m_maxTimeoutRetries != MAX_RETRIES_INFINITE)
162 ++m_nTimeouts;
163
164 if (m_isVerbose)
165 std::cerr << "Timeout for Interest " << interest << std::endl;
166
167 if (m_nTimeouts <= m_maxTimeoutRetries || m_maxTimeoutRetries == MAX_RETRIES_INFINITE) {
168 Interest newInterest(interest);
169 newInterest.refreshNonce();
170 expressInterest(newInterest, self);
171 }
172 else {
173 m_hasError = true;
174 if (m_onTimeout)
175 m_onTimeout(interest, "Reached the maximum number of timeout retries (" + to_string(m_maxTimeoutRetries) +
176 ") while retrieving data for " + interest.getName().toUri());
177 }
178}
179
180} // namespace chunks
181} // namespace ndn