blob: 9f7a46dd52b27d2d2874ac5bc293f2266e8d180d [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/*
Eric Newberrye345baa2018-05-23 18:17:07 -07003 * Copyright (c) 2013-2018, Regents of the University of California,
4 * Colorado State University,
5 * University Pierre & Marie Curie, Sorbonne University.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -07006 *
7 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
8 *
9 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU Lesser General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later version.
12 *
13 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16 *
17 * You should have received copies of the GNU General Public License and GNU Lesser
18 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
19 * <http://www.gnu.org/licenses/>.
20 *
21 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Eric Newberrye345baa2018-05-23 18:17:07 -070022 *
23 * @author Shuo Yang
24 * @author Weiwei Liu
25 * @author Chavoosh Ghasemi
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070026 */
27
28#ifndef NDN_UTIL_SEGMENT_FETCHER_HPP
29#define NDN_UTIL_SEGMENT_FETCHER_HPP
30
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070031#include "../face.hpp"
Alexander Afanasyev6dfeffe2017-01-30 22:40:32 -080032#include "../security/v2/validator.hpp"
Eric Newberrye345baa2018-05-23 18:17:07 -070033#include "rtt-estimator.hpp"
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +000034#include "scheduler.hpp"
35#include "signal.hpp"
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070036
Eric Newberrye345baa2018-05-23 18:17:07 -070037#include <queue>
38
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070039namespace ndn {
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070040namespace util {
41
42/**
Eric Newberry2b765f82018-06-25 14:51:13 -070043 * @brief Utility class to fetch the latest version of a segmented object.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070044 *
Eric Newberry2b765f82018-06-25 14:51:13 -070045 * SegmentFetcher assumes that segments in the object are named `/<prefix>/<version>/<segment>`,
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070046 * where:
Alexander Afanasyevf2a46222015-09-17 18:01:30 -070047 * - `<prefix>` is the specified prefix,
48 * - `<version>` is an unknown version that needs to be discovered, and
Ashlesh Gawande279f3662018-08-26 13:42:47 -050049 * - `<segment>` is a segment number (the number of segments in the object is unknown until a Data
50 * packet containing the `FinalBlockId` field is received).
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070051 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050052 * SegmentFetcher implements the following logic:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070053 *
Eric Newberry2b765f82018-06-25 14:51:13 -070054 * 1. Express an Interest to discover the latest version of the object:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070055 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050056 * Interest: `/<prefix>?ndn.CanBePrefix=true&ndn.MustBeFresh=true`
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070057 *
Eric Newberry2b765f82018-06-25 14:51:13 -070058 * 2. Infer the latest version of the object: `<version> = Data.getName().get(-2)`
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070059 *
Eric Newberry2b765f82018-06-25 14:51:13 -070060 * 3. Keep sending Interests for future segments until an error occurs or the number of segments
61 * indicated by the FinalBlockId in a received Data packet is reached. This retrieval will start
62 * at segment 1 if segment 0 was received in response to the Interest expressed in step 2;
63 * otherwise, retrieval will start at segment 0. By default, congestion control will be used to
64 * manage the Interest window size. Interests expressed in this step will follow this Name
65 * format:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070066 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050067 * Interest: `/<prefix>/<version>/<segment=(N)>`
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070068 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050069 * 4. Signal #onComplete passing a memory buffer that combines the content of all segments in the object.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070070 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050071 * If an error occurs during the fetching process, #onError is signaled with one of the error codes
72 * from SegmentFetcher::ErrorCode.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070073 *
Eric Newberrye345baa2018-05-23 18:17:07 -070074 * A Validator instance must be specified to validate individual segments. Every time a segment has
Ashlesh Gawande279f3662018-08-26 13:42:47 -050075 * been successfully validated, #afterSegmentValidated will be signaled.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070076 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -050077 * Example:
Eric Newberry2b765f82018-06-25 14:51:13 -070078 * @code
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070079 * void
Eric Newberrye345baa2018-05-23 18:17:07 -070080 * afterFetchComplete(ConstBufferPtr data)
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070081 * {
82 * ...
83 * }
84 *
85 * void
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -050086 * afterFetchError(uint32_t errorCode, const std::string& errorMsg)
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070087 * {
88 * ...
89 * }
90 *
91 * ...
Eric Newberrye345baa2018-05-23 18:17:07 -070092 * auto fetcher = SegmentFetcher::start(face, Interest("/data/prefix"), validator);
93 * fetcher->onComplete.connect(bind(&afterFetchComplete, this, _1));
94 * fetcher->onError.connect(bind(&afterFetchError, this, _1, _2));
Eric Newberry2b765f82018-06-25 14:51:13 -070095 * @endcode
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -070096 */
97class SegmentFetcher : noncopyable
98{
99public:
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700100 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500101 * @brief Error codes passed to #onError.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700102 */
103 enum ErrorCode {
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500104 /// Retrieval timed out because the maximum timeout between the successful receipt of segments was exceeded
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700105 INTEREST_TIMEOUT = 1,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500106 /// 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 -0700107 DATA_HAS_NO_SEGMENT = 2,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500108 /// One of the retrieved segments failed user-provided validation
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500109 SEGMENT_VALIDATION_FAIL = 3,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500110 /// An unrecoverable Nack was received during retrieval
Eric Newberrye345baa2018-05-23 18:17:07 -0700111 NACK_ERROR = 4,
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500112 /// A received FinalBlockId did not contain a segment component
113 FINALBLOCKID_NOT_SEGMENT = 5,
Eric Newberrye345baa2018-05-23 18:17:07 -0700114 };
115
116 class Options
117 {
118 public:
119 Options()
120 {
121 }
122
123 void
124 validate();
125
126 public:
127 bool useConstantCwnd = false; ///< if true, window size is kept at `initCwnd`
128 bool useConstantInterestTimeout = false; ///< if true, Interest timeout is kept at `maxTimeout`
129 time::milliseconds maxTimeout = 60_s; ///< maximum allowed time between successful receipt of segments
130 time::milliseconds interestLifetime = 4_s; ///< lifetime of sent Interests - independent of Interest timeout
131 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
135 bool disableCwa = false; ///< disable Conservative Window Adaptation
136 bool resetCwndToInit = false; ///< reduce cwnd to initCwnd when loss event occurs
137 bool ignoreCongMarks = false; ///< disable window decrease after congestion mark received
138 RttEstimator::Options rttOptions; ///< options for RTT estimator
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700139 };
140
141 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500142 * @brief Initiates segment fetching.
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700143 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500144 * Transfer completion, failure, and progress are indicated via signals.
145 *
146 * @param face Reference to the Face that should be used to fetch data.
Eric Newberrye345baa2018-05-23 18:17:07 -0700147 * @param baseInterest Interest for the initial segment of requested data.
Eric Newberry2b765f82018-06-25 14:51:13 -0700148 * This interest may include a custom InterestLifetime and parameters that
149 * will propagate to all subsequent Interests. The only exception is that the
150 * initial Interest will be forced to include the "CanBePrefix=true" and
151 * "MustBeFresh=true" parameters, which will not be included in subsequent
Eric Newberrycc910cd2018-05-06 17:01:40 -0700152 * Interests.
Eric Newberrye345baa2018-05-23 18:17:07 -0700153 * @param validator Reference to the Validator the fetcher will use to validate data.
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500154 * The caller must ensure the validator remains valid until either #onComplete
155 * or #onError has been signaled.
156 * @param options Options controlling the transfer.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700157 *
Eric Newberrye345baa2018-05-23 18:17:07 -0700158 * @return A shared_ptr to the constructed SegmentFetcher.
159 * This shared_ptr is kept internally for the lifetime of the transfer.
160 * Therefore, it does not need to be saved and is provided here so that the
161 * SegmentFetcher's signals can be connected to.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700162 */
Eric Newberrye345baa2018-05-23 18:17:07 -0700163 static shared_ptr<SegmentFetcher>
Eric Newberrycc910cd2018-05-06 17:01:40 -0700164 start(Face& face,
165 const Interest& baseInterest,
Eric Newberrye345baa2018-05-23 18:17:07 -0700166 security::v2::Validator& validator,
167 const Options& options = Options());
Eric Newberrycc910cd2018-05-06 17:01:40 -0700168
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700169private:
Eric Newberrye345baa2018-05-23 18:17:07 -0700170 class PendingSegment;
171
172 SegmentFetcher(Face& face, security::v2::Validator& validator, const Options& options);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700173
174 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700175 fetchFirstSegment(const Interest& baseInterest,
176 bool isRetransmission,
177 shared_ptr<SegmentFetcher> self);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700178
179 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700180 fetchSegmentsInWindow(const Interest& origInterest, shared_ptr<SegmentFetcher> self);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700181
182 void
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000183 afterSegmentReceivedCb(const Interest& origInterest,
Eric Newberrye345baa2018-05-23 18:17:07 -0700184 const Data& data,
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000185 shared_ptr<SegmentFetcher> self);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500186 void
Alexander Afanasyev6dfeffe2017-01-30 22:40:32 -0800187 afterValidationSuccess(const Data& data,
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500188 const Interest& origInterest,
Eric Newberrye345baa2018-05-23 18:17:07 -0700189 std::map<uint64_t, PendingSegment>::iterator pendingSegmentIt,
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500190 shared_ptr<SegmentFetcher> self);
191
192 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700193 afterValidationFailure(const Data& data,
194 const security::v2::ValidationError& error,
195 shared_ptr<SegmentFetcher> self);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500196
197 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700198 afterNackReceivedCb(const Interest& origInterest,
199 const lp::Nack& nack,
200 shared_ptr<SegmentFetcher> self);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500201
202 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700203 afterTimeoutCb(const Interest& origInterest,
204 shared_ptr<SegmentFetcher> self);
205
206 void
207 afterNackOrTimeout(const Interest& origInterest,
208 shared_ptr<SegmentFetcher> self);
209
210 void
211 finalizeFetch(shared_ptr<SegmentFetcher> self);
212
213 void
214 windowIncrease();
215
216 void
217 windowDecrease();
218
219 void
220 signalError(uint32_t code, const std::string& msg);
221
222 void
223 updateRetransmittedSegment(uint64_t segmentNum,
224 const PendingInterestId* pendingInterest,
225 scheduler::EventId timeoutEvent);
226
227 void
228 cancelExcessInFlightSegments();
229
230 bool
231 checkAllSegmentsReceived();
232
233 time::milliseconds
234 getEstimatedRto();
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700235
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000236public:
237 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500238 * @brief Emits upon successful retrieval of the complete data.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700239 */
240 Signal<SegmentFetcher, ConstBufferPtr> onComplete;
241
242 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500243 * @brief Emits when the retrieval could not be completed due to an error.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700244 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500245 * Handlers are provided with an error code and a string error message.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700246 */
247 Signal<SegmentFetcher, uint32_t, std::string> onError;
248
249 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500250 * @brief Emits whenever a data segment received.
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000251 */
252 Signal<SegmentFetcher, Data> afterSegmentReceived;
253
254 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500255 * @brief Emits whenever a received data segment has been successfully validated.
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000256 */
257 Signal<SegmentFetcher, Data> afterSegmentValidated;
258
Eric Newberrye345baa2018-05-23 18:17:07 -0700259 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500260 * @brief Emits whenever an Interest for a data segment is nacked.
Eric Newberrye345baa2018-05-23 18:17:07 -0700261 */
262 Signal<SegmentFetcher> afterSegmentNacked;
263
264 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500265 * @brief Emits whenever an Interest for a data segment times out.
Eric Newberrye345baa2018-05-23 18:17:07 -0700266 */
267 Signal<SegmentFetcher> afterSegmentTimedOut;
268
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700269private:
Eric Newberrye345baa2018-05-23 18:17:07 -0700270 enum class SegmentState {
271 FirstInterest, ///< the first Interest for this segment has been sent
272 InRetxQueue, ///< the segment is awaiting Interest retransmission
273 Retransmitted, ///< one or more retransmitted Interests have been sent for this segment
274 };
275
276 class PendingSegment
277 {
278 public:
279 SegmentState state;
280 time::steady_clock::TimePoint sendTime;
281 const PendingInterestId* id;
282 scheduler::EventId timeoutEvent;
283 };
284
285NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
286 static constexpr double MIN_SSTHRESH = 2.0;
287
288 Options m_options;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700289 Face& m_face;
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500290 Scheduler m_scheduler;
Eric Newberrycc910cd2018-05-06 17:01:40 -0700291 security::v2::Validator& m_validator;
Eric Newberrye345baa2018-05-23 18:17:07 -0700292 RttEstimator m_rttEstimator;
293 time::milliseconds m_timeout;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700294
Eric Newberrye345baa2018-05-23 18:17:07 -0700295 time::steady_clock::TimePoint m_timeLastSegmentReceived;
296 std::queue<uint64_t> m_retxQueue;
297 Name m_versionedDataName;
298 uint64_t m_nextSegmentNum;
299 double m_cwnd;
300 double m_ssthresh;
301 int64_t m_nSegmentsInFlight;
302 int64_t m_nSegments;
303 uint64_t m_highInterest;
304 uint64_t m_highData;
305 uint64_t m_recPoint;
306 int64_t m_nReceived;
307 int64_t m_nBytesReceived;
308
309 std::map<uint64_t, Buffer> m_receivedSegments;
310 std::map<uint64_t, PendingSegment> m_pendingSegments;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700311};
312
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500313} // namespace util
314} // namespace ndn
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700315
316#endif // NDN_UTIL_SEGMENT_FETCHER_HPP