blob: 4844fd66f305fb625aab31948ad8b3abe87dcc6d [file] [log] [blame]
Alexander Afanasyev42290b22017-03-09 12:58:29 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyevc0e26582017-08-13 21:16:49 -04002/*
Davide Pesaventoc221e362019-01-24 22:26:54 -05003 * Copyright (c) 2014-2019, Regents of the University of California.
Wentao Shangbcbc9292014-04-28 21:17:06 -07004 *
5 * This file is part of NDN repo-ng (Next generation of NDN repository).
6 * See AUTHORS.md for complete list of repo-ng authors and contributors.
7 *
8 * repo-ng is free software: you can redistribute it and/or modify it under the terms
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * either version 3 of the License, or (at your option) any later version.
11 *
12 * repo-ng is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 */
19
Davide Pesaventoc221e362019-01-24 22:26:54 -050020#include <ndn-cxx/data.hpp>
21#include <ndn-cxx/face.hpp>
22#include <ndn-cxx/interest.hpp>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040023
Wentao Shang91fb4f22014-05-20 10:55:22 -070024#include <fstream>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040025#include <iostream>
Davide Pesavento31be3f62019-01-24 23:20:23 -050026#include <unistd.h>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040027
28#include <boost/lexical_cast.hpp>
Wentao Shangbcbc9292014-04-28 21:17:06 -070029
30namespace repo {
31
Wentao Shanga8f3c402014-10-30 14:03:27 -070032using ndn::Name;
33using ndn::Interest;
34using ndn::Data;
Wentao Shanga8f3c402014-10-30 14:03:27 -070035
Davide Pesaventoc221e362019-01-24 22:26:54 -050036class Consumer : boost::noncopyable
37{
38public:
39 Consumer(const std::string& dataName, std::ostream& os,
40 bool verbose, bool versioned, bool single,
41 int interestLifetime, int timeout,
42 bool mustBeFresh = false,
43 bool canBePrefix = false)
44 : m_dataName(dataName)
45 , m_os(os)
46 , m_verbose(verbose)
47 , m_hasVersion(versioned)
48 , m_isSingle(single)
49 , m_isFinished(false)
50 , m_isFirst(true)
51 , m_interestLifetime(interestLifetime)
52 , m_timeout(timeout)
53 , m_nextSegment(0)
54 , m_totalSize(0)
55 , m_retryCount(0)
56 , m_mustBeFresh(mustBeFresh)
57 , m_canBePrefix(canBePrefix)
58 {
59 }
Wentao Shangbcbc9292014-04-28 21:17:06 -070060
Davide Pesaventoc221e362019-01-24 22:26:54 -050061 void
62 run();
63
64private:
65 void
66 fetchData(const ndn::Name& name);
67
68 void
69 onVersionedData(const ndn::Interest& interest, const ndn::Data& data);
70
71 void
72 onUnversionedData(const ndn::Interest& interest, const ndn::Data& data);
73
74 void
75 onTimeout(const ndn::Interest& interest);
76
77 void
78 readData(const ndn::Data& data);
79
80 void
81 fetchNextData(const ndn::Name& name, const ndn::Data& data);
82
83private:
84 ndn::Face m_face;
85 ndn::Name m_dataName;
86 std::ostream& m_os;
87 bool m_verbose;
88 bool m_hasVersion;
89 bool m_isSingle;
90 bool m_isFinished;
91 bool m_isFirst;
92 ndn::time::milliseconds m_interestLifetime;
93 ndn::time::milliseconds m_timeout;
94 uint64_t m_nextSegment;
95 int m_totalSize;
96 int m_retryCount;
97 bool m_mustBeFresh;
98 bool m_canBePrefix;
99
100 static constexpr int MAX_RETRY = 3;
101};
Weiqi Shi5822e342014-08-21 20:05:30 -0700102
Wentao Shangbcbc9292014-04-28 21:17:06 -0700103void
104Consumer::fetchData(const Name& name)
105{
106 Interest interest(name);
107 interest.setInterestLifetime(m_interestLifetime);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500108 if (m_hasVersion) {
109 interest.setMustBeFresh(m_mustBeFresh);
110 }
111 else {
112 interest.setMustBeFresh(true);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500113 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700114
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800115 interest.setCanBePrefix(m_canBePrefix);
116
Wentao Shangbcbc9292014-04-28 21:17:06 -0700117 m_face.expressInterest(interest,
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800118 m_hasVersion ? std::bind(&Consumer::onVersionedData, this, _1, _2)
119 : std::bind(&Consumer::onUnversionedData, this, _1, _2),
120 std::bind(&Consumer::onTimeout, this, _1), // Nack
121 std::bind(&Consumer::onTimeout, this, _1));
Wentao Shangbcbc9292014-04-28 21:17:06 -0700122}
123
124void
125Consumer::run()
126{
127 // Send the first Interest
128 Name name(m_dataName);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700129
Weiqi Shi5822e342014-08-21 20:05:30 -0700130 m_nextSegment++;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700131 fetchData(name);
132
133 // processEvents will block until the requested data received or timeout occurs
134 m_face.processEvents(m_timeout);
135}
136
137void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800138Consumer::onVersionedData(const Interest& interest, const Data& data)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700139{
140 const Name& name = data.getName();
Wentao Shangbcbc9292014-04-28 21:17:06 -0700141
Weiqi Shi5822e342014-08-21 20:05:30 -0700142 // the received data name may have segment number or not
143 if (name.size() == m_dataName.size()) {
144 if (!m_isSingle) {
145 Name fetchName = name;
146 fetchName.appendSegment(0);
147 fetchData(fetchName);
148 }
149 }
150 else if (name.size() == m_dataName.size() + 1) {
151 if (!m_isSingle) {
152 if (m_isFirst) {
153 uint64_t segment = name[-1].toSegment();
154 if (segment != 0) {
155 fetchData(Name(m_dataName).appendSegment(0));
156 m_isFirst = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700157 return;
158 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700159 m_isFirst = false;
160 }
161 fetchNextData(name, data);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700162 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700163 else {
164 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
165 return;
166 }
167 }
168 else {
169 std::cerr << "ERROR: Name size does not match" << std::endl;
170 return;
171 }
172 readData(data);
173}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700174
Weiqi Shi5822e342014-08-21 20:05:30 -0700175void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800176Consumer::onUnversionedData(const Interest& interest, const Data& data)
Weiqi Shi5822e342014-08-21 20:05:30 -0700177{
178 const Name& name = data.getName();
Weiqi Shi5822e342014-08-21 20:05:30 -0700179 if (name.size() == m_dataName.size() + 1) {
180 if (!m_isSingle) {
181 Name fetchName = name;
182 fetchName.append(name[-1]).appendSegment(0);
183 fetchData(fetchName);
184 }
185 }
186 else if (name.size() == m_dataName.size() + 2) {
187 if (!m_isSingle) {
188 if (m_isFirst) {
189 uint64_t segment = name[-1].toSegment();
190 if (segment != 0) {
191 fetchData(Name(m_dataName).append(name[-2]).appendSegment(0));
192 m_isFirst = false;
193 return;
194 }
195 m_isFirst = false;
196 }
197 fetchNextData(name, data);
198 }
199 else {
200 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
201 return;
202 }
203 }
204 else {
205 std::cerr << "ERROR: Name size does not match" << std::endl;
206 return;
207 }
208 readData(data);
209}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700210
Weiqi Shi5822e342014-08-21 20:05:30 -0700211void
212Consumer::readData(const Data& data)
213{
Davide Pesaventoc221e362019-01-24 22:26:54 -0500214 const auto& content = data.getContent();
Wentao Shangbcbc9292014-04-28 21:17:06 -0700215 m_os.write(reinterpret_cast<const char*>(content.value()), content.value_size());
216 m_totalSize += content.value_size();
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500217 if (m_verbose) {
Weiqi Shi5822e342014-08-21 20:05:30 -0700218 std::cerr << "LOG: received data = " << data.getName() << std::endl;
219 }
220 if (m_isFinished || m_isSingle) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800221 std::cerr << "INFO: End of file is reached" << std::endl;
Weiqi Shi5822e342014-08-21 20:05:30 -0700222 std::cerr << "INFO: Total # of segments received: " << m_nextSegment << std::endl;
223 std::cerr << "INFO: Total # bytes of content received: " << m_totalSize << std::endl;
224 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700225}
226
Weiqi Shi5822e342014-08-21 20:05:30 -0700227void
228Consumer::fetchNextData(const Name& name, const Data& data)
229{
230 uint64_t segment = name[-1].toSegment();
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500231 BOOST_VERIFY(segment == (m_nextSegment - 1));
232
Davide Pesavento0c139512018-11-03 18:23:38 -0400233 auto finalBlockId = data.getFinalBlock();
Weiqi Shi5822e342014-08-21 20:05:30 -0700234 if (finalBlockId == name[-1]) {
235 m_isFinished = true;
236 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500237 else {
Weiqi Shi5822e342014-08-21 20:05:30 -0700238 // Reset retry counter
239 m_retryCount = 0;
240 if (m_hasVersion)
241 fetchData(Name(m_dataName).appendSegment(m_nextSegment++));
242 else
243 fetchData(Name(m_dataName).append(name[-2]).appendSegment(m_nextSegment++));
244 }
245}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700246
247void
248Consumer::onTimeout(const Interest& interest)
249{
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500250 if (m_retryCount++ < MAX_RETRY) {
251 // Retransmit the interest
252 fetchData(interest.getName());
weijia yuan82cf9142018-10-21 12:25:02 -0700253 if (m_verbose) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800254 std::cerr << "TIMEOUT: retransmit interest for " << interest.getName() << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700255 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500256 }
257 else {
258 std::cerr << "TIMEOUT: last interest sent for segment #" << (m_nextSegment - 1) << std::endl;
259 std::cerr << "TIMEOUT: abort fetching after " << MAX_RETRY
260 << " times of retry" << std::endl;
261 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700262}
263
Davide Pesavento31be3f62019-01-24 23:20:23 -0500264static void
265usage(const char* programName)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700266{
Davide Pesavento31be3f62019-01-24 23:20:23 -0500267 std::cerr << "Usage: "
268 << programName << " [-v] [-s] [-u] [-l lifetime] [-w timeout] [-o filename] ndn-name\n"
269 << "\n"
270 << " -v: be verbose\n"
271 << " -s: only get single data packet\n"
272 << " -u: versioned: ndn-name contains version component\n"
273 << " if -u is not specified, this command will return the rightmost child for the prefix\n"
274 << " -l: InterestLifetime in milliseconds\n"
275 << " -w: timeout in milliseconds for whole process (default unlimited)\n"
276 << " -o: write to local file name instead of stdout\n"
277 << " ndn-name: NDN Name prefix for Data to be read\n"
278 << std::endl;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700279}
280
Davide Pesaventoc221e362019-01-24 22:26:54 -0500281static int
Wentao Shangbcbc9292014-04-28 21:17:06 -0700282main(int argc, char** argv)
283{
284 std::string name;
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500285 const char* outputFile = nullptr;
Weiqi Shi5822e342014-08-21 20:05:30 -0700286 bool verbose = false, versioned = false, single = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700287 int interestLifetime = 4000; // in milliseconds
288 int timeout = 0; // in milliseconds
289
290 int opt;
Davide Pesavento31be3f62019-01-24 23:20:23 -0500291 while ((opt = getopt(argc, argv, "hvsul:w:o:")) != -1) {
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500292 switch (opt) {
Davide Pesavento31be3f62019-01-24 23:20:23 -0500293 case 'h':
294 usage(argv[0]);
295 return 0;
296 case 'v':
297 verbose = true;
298 break;
299 case 's':
300 single = true;
301 break;
302 case 'u':
303 versioned = true;
304 break;
305 case 'l':
306 try {
307 interestLifetime = boost::lexical_cast<int>(optarg);
308 }
309 catch (const boost::bad_lexical_cast&) {
310 std::cerr << "ERROR: -l option should be an integer" << std::endl;
311 return 2;
312 }
313 interestLifetime = std::max(interestLifetime, 0);
314 break;
315 case 'w':
316 try {
317 timeout = boost::lexical_cast<int>(optarg);
318 }
319 catch (const boost::bad_lexical_cast&) {
320 std::cerr << "ERROR: -w option should be an integer" << std::endl;
321 return 2;
322 }
323 timeout = std::max(timeout, 0);
324 break;
325 case 'o':
326 outputFile = optarg;
327 break;
328 default:
329 usage(argv[0]);
330 return 2;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700331 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500332 }
333
weijia yuan82cf9142018-10-21 12:25:02 -0700334 if (optind < argc) {
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500335 name = argv[optind];
weijia yuan82cf9142018-10-21 12:25:02 -0700336 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500337
weijia yuan82cf9142018-10-21 12:25:02 -0700338 if (name.empty()) {
Davide Pesavento31be3f62019-01-24 23:20:23 -0500339 usage(argv[0]);
340 return 2;
weijia yuan82cf9142018-10-21 12:25:02 -0700341 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700342
343 std::streambuf* buf;
344 std::ofstream of;
345
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500346 if (outputFile != nullptr) {
347 of.open(outputFile, std::ios::out | std::ios::binary | std::ios::trunc);
348 if (!of || !of.is_open()) {
349 std::cerr << "ERROR: cannot open " << outputFile << std::endl;
Davide Pesavento31be3f62019-01-24 23:20:23 -0500350 return 2;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700351 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500352 buf = of.rdbuf();
353 }
354 else {
355 buf = std::cout.rdbuf();
356 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700357
358 std::ostream os(buf);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500359 Consumer consumer(name, os, verbose, versioned, single, interestLifetime, timeout);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700360
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500361 try {
362 consumer.run();
363 }
364 catch (const std::exception& e) {
365 std::cerr << "ERROR: " << e.what() << std::endl;
Davide Pesavento31be3f62019-01-24 23:20:23 -0500366 return 1;
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500367 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700368
369 return 0;
370}
371
372} // namespace repo
373
374int
375main(int argc, char** argv)
376{
377 return repo::main(argc, argv);
378}