blob: 2a26962eb35acc95175cc5d85e998bf846a4ccdc [file] [log] [blame]
Wentao Shangbcbc9292014-04-28 21:17:06 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
3 * Copyright (c) 2014, Regents of the University of California.
4 *
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"
21#include <boost/lexical_cast.hpp>
22
23namespace repo {
24
25using namespace ndn;
26
27void
28Consumer::fetchData(const Name& name)
29{
30 Interest interest(name);
31 interest.setInterestLifetime(m_interestLifetime);
32
33 if (m_hasVersion)
34 {
35 interest.setMustBeFresh(m_mustBeFresh);
36 }
37 else
38 {
39 interest.setMustBeFresh(true);
40 interest.setChildSelector(1);
41 }
42
43 m_face.expressInterest(interest,
44 bind(&Consumer::onData, this, _1, _2),
45 bind(&Consumer::onTimeout, this, _1));
46}
47
48void
49Consumer::run()
50{
51 // Send the first Interest
52 Name name(m_dataName);
53 if (m_hasVersion)
54 // If '-u' is specified, we already have version number in the name
55 name.appendSegment(m_nextSegment++);
56
57 fetchData(name);
58
59 // processEvents will block until the requested data received or timeout occurs
60 m_face.processEvents(m_timeout);
61}
62
63void
64Consumer::onData(const Interest& interest, Data& data)
65{
66 const Name& name = data.getName();
67 if (name.size() != m_dataName.size() + (m_hasVersion ? 1 : 2))
68 throw std::invalid_argument("unexpected data name size.");
69
70 uint64_t segment = name[-1].toSegment();
71
72 if (!m_hasVersion)
73 {
74 // Assume the second to last component is the version number
75 // and the last component is the segment number
76 m_dataName.append(name[-2]);
77 m_hasVersion = true;
78 if (m_verbose)
79 {
80 std::cerr << "LOG: version number is " << name[-2].toVersion() << std::endl;
81 }
82
83 if (segment != 0)
84 {
85 // Discard this segment and fetch the first segment
86 fetchData(Name(m_dataName).appendSegment(m_nextSegment++));
87 return;
88 }
89
90 m_nextSegment++;
91 }
92
93 BOOST_ASSERT(segment == (m_nextSegment - 1));
94
95 // Output the current segment
96 const Block& content = data.getContent();
97 m_os.write(reinterpret_cast<const char*>(content.value()), content.value_size());
98 m_totalSize += content.value_size();
99 if (m_verbose)
100 {
101 std::cerr << "LOG: received segment #" << segment << std::endl;
102 }
103
104 // Check final block id
105 const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
106 if (finalBlockId == name[-1])
107 {
108 // Reach EOF
109 std::cerr << "INFO: End of file is reached." << std::endl;
110 std::cerr << "INFO: Total # of segments received: " << (m_nextSegment - 1) << std::endl;
111 std::cerr << "INFO: Total # bytes of content received: " << m_totalSize << std::endl;
112 }
113 else
114 {
115 // Reset retry counter
116 m_retryCount = 0;
117
118 // Fetch next segment
119 fetchData(Name(m_dataName).appendSegment(m_nextSegment++));
120 }
121}
122
123const int Consumer::MAX_RETRY = 3;
124
125void
126Consumer::onTimeout(const Interest& interest)
127{
128 if (m_retryCount++ < Consumer::MAX_RETRY)
129 {
130 // Retransmit the interest
131 fetchData(interest.getName());
132 if (m_verbose)
133 {
134 std::cerr << "TIMEOUT: retransmit interest for " << interest.getName() << std::endl;
135 }
136 }
137 else
138 {
139 std::cerr << "TIMEOUT: last interest sent for segment #" << (m_nextSegment - 1) << std::endl;
140 std::cerr << "TIMEOUT: abort fetching after " << Consumer::MAX_RETRY
141 << " times of retry" << std::endl;
142 }
143}
144
145
146int
147usage(const std::string& filename)
148{
149 std::cerr << "Usage: \n "
150 << filename << " [-v] [-u] [-l lifetime] [-w timeout] [-o filename] ndn-name\n\n"
151 << "-v: be verbose\n"
152 << "-u: unversioned: do not try to find the latest version; "
153 "ndn-name contains version component\n"
154 << "ndn-name: NDN Name prefix for Data to be read\n"
155 << "-l: InterestLifetime in milliseconds\n"
156 << "-w: timeout in milliseconds for whole process (default unlimited)\n"
157 << "-o: write to local file name instead of stdout\n";
158 return 1;
159}
160
161
162int
163main(int argc, char** argv)
164{
165 std::string name;
166 const char* outputFile = 0;
167 bool verbose = false, unversioned = false;
168 int interestLifetime = 4000; // in milliseconds
169 int timeout = 0; // in milliseconds
170
171 int opt;
172 while ((opt = getopt(argc, argv, "vul:w:o:")) != -1)
173 {
174 switch (opt) {
175 case 'v':
176 verbose = true;
177 break;
178 case 'u':
179 unversioned = true;
180 break;
181 case 'l':
182 try
183 {
184 interestLifetime = boost::lexical_cast<int>(optarg);
185 }
186 catch (boost::bad_lexical_cast&)
187 {
188 std::cerr << "ERROR: -l option should be an integer." << std::endl;
189 return 1;
190 }
191 interestLifetime = std::max(interestLifetime, 0);
192 break;
193 case 'w':
194 try
195 {
196 timeout = boost::lexical_cast<int>(optarg);
197 }
198 catch (boost::bad_lexical_cast&)
199 {
200 std::cerr << "ERROR: -w option should be an integer." << std::endl;
201 return 1;
202 }
203 timeout = std::max(timeout, 0);
204 break;
205 case 'o':
206 outputFile = optarg;
207 break;
208 default:
209 return usage(argv[0]);
210 }
211 }
212
213 if (optind < argc)
214 {
215 name = argv[optind];
216 }
217
218 if (name.empty())
219 {
220 return usage(argv[0]);
221 }
222
223 std::streambuf* buf;
224 std::ofstream of;
225
226 if (outputFile != 0)
227 {
228 of.open(outputFile);
229 if (!of)
230 {
231 std::cerr << "ERROR: output file is invalid" << std::endl;
232 return 1;
233 }
234 buf = of.rdbuf();
235 }
236 else
237 {
238 buf = std::cout.rdbuf();
239 }
240
241 std::ostream os(buf);
242
243 Consumer consumer(name, os, verbose, unversioned,
244 interestLifetime, timeout);
245
246 try
247 {
248 consumer.run();
249 }
250 catch (const std::exception& e)
251 {
252 std::cerr << "ERROR: " << e.what() << std::endl;
253 }
254
255 return 0;
256}
257
258} // namespace repo
259
260int
261main(int argc, char** argv)
262{
263 return repo::main(argc, argv);
264}