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