blob: b552ec839505e7d867da64e7bf53ff3c1dda3960 [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>
26
27#include <boost/lexical_cast.hpp>
Wentao Shangbcbc9292014-04-28 21:17:06 -070028
29namespace repo {
30
Wentao Shanga8f3c402014-10-30 14:03:27 -070031using ndn::Name;
32using ndn::Interest;
33using ndn::Data;
Wentao Shanga8f3c402014-10-30 14:03:27 -070034
Davide Pesaventoc221e362019-01-24 22:26:54 -050035class Consumer : boost::noncopyable
36{
37public:
38 Consumer(const std::string& dataName, std::ostream& os,
39 bool verbose, bool versioned, bool single,
40 int interestLifetime, int timeout,
41 bool mustBeFresh = false,
42 bool canBePrefix = false)
43 : m_dataName(dataName)
44 , m_os(os)
45 , m_verbose(verbose)
46 , m_hasVersion(versioned)
47 , m_isSingle(single)
48 , m_isFinished(false)
49 , m_isFirst(true)
50 , m_interestLifetime(interestLifetime)
51 , m_timeout(timeout)
52 , m_nextSegment(0)
53 , m_totalSize(0)
54 , m_retryCount(0)
55 , m_mustBeFresh(mustBeFresh)
56 , m_canBePrefix(canBePrefix)
57 {
58 }
Wentao Shangbcbc9292014-04-28 21:17:06 -070059
Davide Pesaventoc221e362019-01-24 22:26:54 -050060 void
61 run();
62
63private:
64 void
65 fetchData(const ndn::Name& name);
66
67 void
68 onVersionedData(const ndn::Interest& interest, const ndn::Data& data);
69
70 void
71 onUnversionedData(const ndn::Interest& interest, const ndn::Data& data);
72
73 void
74 onTimeout(const ndn::Interest& interest);
75
76 void
77 readData(const ndn::Data& data);
78
79 void
80 fetchNextData(const ndn::Name& name, const ndn::Data& data);
81
82private:
83 ndn::Face m_face;
84 ndn::Name m_dataName;
85 std::ostream& m_os;
86 bool m_verbose;
87 bool m_hasVersion;
88 bool m_isSingle;
89 bool m_isFinished;
90 bool m_isFirst;
91 ndn::time::milliseconds m_interestLifetime;
92 ndn::time::milliseconds m_timeout;
93 uint64_t m_nextSegment;
94 int m_totalSize;
95 int m_retryCount;
96 bool m_mustBeFresh;
97 bool m_canBePrefix;
98
99 static constexpr int MAX_RETRY = 3;
100};
Weiqi Shi5822e342014-08-21 20:05:30 -0700101
Wentao Shangbcbc9292014-04-28 21:17:06 -0700102void
103Consumer::fetchData(const Name& name)
104{
105 Interest interest(name);
106 interest.setInterestLifetime(m_interestLifetime);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500107 if (m_hasVersion) {
108 interest.setMustBeFresh(m_mustBeFresh);
109 }
110 else {
111 interest.setMustBeFresh(true);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500112 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700113
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800114 interest.setCanBePrefix(m_canBePrefix);
115
Wentao Shangbcbc9292014-04-28 21:17:06 -0700116 m_face.expressInterest(interest,
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800117 m_hasVersion ? std::bind(&Consumer::onVersionedData, this, _1, _2)
118 : std::bind(&Consumer::onUnversionedData, this, _1, _2),
119 std::bind(&Consumer::onTimeout, this, _1), // Nack
120 std::bind(&Consumer::onTimeout, this, _1));
Wentao Shangbcbc9292014-04-28 21:17:06 -0700121}
122
123void
124Consumer::run()
125{
126 // Send the first Interest
127 Name name(m_dataName);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700128
Weiqi Shi5822e342014-08-21 20:05:30 -0700129 m_nextSegment++;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700130 fetchData(name);
131
132 // processEvents will block until the requested data received or timeout occurs
133 m_face.processEvents(m_timeout);
134}
135
136void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800137Consumer::onVersionedData(const Interest& interest, const Data& data)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700138{
139 const Name& name = data.getName();
Wentao Shangbcbc9292014-04-28 21:17:06 -0700140
Weiqi Shi5822e342014-08-21 20:05:30 -0700141 // the received data name may have segment number or not
142 if (name.size() == m_dataName.size()) {
143 if (!m_isSingle) {
144 Name fetchName = name;
145 fetchName.appendSegment(0);
146 fetchData(fetchName);
147 }
148 }
149 else if (name.size() == m_dataName.size() + 1) {
150 if (!m_isSingle) {
151 if (m_isFirst) {
152 uint64_t segment = name[-1].toSegment();
153 if (segment != 0) {
154 fetchData(Name(m_dataName).appendSegment(0));
155 m_isFirst = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700156 return;
157 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700158 m_isFirst = false;
159 }
160 fetchNextData(name, data);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700161 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700162 else {
163 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
164 return;
165 }
166 }
167 else {
168 std::cerr << "ERROR: Name size does not match" << std::endl;
169 return;
170 }
171 readData(data);
172}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700173
Weiqi Shi5822e342014-08-21 20:05:30 -0700174void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800175Consumer::onUnversionedData(const Interest& interest, const Data& data)
Weiqi Shi5822e342014-08-21 20:05:30 -0700176{
177 const Name& name = data.getName();
Weiqi Shi5822e342014-08-21 20:05:30 -0700178 if (name.size() == m_dataName.size() + 1) {
179 if (!m_isSingle) {
180 Name fetchName = name;
181 fetchName.append(name[-1]).appendSegment(0);
182 fetchData(fetchName);
183 }
184 }
185 else if (name.size() == m_dataName.size() + 2) {
186 if (!m_isSingle) {
187 if (m_isFirst) {
188 uint64_t segment = name[-1].toSegment();
189 if (segment != 0) {
190 fetchData(Name(m_dataName).append(name[-2]).appendSegment(0));
191 m_isFirst = false;
192 return;
193 }
194 m_isFirst = false;
195 }
196 fetchNextData(name, data);
197 }
198 else {
199 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
200 return;
201 }
202 }
203 else {
204 std::cerr << "ERROR: Name size does not match" << std::endl;
205 return;
206 }
207 readData(data);
208}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700209
Weiqi Shi5822e342014-08-21 20:05:30 -0700210void
211Consumer::readData(const Data& data)
212{
Davide Pesaventoc221e362019-01-24 22:26:54 -0500213 const auto& content = data.getContent();
Wentao Shangbcbc9292014-04-28 21:17:06 -0700214 m_os.write(reinterpret_cast<const char*>(content.value()), content.value_size());
215 m_totalSize += content.value_size();
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500216 if (m_verbose) {
Weiqi Shi5822e342014-08-21 20:05:30 -0700217 std::cerr << "LOG: received data = " << data.getName() << std::endl;
218 }
219 if (m_isFinished || m_isSingle) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800220 std::cerr << "INFO: End of file is reached" << std::endl;
Weiqi Shi5822e342014-08-21 20:05:30 -0700221 std::cerr << "INFO: Total # of segments received: " << m_nextSegment << std::endl;
222 std::cerr << "INFO: Total # bytes of content received: " << m_totalSize << std::endl;
223 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700224}
225
Weiqi Shi5822e342014-08-21 20:05:30 -0700226void
227Consumer::fetchNextData(const Name& name, const Data& data)
228{
229 uint64_t segment = name[-1].toSegment();
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500230 BOOST_VERIFY(segment == (m_nextSegment - 1));
231
Davide Pesavento0c139512018-11-03 18:23:38 -0400232 auto finalBlockId = data.getFinalBlock();
Weiqi Shi5822e342014-08-21 20:05:30 -0700233 if (finalBlockId == name[-1]) {
234 m_isFinished = true;
235 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500236 else {
Weiqi Shi5822e342014-08-21 20:05:30 -0700237 // Reset retry counter
238 m_retryCount = 0;
239 if (m_hasVersion)
240 fetchData(Name(m_dataName).appendSegment(m_nextSegment++));
241 else
242 fetchData(Name(m_dataName).append(name[-2]).appendSegment(m_nextSegment++));
243 }
244}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700245
246void
247Consumer::onTimeout(const Interest& interest)
248{
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500249 if (m_retryCount++ < MAX_RETRY) {
250 // Retransmit the interest
251 fetchData(interest.getName());
weijia yuan82cf9142018-10-21 12:25:02 -0700252 if (m_verbose) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800253 std::cerr << "TIMEOUT: retransmit interest for " << interest.getName() << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700254 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500255 }
256 else {
257 std::cerr << "TIMEOUT: last interest sent for segment #" << (m_nextSegment - 1) << std::endl;
258 std::cerr << "TIMEOUT: abort fetching after " << MAX_RETRY
259 << " times of retry" << std::endl;
260 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700261}
262
Davide Pesaventoc221e362019-01-24 22:26:54 -0500263static int
Wentao Shangbcbc9292014-04-28 21:17:06 -0700264usage(const std::string& filename)
265{
266 std::cerr << "Usage: \n "
Weiqi Shi5822e342014-08-21 20:05:30 -0700267 << filename << " [-v] [-s] [-u] [-l lifetime] [-w timeout] [-o filename] ndn-name\n\n"
Wentao Shangbcbc9292014-04-28 21:17:06 -0700268 << "-v: be verbose\n"
Weiqi Shi5822e342014-08-21 20:05:30 -0700269 << "-s: only get single data packet\n"
270 << "-u: versioned: ndn-name contains version component\n"
271 << " if -u is not specified, this command will return the rightmost child for the prefix\n"
Wentao Shangbcbc9292014-04-28 21:17:06 -0700272 << "-l: InterestLifetime in milliseconds\n"
273 << "-w: timeout in milliseconds for whole process (default unlimited)\n"
Weiqi Shi5822e342014-08-21 20:05:30 -0700274 << "-o: write to local file name instead of stdout\n"
275 << "ndn-name: NDN Name prefix for Data to be read\n";
Wentao Shangbcbc9292014-04-28 21:17:06 -0700276 return 1;
277}
278
Davide Pesaventoc221e362019-01-24 22:26:54 -0500279static int
Wentao Shangbcbc9292014-04-28 21:17:06 -0700280main(int argc, char** argv)
281{
282 std::string name;
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500283 const char* outputFile = nullptr;
Weiqi Shi5822e342014-08-21 20:05:30 -0700284 bool verbose = false, versioned = false, single = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700285 int interestLifetime = 4000; // in milliseconds
286 int timeout = 0; // in milliseconds
287
288 int opt;
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500289 while ((opt = getopt(argc, argv, "vsul:w:o:")) != -1) {
290 switch (opt) {
weijia yuan82cf9142018-10-21 12:25:02 -0700291 case 'v':
292 verbose = true;
293 break;
294 case 's':
295 single = true;
296 break;
297 case 'u':
298 versioned = true;
299 break;
300 case 'l':
301 try {
302 interestLifetime = boost::lexical_cast<int>(optarg);
303 }
304 catch (const boost::bad_lexical_cast&) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800305 std::cerr << "ERROR: -l option should be an integer" << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700306 return 1;
307 }
308 interestLifetime = std::max(interestLifetime, 0);
309 break;
310 case 'w':
311 try {
312 timeout = boost::lexical_cast<int>(optarg);
313 }
314 catch (const boost::bad_lexical_cast&) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800315 std::cerr << "ERROR: -w option should be an integer" << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700316 return 1;
317 }
318 timeout = std::max(timeout, 0);
319 break;
320 case 'o':
321 outputFile = optarg;
322 break;
323 default:
324 return usage(argv[0]);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700325 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500326 }
327
weijia yuan82cf9142018-10-21 12:25:02 -0700328 if (optind < argc) {
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500329 name = argv[optind];
weijia yuan82cf9142018-10-21 12:25:02 -0700330 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500331
weijia yuan82cf9142018-10-21 12:25:02 -0700332 if (name.empty()) {
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500333 return usage(argv[0]);
weijia yuan82cf9142018-10-21 12:25:02 -0700334 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700335
336 std::streambuf* buf;
337 std::ofstream of;
338
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500339 if (outputFile != nullptr) {
340 of.open(outputFile, std::ios::out | std::ios::binary | std::ios::trunc);
341 if (!of || !of.is_open()) {
342 std::cerr << "ERROR: cannot open " << outputFile << std::endl;
343 return 1;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700344 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500345 buf = of.rdbuf();
346 }
347 else {
348 buf = std::cout.rdbuf();
349 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700350
351 std::ostream os(buf);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500352 Consumer consumer(name, os, verbose, versioned, single, interestLifetime, timeout);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700353
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500354 try {
355 consumer.run();
356 }
357 catch (const std::exception& e) {
358 std::cerr << "ERROR: " << e.what() << std::endl;
359 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700360
361 return 0;
362}
363
364} // namespace repo
365
366int
367main(int argc, char** argv)
368{
369 return repo::main(argc, argv);
370}