blob: 559541cdfda3f9a40d7b9170d4d195a237906e41 [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 Pesaventof43a03e2018-02-21 22:04:21 -05003 * Copyright (c) 2014-2018, 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
20#include "ndngetfile.hpp"
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040021
Wentao Shang91fb4f22014-05-20 10:55:22 -070022#include <fstream>
Alexander Afanasyevc0e26582017-08-13 21:16:49 -040023#include <iostream>
24
25#include <boost/lexical_cast.hpp>
Wentao Shangbcbc9292014-04-28 21:17:06 -070026
27namespace repo {
28
Wentao Shanga8f3c402014-10-30 14:03:27 -070029using ndn::Name;
30using ndn::Interest;
31using ndn::Data;
32using ndn::Block;
33
34using std::bind;
35using std::placeholders::_1;
36using std::placeholders::_2;
Wentao Shangbcbc9292014-04-28 21:17:06 -070037
Weiqi Shi5822e342014-08-21 20:05:30 -070038static const int MAX_RETRY = 3;
39
Wentao Shangbcbc9292014-04-28 21:17:06 -070040void
41Consumer::fetchData(const Name& name)
42{
43 Interest interest(name);
44 interest.setInterestLifetime(m_interestLifetime);
Davide Pesaventof43a03e2018-02-21 22:04:21 -050045 if (m_hasVersion) {
46 interest.setMustBeFresh(m_mustBeFresh);
47 }
48 else {
49 interest.setMustBeFresh(true);
Davide Pesaventof43a03e2018-02-21 22:04:21 -050050 }
Wentao Shangbcbc9292014-04-28 21:17:06 -070051
weijia yuan3aa8d2b2018-03-06 15:35:57 -080052 interest.setCanBePrefix(m_canBePrefix);
53
Wentao Shangbcbc9292014-04-28 21:17:06 -070054 m_face.expressInterest(interest,
weijia yuan3aa8d2b2018-03-06 15:35:57 -080055 m_hasVersion ? std::bind(&Consumer::onVersionedData, this, _1, _2)
56 : std::bind(&Consumer::onUnversionedData, this, _1, _2),
57 std::bind(&Consumer::onTimeout, this, _1), // Nack
58 std::bind(&Consumer::onTimeout, this, _1));
Wentao Shangbcbc9292014-04-28 21:17:06 -070059}
60
61void
62Consumer::run()
63{
64 // Send the first Interest
65 Name name(m_dataName);
Wentao Shangbcbc9292014-04-28 21:17:06 -070066
Weiqi Shi5822e342014-08-21 20:05:30 -070067 m_nextSegment++;
Wentao Shangbcbc9292014-04-28 21:17:06 -070068 fetchData(name);
69
70 // processEvents will block until the requested data received or timeout occurs
71 m_face.processEvents(m_timeout);
72}
73
74void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080075Consumer::onVersionedData(const Interest& interest, const Data& data)
Wentao Shangbcbc9292014-04-28 21:17:06 -070076{
77 const Name& name = data.getName();
Wentao Shangbcbc9292014-04-28 21:17:06 -070078
Weiqi Shi5822e342014-08-21 20:05:30 -070079 // the received data name may have segment number or not
80 if (name.size() == m_dataName.size()) {
81 if (!m_isSingle) {
82 Name fetchName = name;
83 fetchName.appendSegment(0);
84 fetchData(fetchName);
85 }
86 }
87 else if (name.size() == m_dataName.size() + 1) {
88 if (!m_isSingle) {
89 if (m_isFirst) {
90 uint64_t segment = name[-1].toSegment();
91 if (segment != 0) {
92 fetchData(Name(m_dataName).appendSegment(0));
93 m_isFirst = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -070094 return;
95 }
Weiqi Shi5822e342014-08-21 20:05:30 -070096 m_isFirst = false;
97 }
98 fetchNextData(name, data);
Wentao Shangbcbc9292014-04-28 21:17:06 -070099 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700100 else {
101 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
102 return;
103 }
104 }
105 else {
106 std::cerr << "ERROR: Name size does not match" << std::endl;
107 return;
108 }
109 readData(data);
110}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700111
Weiqi Shi5822e342014-08-21 20:05:30 -0700112void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800113Consumer::onUnversionedData(const Interest& interest, const Data& data)
Weiqi Shi5822e342014-08-21 20:05:30 -0700114{
115 const Name& name = data.getName();
Weiqi Shi5822e342014-08-21 20:05:30 -0700116 if (name.size() == m_dataName.size() + 1) {
117 if (!m_isSingle) {
118 Name fetchName = name;
119 fetchName.append(name[-1]).appendSegment(0);
120 fetchData(fetchName);
121 }
122 }
123 else if (name.size() == m_dataName.size() + 2) {
124 if (!m_isSingle) {
125 if (m_isFirst) {
126 uint64_t segment = name[-1].toSegment();
127 if (segment != 0) {
128 fetchData(Name(m_dataName).append(name[-2]).appendSegment(0));
129 m_isFirst = false;
130 return;
131 }
132 m_isFirst = false;
133 }
134 fetchNextData(name, data);
135 }
136 else {
137 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
138 return;
139 }
140 }
141 else {
142 std::cerr << "ERROR: Name size does not match" << std::endl;
143 return;
144 }
145 readData(data);
146}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700147
Weiqi Shi5822e342014-08-21 20:05:30 -0700148void
149Consumer::readData(const Data& data)
150{
Wentao Shangbcbc9292014-04-28 21:17:06 -0700151 const Block& content = data.getContent();
152 m_os.write(reinterpret_cast<const char*>(content.value()), content.value_size());
153 m_totalSize += content.value_size();
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500154 if (m_verbose) {
Weiqi Shi5822e342014-08-21 20:05:30 -0700155 std::cerr << "LOG: received data = " << data.getName() << std::endl;
156 }
157 if (m_isFinished || m_isSingle) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800158 std::cerr << "INFO: End of file is reached" << std::endl;
Weiqi Shi5822e342014-08-21 20:05:30 -0700159 std::cerr << "INFO: Total # of segments received: " << m_nextSegment << std::endl;
160 std::cerr << "INFO: Total # bytes of content received: " << m_totalSize << std::endl;
161 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700162}
163
Weiqi Shi5822e342014-08-21 20:05:30 -0700164void
165Consumer::fetchNextData(const Name& name, const Data& data)
166{
167 uint64_t segment = name[-1].toSegment();
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500168 BOOST_VERIFY(segment == (m_nextSegment - 1));
169
Davide Pesavento0c139512018-11-03 18:23:38 -0400170 auto finalBlockId = data.getFinalBlock();
Weiqi Shi5822e342014-08-21 20:05:30 -0700171 if (finalBlockId == name[-1]) {
172 m_isFinished = true;
173 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500174 else {
Weiqi Shi5822e342014-08-21 20:05:30 -0700175 // Reset retry counter
176 m_retryCount = 0;
177 if (m_hasVersion)
178 fetchData(Name(m_dataName).appendSegment(m_nextSegment++));
179 else
180 fetchData(Name(m_dataName).append(name[-2]).appendSegment(m_nextSegment++));
181 }
182}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700183
184void
185Consumer::onTimeout(const Interest& interest)
186{
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500187 if (m_retryCount++ < MAX_RETRY) {
188 // Retransmit the interest
189 fetchData(interest.getName());
weijia yuan82cf9142018-10-21 12:25:02 -0700190 if (m_verbose) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800191 std::cerr << "TIMEOUT: retransmit interest for " << interest.getName() << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700192 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500193 }
194 else {
195 std::cerr << "TIMEOUT: last interest sent for segment #" << (m_nextSegment - 1) << std::endl;
196 std::cerr << "TIMEOUT: abort fetching after " << MAX_RETRY
197 << " times of retry" << std::endl;
198 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700199}
200
Wentao Shangbcbc9292014-04-28 21:17:06 -0700201int
202usage(const std::string& filename)
203{
204 std::cerr << "Usage: \n "
Weiqi Shi5822e342014-08-21 20:05:30 -0700205 << filename << " [-v] [-s] [-u] [-l lifetime] [-w timeout] [-o filename] ndn-name\n\n"
Wentao Shangbcbc9292014-04-28 21:17:06 -0700206 << "-v: be verbose\n"
Weiqi Shi5822e342014-08-21 20:05:30 -0700207 << "-s: only get single data packet\n"
208 << "-u: versioned: ndn-name contains version component\n"
209 << " if -u is not specified, this command will return the rightmost child for the prefix\n"
Wentao Shangbcbc9292014-04-28 21:17:06 -0700210 << "-l: InterestLifetime in milliseconds\n"
211 << "-w: timeout in milliseconds for whole process (default unlimited)\n"
Weiqi Shi5822e342014-08-21 20:05:30 -0700212 << "-o: write to local file name instead of stdout\n"
213 << "ndn-name: NDN Name prefix for Data to be read\n";
Wentao Shangbcbc9292014-04-28 21:17:06 -0700214 return 1;
215}
216
Wentao Shangbcbc9292014-04-28 21:17:06 -0700217int
218main(int argc, char** argv)
219{
220 std::string name;
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500221 const char* outputFile = nullptr;
Weiqi Shi5822e342014-08-21 20:05:30 -0700222 bool verbose = false, versioned = false, single = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700223 int interestLifetime = 4000; // in milliseconds
224 int timeout = 0; // in milliseconds
225
226 int opt;
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500227 while ((opt = getopt(argc, argv, "vsul:w:o:")) != -1) {
228 switch (opt) {
weijia yuan82cf9142018-10-21 12:25:02 -0700229 case 'v':
230 verbose = true;
231 break;
232 case 's':
233 single = true;
234 break;
235 case 'u':
236 versioned = true;
237 break;
238 case 'l':
239 try {
240 interestLifetime = boost::lexical_cast<int>(optarg);
241 }
242 catch (const boost::bad_lexical_cast&) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800243 std::cerr << "ERROR: -l option should be an integer" << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700244 return 1;
245 }
246 interestLifetime = std::max(interestLifetime, 0);
247 break;
248 case 'w':
249 try {
250 timeout = boost::lexical_cast<int>(optarg);
251 }
252 catch (const boost::bad_lexical_cast&) {
weijia yuan3aa8d2b2018-03-06 15:35:57 -0800253 std::cerr << "ERROR: -w option should be an integer" << std::endl;
weijia yuan82cf9142018-10-21 12:25:02 -0700254 return 1;
255 }
256 timeout = std::max(timeout, 0);
257 break;
258 case 'o':
259 outputFile = optarg;
260 break;
261 default:
262 return usage(argv[0]);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700263 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500264 }
265
weijia yuan82cf9142018-10-21 12:25:02 -0700266 if (optind < argc) {
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500267 name = argv[optind];
weijia yuan82cf9142018-10-21 12:25:02 -0700268 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500269
weijia yuan82cf9142018-10-21 12:25:02 -0700270 if (name.empty()) {
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500271 return usage(argv[0]);
weijia yuan82cf9142018-10-21 12:25:02 -0700272 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700273
274 std::streambuf* buf;
275 std::ofstream of;
276
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500277 if (outputFile != nullptr) {
278 of.open(outputFile, std::ios::out | std::ios::binary | std::ios::trunc);
279 if (!of || !of.is_open()) {
280 std::cerr << "ERROR: cannot open " << outputFile << std::endl;
281 return 1;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700282 }
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500283 buf = of.rdbuf();
284 }
285 else {
286 buf = std::cout.rdbuf();
287 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700288
289 std::ostream os(buf);
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500290 Consumer consumer(name, os, verbose, versioned, single, interestLifetime, timeout);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700291
Davide Pesaventof43a03e2018-02-21 22:04:21 -0500292 try {
293 consumer.run();
294 }
295 catch (const std::exception& e) {
296 std::cerr << "ERROR: " << e.what() << std::endl;
297 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700298
299 return 0;
300}
301
302} // namespace repo
303
304int
305main(int argc, char** argv)
306{
307 return repo::main(argc, argv);
308}