blob: 125eab0e1855e471662538a11459135c6ac1308e [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/*
Alexander Afanasyev42290b22017-03-09 12:58:29 -08003 * Copyright (c) 2014-2017, 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);
Weiqi Shi5822e342014-08-21 20:05:30 -070045 //std::cout<<"interest name = "<<interest.getName()<<std::endl;
Wentao Shangbcbc9292014-04-28 21:17:06 -070046 if (m_hasVersion)
47 {
48 interest.setMustBeFresh(m_mustBeFresh);
49 }
50 else
51 {
52 interest.setMustBeFresh(true);
53 interest.setChildSelector(1);
54 }
55
56 m_face.expressInterest(interest,
Weiqi Shi5822e342014-08-21 20:05:30 -070057 m_hasVersion ?
Wentao Shanga8f3c402014-10-30 14:03:27 -070058 bind(&Consumer::onVersionedData, this, _1, _2)
59 :
60 bind(&Consumer::onUnversionedData, this, _1, _2),
Alexander Afanasyev42290b22017-03-09 12:58:29 -080061 bind(&Consumer::onTimeout, this, _1), // Nack
Wentao Shangbcbc9292014-04-28 21:17:06 -070062 bind(&Consumer::onTimeout, this, _1));
63}
64
65void
66Consumer::run()
67{
68 // Send the first Interest
69 Name name(m_dataName);
Wentao Shangbcbc9292014-04-28 21:17:06 -070070
Weiqi Shi5822e342014-08-21 20:05:30 -070071 m_nextSegment++;
Wentao Shangbcbc9292014-04-28 21:17:06 -070072 fetchData(name);
73
74 // processEvents will block until the requested data received or timeout occurs
75 m_face.processEvents(m_timeout);
76}
77
78void
Alexander Afanasyev42290b22017-03-09 12:58:29 -080079Consumer::onVersionedData(const Interest& interest, const Data& data)
Wentao Shangbcbc9292014-04-28 21:17:06 -070080{
81 const Name& name = data.getName();
Wentao Shangbcbc9292014-04-28 21:17:06 -070082
Weiqi Shi5822e342014-08-21 20:05:30 -070083 // the received data name may have segment number or not
84 if (name.size() == m_dataName.size()) {
85 if (!m_isSingle) {
86 Name fetchName = name;
87 fetchName.appendSegment(0);
88 fetchData(fetchName);
89 }
90 }
91 else if (name.size() == m_dataName.size() + 1) {
92 if (!m_isSingle) {
93 if (m_isFirst) {
94 uint64_t segment = name[-1].toSegment();
95 if (segment != 0) {
96 fetchData(Name(m_dataName).appendSegment(0));
97 m_isFirst = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -070098 return;
99 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700100 m_isFirst = false;
101 }
102 fetchNextData(name, data);
Wentao Shangbcbc9292014-04-28 21:17:06 -0700103 }
Weiqi Shi5822e342014-08-21 20:05:30 -0700104 else {
105 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
106 return;
107 }
108 }
109 else {
110 std::cerr << "ERROR: Name size does not match" << std::endl;
111 return;
112 }
113 readData(data);
114}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700115
Weiqi Shi5822e342014-08-21 20:05:30 -0700116void
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800117Consumer::onUnversionedData(const Interest& interest, const Data& data)
Weiqi Shi5822e342014-08-21 20:05:30 -0700118{
119 const Name& name = data.getName();
120 //std::cout<<"recevied data name = "<<name<<std::endl;
121 if (name.size() == m_dataName.size() + 1) {
122 if (!m_isSingle) {
123 Name fetchName = name;
124 fetchName.append(name[-1]).appendSegment(0);
125 fetchData(fetchName);
126 }
127 }
128 else if (name.size() == m_dataName.size() + 2) {
129 if (!m_isSingle) {
130 if (m_isFirst) {
131 uint64_t segment = name[-1].toSegment();
132 if (segment != 0) {
133 fetchData(Name(m_dataName).append(name[-2]).appendSegment(0));
134 m_isFirst = false;
135 return;
136 }
137 m_isFirst = false;
138 }
139 fetchNextData(name, data);
140 }
141 else {
142 std::cerr << "ERROR: Data is not stored in a single packet" << std::endl;
143 return;
144 }
145 }
146 else {
147 std::cerr << "ERROR: Name size does not match" << std::endl;
148 return;
149 }
150 readData(data);
151}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700152
Weiqi Shi5822e342014-08-21 20:05:30 -0700153void
154Consumer::readData(const Data& data)
155{
Wentao Shangbcbc9292014-04-28 21:17:06 -0700156 const Block& content = data.getContent();
157 m_os.write(reinterpret_cast<const char*>(content.value()), content.value_size());
158 m_totalSize += content.value_size();
159 if (m_verbose)
Weiqi Shi5822e342014-08-21 20:05:30 -0700160 {
161 std::cerr << "LOG: received data = " << data.getName() << std::endl;
162 }
163 if (m_isFinished || m_isSingle) {
164 std::cerr << "INFO: End of file is reached." << std::endl;
165 std::cerr << "INFO: Total # of segments received: " << m_nextSegment << std::endl;
166 std::cerr << "INFO: Total # bytes of content received: " << m_totalSize << std::endl;
167 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700168}
169
Weiqi Shi5822e342014-08-21 20:05:30 -0700170void
171Consumer::fetchNextData(const Name& name, const Data& data)
172{
173 uint64_t segment = name[-1].toSegment();
174 BOOST_ASSERT(segment == (m_nextSegment - 1));
Wentao Shanga8f3c402014-10-30 14:03:27 -0700175 const ndn::name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
Weiqi Shi5822e342014-08-21 20:05:30 -0700176 if (finalBlockId == name[-1]) {
177 m_isFinished = true;
178 }
179 else
180 {
181 // Reset retry counter
182 m_retryCount = 0;
183 if (m_hasVersion)
184 fetchData(Name(m_dataName).appendSegment(m_nextSegment++));
185 else
186 fetchData(Name(m_dataName).append(name[-2]).appendSegment(m_nextSegment++));
187 }
188}
Wentao Shangbcbc9292014-04-28 21:17:06 -0700189
190void
191Consumer::onTimeout(const Interest& interest)
192{
Weiqi Shi5822e342014-08-21 20:05:30 -0700193 if (m_retryCount++ < MAX_RETRY)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700194 {
195 // Retransmit the interest
196 fetchData(interest.getName());
197 if (m_verbose)
198 {
199 std::cerr << "TIMEOUT: retransmit interest for " << interest.getName() << std::endl;
200 }
201 }
202 else
203 {
204 std::cerr << "TIMEOUT: last interest sent for segment #" << (m_nextSegment - 1) << std::endl;
Weiqi Shi5822e342014-08-21 20:05:30 -0700205 std::cerr << "TIMEOUT: abort fetching after " << MAX_RETRY
Wentao Shangbcbc9292014-04-28 21:17:06 -0700206 << " times of retry" << std::endl;
207 }
208}
209
210
211int
212usage(const std::string& filename)
213{
214 std::cerr << "Usage: \n "
Weiqi Shi5822e342014-08-21 20:05:30 -0700215 << filename << " [-v] [-s] [-u] [-l lifetime] [-w timeout] [-o filename] ndn-name\n\n"
Wentao Shangbcbc9292014-04-28 21:17:06 -0700216 << "-v: be verbose\n"
Weiqi Shi5822e342014-08-21 20:05:30 -0700217 << "-s: only get single data packet\n"
218 << "-u: versioned: ndn-name contains version component\n"
219 << " if -u is not specified, this command will return the rightmost child for the prefix\n"
Wentao Shangbcbc9292014-04-28 21:17:06 -0700220 << "-l: InterestLifetime in milliseconds\n"
221 << "-w: timeout in milliseconds for whole process (default unlimited)\n"
Weiqi Shi5822e342014-08-21 20:05:30 -0700222 << "-o: write to local file name instead of stdout\n"
223 << "ndn-name: NDN Name prefix for Data to be read\n";
Wentao Shangbcbc9292014-04-28 21:17:06 -0700224 return 1;
225}
226
227
228int
229main(int argc, char** argv)
230{
231 std::string name;
232 const char* outputFile = 0;
Weiqi Shi5822e342014-08-21 20:05:30 -0700233 bool verbose = false, versioned = false, single = false;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700234 int interestLifetime = 4000; // in milliseconds
235 int timeout = 0; // in milliseconds
236
237 int opt;
Weiqi Shi5822e342014-08-21 20:05:30 -0700238 while ((opt = getopt(argc, argv, "vsul:w:o:")) != -1)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700239 {
240 switch (opt) {
241 case 'v':
242 verbose = true;
243 break;
Weiqi Shi5822e342014-08-21 20:05:30 -0700244 case 's':
245 single = true;
246 break;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700247 case 'u':
Weiqi Shi5822e342014-08-21 20:05:30 -0700248 versioned = true;
Wentao Shangbcbc9292014-04-28 21:17:06 -0700249 break;
250 case 'l':
251 try
252 {
253 interestLifetime = boost::lexical_cast<int>(optarg);
254 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800255 catch (const boost::bad_lexical_cast&)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700256 {
257 std::cerr << "ERROR: -l option should be an integer." << std::endl;
258 return 1;
259 }
260 interestLifetime = std::max(interestLifetime, 0);
261 break;
262 case 'w':
263 try
264 {
265 timeout = boost::lexical_cast<int>(optarg);
266 }
Alexander Afanasyev42290b22017-03-09 12:58:29 -0800267 catch (const boost::bad_lexical_cast&)
Wentao Shangbcbc9292014-04-28 21:17:06 -0700268 {
269 std::cerr << "ERROR: -w option should be an integer." << std::endl;
270 return 1;
271 }
272 timeout = std::max(timeout, 0);
273 break;
274 case 'o':
275 outputFile = optarg;
276 break;
277 default:
278 return usage(argv[0]);
279 }
280 }
281
282 if (optind < argc)
283 {
284 name = argv[optind];
285 }
286
287 if (name.empty())
288 {
289 return usage(argv[0]);
290 }
291
292 std::streambuf* buf;
293 std::ofstream of;
294
295 if (outputFile != 0)
296 {
Diarmuid Collinsdd6348a2014-12-14 12:32:07 -0800297 of.open(outputFile, std::ios::out | std::ios::binary | std::ios::trunc);
298 if (!of || !of.is_open()) {
299 std::cerr << "ERROR: cannot open " << outputFile << std::endl;
300 return 1;
301 }
Wentao Shangbcbc9292014-04-28 21:17:06 -0700302 buf = of.rdbuf();
303 }
304 else
305 {
306 buf = std::cout.rdbuf();
307 }
308
309 std::ostream os(buf);
310
Weiqi Shi5822e342014-08-21 20:05:30 -0700311 Consumer consumer(name, os, verbose, versioned, single,
Wentao Shangbcbc9292014-04-28 21:17:06 -0700312 interestLifetime, timeout);
313
314 try
315 {
316 consumer.run();
317 }
318 catch (const std::exception& e)
319 {
320 std::cerr << "ERROR: " << e.what() << std::endl;
321 }
322
323 return 0;
324}
325
326} // namespace repo
327
328int
329main(int argc, char** argv)
330{
331 return repo::main(argc, argv);
332}