blob: 889027f19158fe87cfdd3cd72d4ecb4076acb835 [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
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500169 /**
170 * @brief Stops fetching.
171 *
172 * This cancels all interests that are still pending.
173 */
174 void
175 stop();
176
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700177private:
Eric Newberrye345baa2018-05-23 18:17:07 -0700178 class PendingSegment;
179
180 SegmentFetcher(Face& face, security::v2::Validator& validator, const Options& options);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700181
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500182 static bool
183 shouldStop(const weak_ptr<SegmentFetcher>& weakSelf);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700184
185 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500186 fetchFirstSegment(const Interest& baseInterest, bool isRetransmission);
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700187
188 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500189 fetchSegmentsInWindow(const Interest& origInterest);
190
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500191 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500192 afterSegmentReceivedCb(const Interest& origInterest, const Data& data,
193 const weak_ptr<SegmentFetcher>& weakSelf);
194
195 void
196 afterValidationSuccess(const Data& data, const Interest& origInterest,
Eric Newberrye345baa2018-05-23 18:17:07 -0700197 std::map<uint64_t, PendingSegment>::iterator pendingSegmentIt,
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500198 const weak_ptr<SegmentFetcher>& weakSelf);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500199
200 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700201 afterValidationFailure(const Data& data,
202 const security::v2::ValidationError& error,
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500203 const weak_ptr<SegmentFetcher>& weakSelf);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500204
205 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500206 afterNackReceivedCb(const Interest& origInterest, const lp::Nack& nack,
207 const weak_ptr<SegmentFetcher>& weakSelf);
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500208
209 void
Eric Newberrye345baa2018-05-23 18:17:07 -0700210 afterTimeoutCb(const Interest& origInterest,
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500211 const weak_ptr<SegmentFetcher>& weakSelf);
Eric Newberrye345baa2018-05-23 18:17:07 -0700212
213 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500214 afterNackOrTimeout(const Interest& origInterest);
Eric Newberrye345baa2018-05-23 18:17:07 -0700215
216 void
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500217 finalizeFetch();
Eric Newberrye345baa2018-05-23 18:17:07 -0700218
219 void
220 windowIncrease();
221
222 void
223 windowDecrease();
224
225 void
226 signalError(uint32_t code, const std::string& msg);
227
228 void
229 updateRetransmittedSegment(uint64_t segmentNum,
230 const PendingInterestId* pendingInterest,
231 scheduler::EventId timeoutEvent);
232
233 void
234 cancelExcessInFlightSegments();
235
236 bool
237 checkAllSegmentsReceived();
238
239 time::milliseconds
240 getEstimatedRto();
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700241
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000242public:
243 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500244 * @brief Emits upon successful retrieval of the complete data.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700245 */
246 Signal<SegmentFetcher, ConstBufferPtr> onComplete;
247
248 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500249 * @brief Emits when the retrieval could not be completed due to an error.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700250 *
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500251 * Handlers are provided with an error code and a string error message.
Eric Newberrycc910cd2018-05-06 17:01:40 -0700252 */
253 Signal<SegmentFetcher, uint32_t, std::string> onError;
254
255 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500256 * @brief Emits whenever a data segment received.
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000257 */
258 Signal<SegmentFetcher, Data> afterSegmentReceived;
259
260 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500261 * @brief Emits whenever a received data segment has been successfully validated.
Muktadir Chowdhury1c109b42018-01-10 08:36:00 +0000262 */
263 Signal<SegmentFetcher, Data> afterSegmentValidated;
264
Eric Newberrye345baa2018-05-23 18:17:07 -0700265 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500266 * @brief Emits whenever an Interest for a data segment is nacked.
Eric Newberrye345baa2018-05-23 18:17:07 -0700267 */
268 Signal<SegmentFetcher> afterSegmentNacked;
269
270 /**
Ashlesh Gawande279f3662018-08-26 13:42:47 -0500271 * @brief Emits whenever an Interest for a data segment times out.
Eric Newberrye345baa2018-05-23 18:17:07 -0700272 */
273 Signal<SegmentFetcher> afterSegmentTimedOut;
274
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700275private:
Eric Newberrye345baa2018-05-23 18:17:07 -0700276 enum class SegmentState {
277 FirstInterest, ///< the first Interest for this segment has been sent
278 InRetxQueue, ///< the segment is awaiting Interest retransmission
279 Retransmitted, ///< one or more retransmitted Interests have been sent for this segment
280 };
281
282 class PendingSegment
283 {
284 public:
285 SegmentState state;
286 time::steady_clock::TimePoint sendTime;
287 const PendingInterestId* id;
288 scheduler::EventId timeoutEvent;
289 };
290
291NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
292 static constexpr double MIN_SSTHRESH = 2.0;
293
Ashlesh Gawande679dbb02018-08-21 11:43:21 -0500294 shared_ptr<SegmentFetcher> m_this;
295
Eric Newberrye345baa2018-05-23 18:17:07 -0700296 Options m_options;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700297 Face& m_face;
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500298 Scheduler m_scheduler;
Eric Newberrycc910cd2018-05-06 17:01:40 -0700299 security::v2::Validator& m_validator;
Eric Newberrye345baa2018-05-23 18:17:07 -0700300 RttEstimator m_rttEstimator;
301 time::milliseconds m_timeout;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700302
Eric Newberrye345baa2018-05-23 18:17:07 -0700303 time::steady_clock::TimePoint m_timeLastSegmentReceived;
304 std::queue<uint64_t> m_retxQueue;
305 Name m_versionedDataName;
306 uint64_t m_nextSegmentNum;
307 double m_cwnd;
308 double m_ssthresh;
309 int64_t m_nSegmentsInFlight;
310 int64_t m_nSegments;
311 uint64_t m_highInterest;
312 uint64_t m_highData;
313 uint64_t m_recPoint;
314 int64_t m_nReceived;
315 int64_t m_nBytesReceived;
316
317 std::map<uint64_t, Buffer> m_receivedSegments;
318 std::map<uint64_t, PendingSegment> m_pendingSegments;
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700319};
320
Muktadir R Chowdhuryf58f8f42015-09-02 11:56:49 -0500321} // namespace util
322} // namespace ndn
Alexander Afanasyevf3cfab52014-08-17 22:15:25 -0700323
324#endif // NDN_UTIL_SEGMENT_FETCHER_HPP