blob: eadb426397a16b16d32bb2262909374e007e3e31 [file] [log] [blame]
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyev6dfeffe2017-01-30 22:40:32 -08002/*
Davide Pesavento09904412021-03-24 16:40:53 -04003 * Copyright (c) 2013-2021 Regents of the University of California.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20 */
21
Davide Pesavento09904412021-03-24 16:40:53 -040022#ifndef NDN_CXX_UTIL_SEGMENT_FETCHER_HPP
23#define NDN_CXX_UTIL_SEGMENT_FETCHER_HPP
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070024
Davide Pesavento7e780642018-11-24 15:51:34 -050025#include "ndn-cxx/face.hpp"
Alexander Afanasyev09236c22020-06-03 13:42:38 -040026#include "ndn-cxx/security/validator.hpp"
Davide Pesavento7e780642018-11-24 15:51:34 -050027#include "ndn-cxx/util/rtt-estimator.hpp"
28#include "ndn-cxx/util/scheduler.hpp"
29#include "ndn-cxx/util/signal.hpp"
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070030
Eric Newberrye345baa2018-05-23 18:17:07 -070031#include <queue>
jrclark240ebaad2020-04-02 11:18:00 -050032#include <set>
Eric Newberrye345baa2018-05-23 18:17:07 -070033
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070034namespace ndn {
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070035namespace util {
36
37/**
Eric Newberry2b765f82018-06-25 14:51:13 -070038 * @brief Utility class to fetch the latest version of a segmented object.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070039 *
Eric Newberry2b765f82018-06-25 14:51:13 -070040 * SegmentFetcher assumes that segments in the object are named `/<prefix>/<version>/<segment>`,
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070041 * where:
Alexander Afanasyevf2a46222015-09-17 18:01:30 -070042 * - `<prefix>` is the specified prefix,
43 * - `<version>` is an unknown version that needs to be discovered, and
Ashlesh Gawande279f3662018-08-26 13:42:47 -050044 * - `<segment>` is a segment number (the number of segments in the object is unknown until a Data
45 * packet containing the `FinalBlockId` field is received).
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070046 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050047 * SegmentFetcher implements the following logic:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070048 *
Eric Newberry2b765f82018-06-25 14:51:13 -070049 * 1. Express an Interest to discover the latest version of the object:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070050 *
jrclark240ebaad2020-04-02 11:18:00 -050051 * Interest: `/<prefix>?CanBePrefix&MustBeFresh`
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070052 *
jrclark240ebaad2020-04-02 11:18:00 -050053 * 2. Infer the latest version of the object: `<version> = Data.getName().get(-2)`.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070054 *
Eric Newberry2b765f82018-06-25 14:51:13 -070055 * 3. Keep sending Interests for future segments until an error occurs or the number of segments
56 * indicated by the FinalBlockId in a received Data packet is reached. This retrieval will start
57 * at segment 1 if segment 0 was received in response to the Interest expressed in step 2;
58 * otherwise, retrieval will start at segment 0. By default, congestion control will be used to
59 * manage the Interest window size. Interests expressed in this step will follow this Name
jrclark240ebaad2020-04-02 11:18:00 -050060 * format: `/<prefix>/<version>/<segment=(N)>`.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070061 *
jrclark240ebaad2020-04-02 11:18:00 -050062 * 4. If set to 'block' mode, signal #onComplete passing a memory buffer that combines the content
63 * of all segments in the object. If set to 'in order' mode, signal #onInOrderData is triggered
64 * upon validation of each segment in segment order, storing later segments that arrived out of
65 * order internally until all earlier segments have arrived and have been validated.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070066 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050067 * If an error occurs during the fetching process, #onError is signaled with one of the error codes
68 * from SegmentFetcher::ErrorCode.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070069 *
Eric Newberrye345baa2018-05-23 18:17:07 -070070 * A Validator instance must be specified to validate individual segments. Every time a segment has
Ashlesh Gawande279f3662018-08-26 13:42:47 -050071 * been successfully validated, #afterSegmentValidated will be signaled.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070072 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050073 * Example:
Eric Newberry2b765f82018-06-25 14:51:13 -070074 * @code
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070075 * void
Eric Newberrye345baa2018-05-23 18:17:07 -070076 * afterFetchComplete(ConstBufferPtr data)
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070077 * {
78 * ...
79 * }
80 *
81 * void
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -050082 * afterFetchError(uint32_t errorCode, const std::string& errorMsg)
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070083 * {
84 * ...
85 * }
86 *
87 * ...
Eric Newberrye345baa2018-05-23 18:17:07 -070088 * auto fetcher = SegmentFetcher::start(face, Interest("/data/prefix"), validator);
89 * fetcher->onComplete.connect(bind(&afterFetchComplete, this, _1));
90 * fetcher->onError.connect(bind(&afterFetchError, this, _1, _2));
Eric Newberry2b765f82018-06-25 14:51:13 -070091 * @endcode
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070092 */
93class SegmentFetcher : noncopyable
94{
95public:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070096 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -050097 * @brief Error codes passed to #onError.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070098 */
99 enum ErrorCode {
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500100 /// Retrieval timed out because the maximum timeout between the successful receipt of segments was exceeded
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700101 INTEREST_TIMEOUT = 1,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500102 /// One of the retrieved Data packets lacked a segment number in the last Name component (excl. implicit digest)
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700103 DATA_HAS_NO_SEGMENT = 2,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500104 /// One of the retrieved segments failed user-provided validation
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500105 SEGMENT_VALIDATION_FAIL = 3,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500106 /// An unrecoverable Nack was received during retrieval
Eric Newberrye345baa2018-05-23 18:17:07 -0700107 NACK_ERROR = 4,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500108 /// A received FinalBlockId did not contain a segment component
109 FINALBLOCKID_NOT_SEGMENT = 5,
Eric Newberrye345baa2018-05-23 18:17:07 -0700110 };
111
112 class Options
113 {
114 public:
115 Options()
116 {
117 }
118
119 void
120 validate();
121
122 public:
Eric Newberrye345baa2018-05-23 18:17:07 -0700123 time::milliseconds interestLifetime = 4_s; ///< lifetime of sent Interests - independent of Interest timeout
jrclark240ebaad2020-04-02 11:18:00 -0500124 time::milliseconds maxTimeout = 60_s; ///< maximum allowed time between successful receipt of segments
125 bool inOrder = false; ///< true for 'in order' mode, false for 'block' mode
126 bool useConstantInterestTimeout = false; ///< if true, Interest timeout is kept at `maxTimeout`
127 bool useConstantCwnd = false; ///< if true, window size is kept at `initCwnd`
128 bool disableCwa = false; ///< disable Conservative Window Adaptation
129 bool resetCwndToInit = false; ///< reduce cwnd to initCwnd when loss event occurs
130 bool ignoreCongMarks = false; ///< disable window decrease after congestion mark received
Eric Newberrye345baa2018-05-23 18:17:07 -0700131 double initCwnd = 1.0; ///< initial congestion window size
132 double initSsthresh = std::numeric_limits<double>::max(); ///< initial slow start threshold
133 double aiStep = 1.0; ///< additive increase step (in segments)
134 double mdCoef = 0.5; ///< multiplicative decrease coefficient
Eric Newberrye345baa2018-05-23 18:17:07 -0700135 RttEstimator::Options rttOptions; ///< options for RTT estimator
jrclark240ebaad2020-04-02 11:18:00 -0500136 size_t flowControlWindow = 25000; ///< maximum number of segments stored in the reorder buffer
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700137 };
138
139 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500140 * @brief Initiates segment fetching.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700141 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500142 * Transfer completion, failure, and progress are indicated via signals.
143 *
144 * @param face Reference to the Face that should be used to fetch data.
Eric Newberrye345baa2018-05-23 18:17:07 -0700145 * @param baseInterest Interest for the initial segment of requested data.
Eric Newberry2b765f82018-06-25 14:51:13 -0700146 * This interest may include a custom InterestLifetime and parameters that
147 * will propagate to all subsequent Interests. The only exception is that the
148 * initial Interest will be forced to include the "CanBePrefix=true" and
149 * "MustBeFresh=true" parameters, which will not be included in subsequent
Eric Newberrycc910cd2018-05-06 17:01:40 -0700150 * Interests.
Eric Newberrye345baa2018-05-23 18:17:07 -0700151 * @param validator Reference to the Validator the fetcher will use to validate data.
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500152 * The caller must ensure the validator remains valid until either #onComplete
153 * or #onError has been signaled.
154 * @param options Options controlling the transfer.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700155 *
Eric Newberrye345baa2018-05-23 18:17:07 -0700156 * @return A shared_ptr to the constructed SegmentFetcher.
157 * This shared_ptr is kept internally for the lifetime of the transfer.
158 * Therefore, it does not need to be saved and is provided here so that the
159 * SegmentFetcher's signals can be connected to.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700160 */
Eric Newberrye345baa2018-05-23 18:17:07 -0700161 static shared_ptr<SegmentFetcher>
Eric Newberrycc910cd2018-05-06 17:01:40 -0700162 start(Face& face,
163 const Interest& baseInterest,
Davide Pesaventof2cae612021-03-24 18:47:05 -0400164 security::Validator& validator,
Eric Newberrye345baa2018-05-23 18:17:07 -0700165 const Options& options = Options());
Eric Newberrycc910cd2018-05-06 17:01:40 -0700166
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500167 /**
168 * @brief Stops fetching.
169 *
170 * This cancels all interests that are still pending.
171 */
172 void
173 stop();
174
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700175private:
Eric Newberrye345baa2018-05-23 18:17:07 -0700176 class PendingSegment;
177
Davide Pesaventof2cae612021-03-24 18:47:05 -0400178 SegmentFetcher(Face& face, security::Validator& validator, const Options& options);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700179
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500180 static bool
181 shouldStop(const weak_ptr<SegmentFetcher>& weakSelf);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700182
183 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500184 fetchFirstSegment(const Interest& baseInterest, bool isRetransmission);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700185
186 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500187 fetchSegmentsInWindow(const Interest& origInterest);
188
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500189 void
Junxiao Shia5f233e2019-03-18 09:39:22 -0600190 sendInterest(uint64_t segNum, const Interest& interest, bool isRetransmission);
191
192 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500193 afterSegmentReceivedCb(const Interest& origInterest, const Data& data,
194 const weak_ptr<SegmentFetcher>& weakSelf);
195
196 void
197 afterValidationSuccess(const Data& data, const Interest& origInterest,
Eric Newberrye345baa2018-05-23 18:17:07 -0700198 std::map<uint64_t, PendingSegment>::iterator pendingSegmentIt,
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500199 const weak_ptr<SegmentFetcher>& weakSelf);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500200
201 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700202 afterValidationFailure(const Data& data,
Davide Pesaventof2cae612021-03-24 18:47:05 -0400203 const security::ValidationError& error,
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500204 const weak_ptr<SegmentFetcher>& weakSelf);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500205
206 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500207 afterNackReceivedCb(const Interest& origInterest, const lp::Nack& nack,
208 const weak_ptr<SegmentFetcher>& weakSelf);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500209
210 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700211 afterTimeoutCb(const Interest& origInterest,
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500212 const weak_ptr<SegmentFetcher>& weakSelf);
Eric Newberrye345baa2018-05-23 18:17:07 -0700213
214 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500215 afterNackOrTimeout(const Interest& origInterest);
Eric Newberrye345baa2018-05-23 18:17:07 -0700216
217 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500218 finalizeFetch();
Eric Newberrye345baa2018-05-23 18:17:07 -0700219
220 void
221 windowIncrease();
222
223 void
224 windowDecrease();
225
226 void
227 signalError(uint32_t code, const std::string& msg);
228
229 void
230 updateRetransmittedSegment(uint64_t segmentNum,
Junxiao Shi4fdcb272019-02-11 15:05:46 -0700231 const PendingInterestHandle& pendingInterest,
Eric Newberrye345baa2018-05-23 18:17:07 -0700232 scheduler::EventId timeoutEvent);
233
234 void
235 cancelExcessInFlightSegments();
236
237 bool
238 checkAllSegmentsReceived();
239
240 time::milliseconds
241 getEstimatedRto();
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700242
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000243public:
244 /**
jrclark240ebaad2020-04-02 11:18:00 -0500245 * @brief Emitted upon successful retrieval of the complete object (all segments).
246 * @note Emitted only if SegmentFetcher is operating in 'block' mode.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700247 */
248 Signal<SegmentFetcher, ConstBufferPtr> onComplete;
249
250 /**
jrclark240ebaad2020-04-02 11:18:00 -0500251 * @brief Emitted when the retrieval could not be completed due to an error.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700252 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500253 * Handlers are provided with an error code and a string error message.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700254 */
255 Signal<SegmentFetcher, uint32_t, std::string> onError;
256
257 /**
jrclark240ebaad2020-04-02 11:18:00 -0500258 * @brief Emitted whenever a data segment received.
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000259 */
260 Signal<SegmentFetcher, Data> afterSegmentReceived;
261
262 /**
jrclark240ebaad2020-04-02 11:18:00 -0500263 * @brief Emitted whenever a received data segment has been successfully validated.
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000264 */
265 Signal<SegmentFetcher, Data> afterSegmentValidated;
266
Eric Newberrye345baa2018-05-23 18:17:07 -0700267 /**
jrclark240ebaad2020-04-02 11:18:00 -0500268 * @brief Emitted whenever an Interest for a data segment is nacked.
Eric Newberrye345baa2018-05-23 18:17:07 -0700269 */
270 Signal<SegmentFetcher> afterSegmentNacked;
271
272 /**
jrclark240ebaad2020-04-02 11:18:00 -0500273 * @brief Emitted whenever an Interest for a data segment times out.
Eric Newberrye345baa2018-05-23 18:17:07 -0700274 */
275 Signal<SegmentFetcher> afterSegmentTimedOut;
276
jrclark240ebaad2020-04-02 11:18:00 -0500277 /**
278 * @brief Emitted after each data segment in segment order has been validated.
279 * @note Emitted only if SegmentFetcher is operating in 'in order' mode.
280 */
281 Signal<SegmentFetcher, ConstBufferPtr> onInOrderData;
282
283 /**
284 * @brief Emitted on successful retrieval of all segments in 'in order' mode.
285 * @note Emitted only if SegmentFetcher is operating in 'in order' mode.
286 */
287 Signal<SegmentFetcher> onInOrderComplete;
288
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700289private:
Eric Newberrye345baa2018-05-23 18:17:07 -0700290 enum class SegmentState {
291 FirstInterest, ///< the first Interest for this segment has been sent
292 InRetxQueue, ///< the segment is awaiting Interest retransmission
293 Retransmitted, ///< one or more retransmitted Interests have been sent for this segment
294 };
295
296 class PendingSegment
297 {
298 public:
299 SegmentState state;
300 time::steady_clock::TimePoint sendTime;
Junxiao Shi4fdcb272019-02-11 15:05:46 -0700301 ScopedPendingInterestHandle hdl;
302 scheduler::ScopedEventId timeoutEvent;
Eric Newberrye345baa2018-05-23 18:17:07 -0700303 };
304
305NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
306 static constexpr double MIN_SSTHRESH = 2.0;
307
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500308 shared_ptr<SegmentFetcher> m_this;
309
Eric Newberrye345baa2018-05-23 18:17:07 -0700310 Options m_options;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700311 Face& m_face;
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500312 Scheduler m_scheduler;
Davide Pesaventof2cae612021-03-24 18:47:05 -0400313 security::Validator& m_validator;
Eric Newberrye345baa2018-05-23 18:17:07 -0700314 RttEstimator m_rttEstimator;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700315
Eric Newberrye345baa2018-05-23 18:17:07 -0700316 time::steady_clock::TimePoint m_timeLastSegmentReceived;
317 std::queue<uint64_t> m_retxQueue;
318 Name m_versionedDataName;
jrclark240ebaad2020-04-02 11:18:00 -0500319 uint64_t m_nextSegmentNum = 0;
Eric Newberrye345baa2018-05-23 18:17:07 -0700320 double m_cwnd;
321 double m_ssthresh;
jrclark240ebaad2020-04-02 11:18:00 -0500322 int64_t m_nSegmentsInFlight = 0;
323 int64_t m_nSegments = 0;
324 uint64_t m_highInterest = 0;
325 uint64_t m_highData = 0;
326 uint64_t m_recPoint = 0;
327 int64_t m_nReceived = 0;
328 int64_t m_nBytesReceived = 0;
329 uint64_t m_nextSegmentInOrder = 0;
Eric Newberrye345baa2018-05-23 18:17:07 -0700330
jrclark240ebaad2020-04-02 11:18:00 -0500331 std::map<uint64_t, Buffer> m_segmentBuffer;
Eric Newberrye345baa2018-05-23 18:17:07 -0700332 std::map<uint64_t, PendingSegment> m_pendingSegments;
jrclark240ebaad2020-04-02 11:18:00 -0500333 std::set<uint64_t> m_receivedSegments;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700334};
335
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500336} // namespace util
337} // namespace ndn
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700338
Davide Pesavento09904412021-03-24 16:40:53 -0400339#endif // NDN_CXX_UTIL_SEGMENT_FETCHER_HPP