blob: 20fdb02977527a93edac7205e05a297ab9e1fc9c [file] [log] [blame]
Mickey Sweatt527b0492016-03-02 11:07:48 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3* Copyright (c) 2016 Regents of the University of California.
4*
5* This file is part of the nTorrent codebase.
6*
7* nTorrent is free software: you can redistribute it and/or modify it under the
8* terms of the GNU Lesser General Public License as published by the Free Software
9* Foundation, either version 3 of the License, or (at your option) any later version.
10*
11* nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY
12* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14*
15* You should have received copies of the GNU General Public License and GNU Lesser
16* General Public License along with nTorrent, e.g., in COPYING.md file. If not, see
17* <http://www.gnu.org/licenses/>.
18*
19* See AUTHORS for complete list of nTorrent authors and contributors.
20*/
21#ifndef INCLUDED_TORRENT_FILE_MANAGER_H
22#define INCLUDED_TORRENT_FILE_MANAGER_H
23
Mickey Sweatt527b0492016-03-02 11:07:48 -080024#include "file-manifest.hpp"
spirosmastorakisa46eee42016-04-05 14:24:45 -070025#include "torrent-file.hpp"
spirosmastorakis4ff8c872016-04-14 09:51:38 -070026#include "update-handler.hpp"
Mickey Sweatt527b0492016-03-02 11:07:48 -080027
Mickey Sweatt527b0492016-03-02 11:07:48 -080028#include <ndn-cxx/data.hpp>
spirosmastorakisa46eee42016-04-05 14:24:45 -070029#include <ndn-cxx/face.hpp>
30#include <ndn-cxx/interest.hpp>
31#include <ndn-cxx/link.hpp>
32#include <ndn-cxx/name.hpp>
33#include <ndn-cxx/security/key-chain.hpp>
Mickey Sweatt527b0492016-03-02 11:07:48 -080034
35#include <boost/filesystem/fstream.hpp>
Mickey Sweatt527b0492016-03-02 11:07:48 -080036
37#include <functional>
38#include <memory>
39#include <string>
40#include <unordered_map>
Mickey Sweatt44e4fd92016-05-02 15:43:11 -070041#include <unordered_set>
Mickey Sweatt527b0492016-03-02 11:07:48 -080042#include <vector>
43
44namespace fs = boost::filesystem;
45
46namespace ndn {
47namespace ntorrent {
48
Mickey Sweatt599bfef2016-04-05 19:11:20 -070049class TorrentManager : noncopyable {
Mickey Sweatt527b0492016-03-02 11:07:48 -080050 /**
51 * \class TorrentManager
52 *
53 * \brief A class to manage the interaction with the system in seeding/leaching a torrent
54 */
55 public:
spirosmastorakisa46eee42016-04-05 14:24:45 -070056 typedef std::function<void(const ndn::Name&)> DataReceivedCallback;
57 typedef std::function<void(const std::vector<ndn::Name>&)> ManifestReceivedCallback;
spirosmastorakis50642f82016-04-08 12:11:18 -070058 typedef std::function<void(const std::vector<ndn::Name>&)> TorrentFileReceivedCallback;
spirosmastorakisa46eee42016-04-05 14:24:45 -070059 typedef std::function<void(const ndn::Name&, const std::string&)> FailedCallback;
Mickey Sweatt527b0492016-03-02 11:07:48 -080060
spirosmastorakisa46eee42016-04-05 14:24:45 -070061 /*
62 * \brief Create a new Torrent manager with the specified parameters.
63 * @param torrentFileName The full name of the initial segment of the torrent file
64 * @param dataPath The path to the location on disk to use for the torrent data
65 * @param face Optional face object to be used for data retrieval
66 *
67 * The behavior is undefined unless Initialize() is called before calling any other method on a
68 * TorrentManger object.
69 */
Mickey Sweatte908a5c2016-04-08 14:10:45 -070070 TorrentManager(const ndn::Name& torrentFileName,
71 const std::string& dataPath,
Mickey Sweatt44e4fd92016-05-02 15:43:11 -070072 bool seed = true,
Mickey Sweatte908a5c2016-04-08 14:10:45 -070073 std::shared_ptr<Face> face = nullptr);
spirosmastorakisa46eee42016-04-05 14:24:45 -070074
Mickey Sweatt527b0492016-03-02 11:07:48 -080075 /*
spirosmastorakisa46eee42016-04-05 14:24:45 -070076 * @brief Initialize the state of this object.
Mickey Sweatt527b0492016-03-02 11:07:48 -080077 *
Mickey Sweatt527b0492016-03-02 11:07:48 -080078 * Read and validate from disk all torrent file segments, file manifests, and data packets for
79 * the torrent file managed by this object initializing all state in this manager respectively.
80 * Also seeds all validated data.
spirosmastorakisa46eee42016-04-05 14:24:45 -070081 */
82 void
83 Initialize();
84
Mickey Sweatt15dde2d2016-04-28 23:42:45 -070085 /**
86 * brief Return 'true' if all segments of the torrent file downloaded, 'false' otherwise.
87 */
88 bool
89 hasAllTorrentSegments() const;
90
spirosmastorakisa46eee42016-04-05 14:24:45 -070091 /*
Mickey Sweatt15dde2d2016-04-28 23:42:45 -070092 * \brief Given a data packet name, find whether we have already downloaded this packet
93 * @param dataName The name of the data packet to download
94 * @return True if we have already downloaded this packet, false otherwise
spirosmastorakisa46eee42016-04-05 14:24:45 -070095 *
Mickey Sweatt527b0492016-03-02 11:07:48 -080096 */
Mickey Sweatt15dde2d2016-04-28 23:42:45 -070097 bool
98 hasDataPacket(const Name& dataName) const;
99
100 /*
101 * \brief Find the torrent file segment that we should download
102 * (either we have nothing or we have them all)
103 * @return A shared_ptr to the name of the segment to download or
104 * nullptr if we have all the segments
105 *
106 */
107 shared_ptr<Name>
108 findTorrentFileSegmentToDownload() const;
109
110 /*
111 * \brief Given a file manifest segment name, find the next file manifest segment
112 * that we should download
113 * @param manifestName The name of the file manifest segment that we want to download
114 * @return A shared_ptr to the name of the segment to download or
115 * nullptr if we have all the segments
116 *
117 */
118 shared_ptr<Name>
119 findManifestSegmentToDownload(const Name& manifestName) const;
120
121 /*
122 * \brief Find the segments of all the file manifests that we are missing
123 * @param manifestNames The name of the manifest file segments to download (currently missing)
124 * This parameter is used as an output vector of names
125 */
126 void
127 findFileManifestsToDownload(std::vector<Name>& manifestNames) const;
128
129 /*
130 * \brief Find the names of the data packets of a file manifest that we are currently missing
131 * @param manifestName The name of the manifest
132 * @param packetNames The name of the data packets to be downloaded
133 * (used as an output vector of names)
134 *
135 * No matter what segment of a manifest file the manifestName parameter might refer to, the
136 * missing data packets starting from the first segment of this manifest file would be returned
137 *
138 */
139 void
140 findDataPacketsToDownload(const Name& manifestName, std::vector<Name>& packetNames) const;
141
142 /*
143 * \brief Find all the data packets that we are currently missing
144 * @param packetNames The name of the data packets to be downloaded
145 * (used as an output vector of names)
146 *
147 */
148 void
149 findAllMissingDataPackets(std::vector<Name>& packetNames) const;
Mickey Sweatt527b0492016-03-02 11:07:48 -0800150
spirosmastorakisa46eee42016-04-05 14:24:45 -0700151 /*
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700152 * @brief Stop all network activities of this manager
153 */
154 void
155 shutdown();
156 /*
spirosmastorakisa46eee42016-04-05 14:24:45 -0700157 * @brief Download the torrent file
158 * @param path The path to write the downloaded segments
159 * @param onSuccess Callback to be called if we successfully download all the
160 * segments of the torrent file. It passes the name of the file manifest
161 * to be downloaded to the callback
162 * @param onFailed Callaback to be called if we fail to download a segment of
163 * the torrent file. It passes the name of the torrent file segment that
164 * failed to download and a failure reason to the callback
165 *
166 * This method provides non-blocking downloading of all the torrent file segments
spirosmastorakisa46eee42016-04-05 14:24:45 -0700167 */
168 void
169 downloadTorrentFile(const std::string& path,
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700170 TorrentFileReceivedCallback onSuccess = {},
171 FailedCallback onFailed = {});
Mickey Sweatt527b0492016-03-02 11:07:48 -0800172
spirosmastorakisa46eee42016-04-05 14:24:45 -0700173 /*
174 * @brief Download a file manifest
175 * @param manifestName The name of the manifest file to be downloaded
176 * @param path The path to write the downloaded segments
177 * @param onSuccess Callback to be called if we successfully download all the
178 * segments of the file manifest. It passes the names of the data packets
179 * to be downloaded to the callback
180 * @param onFailed Callaback to be called if we fail to download a segment of
181 * the file manifest. It passes the name of the data packet that failed
182 * to download and a failure reason
183 *
184 * This method provides non-blocking downloading of all the file manifest segments
185 *
186 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700187 void
188 download_file_manifest(const Name& manifestName,
189 const std::string& path,
190 ManifestReceivedCallback onSuccess,
191 FailedCallback onFailed);
Mickey Sweatt527b0492016-03-02 11:07:48 -0800192
spirosmastorakisa46eee42016-04-05 14:24:45 -0700193 /*
194 * @brief Download a data packet
195 * @param packetName The name of the data packet to be downloaded
196 * @param onSuccess Callback to be called if we successfully download the data packet.
197 * It passes the name of the data packet to the callback
198 * @param onFailed Callaback to be called if we fail to download the requested data packet
199 * It passes the name of the data packet to the callback and a failure reason
200 *
201 * This method writes the downloaded data packet to m_dataPath on disk
202 *
203 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700204 void
205 download_data_packet(const Name& packetName,
206 DataReceivedCallback onSuccess,
207 FailedCallback onFailed);
Mickey Sweatt527b0492016-03-02 11:07:48 -0800208
Mickey Sweatt527b0492016-03-02 11:07:48 -0800209 // Seed the specified 'data' to the network.
spirosmastorakis50642f82016-04-08 12:11:18 -0700210 void
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700211 seed(const Data& data);
Mickey Sweatt527b0492016-03-02 11:07:48 -0800212
Mickey Sweattb7ee19c2016-04-21 12:18:15 -0700213 /**
214 * @brief Process any data to receive or call timeout callbacks and update prefix list (if needed)
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700215 * By default this blocks until all operations are complete.
Mickey Sweattb7ee19c2016-04-21 12:18:15 -0700216 */
217 void
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700218 processEvents(const time::milliseconds& timeout = time::milliseconds(0));
Mickey Sweattb7ee19c2016-04-21 12:18:15 -0700219
Mickey Sweatt527b0492016-03-02 11:07:48 -0800220 protected:
Mickey Sweattb7ee19c2016-04-21 12:18:15 -0700221 /**
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700222 * \brief Write @p packet composed of torrent date to disk.
223 * @param packet The data packet to be written to the disk
224 * Write the Data packet to disk, return 'true' if data successfully written to disk 'false'
225 * otherwise. Behavior is undefined unless the corresponding file manifest has already been
226 * downloaded.
spirosmastorakisa46eee42016-04-05 14:24:45 -0700227 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700228 bool
229 writeData(const Data& packet);
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700230
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700231 /*
232 * \brief Write the @p segment torrent segment to disk at the specified path.
233 * @param segment The torrent file segment to be written to disk
234 * @param path The path at which to write the torrent file segment
235 * Write the segment to disk, return 'true' if data successfully written to disk 'false'
236 * otherwise. Behavior is undefined unless @segment is a correct segment for the torrent file of
237 * this manager and @p path is the directory used for all segments of this torrent file.
238 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700239 bool
240 writeTorrentSegment(const TorrentFile& segment, const std::string& path);
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700241
Mickey Sweatt599bfef2016-04-05 19:11:20 -0700242 /*
243 * \brief Write the @p manifest file manifest to disk at the specified @p path.
244 * @param manifest The file manifest to be written to disk
245 * @param path The path at which to write the file manifest
246 * Write the file manifest to disk, return 'true' if data successfully written to disk 'false'
247 * otherwise. Behavior is undefined unless @manifest is a correct file manifest for a file in the
248 * torrent file of this manager and @p path is the directory used for all file manifests of this
249 * torrent file.
250 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700251 bool
252 writeFileManifest(const FileManifest& manifest, const std::string& path);
Mickey Sweattafda1f12016-04-04 17:15:11 -0700253
spirosmastorakisa46eee42016-04-05 14:24:45 -0700254 /*
255 * \brief Download the segments of the torrent file
256 * @param name The name of the torrent file to be downloaded
257 * @param path The path to write the torrent file on disk
spirosmastorakisa46eee42016-04-05 14:24:45 -0700258 * @param onSuccess Optional callback to be called when all the segments of the torrent file
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700259 * have been downloaded. The default value is an empty callback.
spirosmastorakisa46eee42016-04-05 14:24:45 -0700260 * @param onFailed Optional callback to be called when we fail to download a segment of the
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700261 * torrent file. The default value is an empty callback.
spirosmastorakisa46eee42016-04-05 14:24:45 -0700262 *
263 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700264 void
265 downloadTorrentFileSegment(const ndn::Name& name,
266 const std::string& path,
spirosmastorakis50642f82016-04-08 12:11:18 -0700267 TorrentFileReceivedCallback onSuccess,
268 FailedCallback onFailed);
Mickey Sweatt527b0492016-03-02 11:07:48 -0800269
spirosmastorakisa46eee42016-04-05 14:24:45 -0700270 /*
271 * \brief Download the segments of a file manifest
272 * @param manifestName The name of the file manifest to be downloaded
273 * @param path The path to write the file manifest on disk
274 * @param packetNames A vector containing the name of the data packets in the file manifest
275 * @param onSuccess Callback to be called when all the segments of the file manifest have been
276 * downloaded
277 * @param onFailed Callback to be called when we fail to download a file manifest segment
278 *
279 */
spirosmastorakis50642f82016-04-08 12:11:18 -0700280 void
281 downloadFileManifestSegment(const Name& manifestName,
282 const std::string& path,
283 std::shared_ptr<std::vector<Name>> packetNames,
284 ManifestReceivedCallback onSuccess,
285 FailedCallback onFailed);
spirosmastorakisa46eee42016-04-05 14:24:45 -0700286
287 enum {
288 // Number of times to retry if a routable prefix fails to retrieve data
289 MAX_NUM_OF_RETRIES = 5,
290 // Number of Interests to be sent before sorting the stats table
291 SORTING_INTERVAL = 100
292 };
Mickey Sweatt527b0492016-03-02 11:07:48 -0800293
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700294 void onDataReceived(const Data& data);
295
296 void
297 onInterestReceived(const InterestFilter& filter, const Interest& interest);
298
299 void
300 onRegisterFailed(const Name& prefix, const std::string& reason);
301
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700302protected:
Mickey Sweatt527b0492016-03-02 11:07:48 -0800303 // A map from each fileManifest to corresponding file stream on disk and a bitmap of which Data
304 // packets this manager currently has
305 mutable std::unordered_map<Name,
306 std::pair<std::shared_ptr<fs::fstream>,
307 std::vector<bool>>> m_fileStates;
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700308 // A map for each initial manifest to the size for the sub-manifest
309 std::unordered_map<std::string, size_t> m_subManifestSizes;
Mickey Sweatt527b0492016-03-02 11:07:48 -0800310 // The segments of the TorrentFile this manager has
311 std::vector<TorrentFile> m_torrentSegments;
312 // The FileManifests this manager has
313 std::vector<FileManifest> m_fileManifests;
314 // The name of the initial segment of the torrent file for this manager
315 Name m_torrentFileName;
316 // The path to the location on disk of the Data packet for this manager
317 std::string m_dataPath;
spirosmastorakisa46eee42016-04-05 14:24:45 -0700318
319private:
320 shared_ptr<Interest>
321 createInterest(Name name);
322
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700323 // A flag to determine if upon completion we should continue seeding
324 bool m_seedFlag;
spirosmastorakisa46eee42016-04-05 14:24:45 -0700325 // Face used for network communication
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700326 std::shared_ptr<Face> m_face;
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700327 // Stats table where routable prefixes are stored
328 StatsTable m_statsTable;
spirosmastorakisa46eee42016-04-05 14:24:45 -0700329 // Iterator to the routable prefix that we currently use
330 StatsTable::iterator m_stats_table_iter;
331 // Number of retries per routable prefix
332 uint64_t m_retries;
333 // Number of Interests sent since last sorting
334 uint64_t m_sortingCounter;
335 // Keychain instance
spirosmastorakis4ff8c872016-04-14 09:51:38 -0700336 shared_ptr<KeyChain> m_keyChain;
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700337
338 std::unordered_set<ndn::Name> m_pendingInterests;
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700339 // TODO(spyros) Fix and reintegrate update handler
340 // // Update Handler instance
341 // shared_ptr<UpdateHandler> m_updateHandler;
Mickey Sweatt527b0492016-03-02 11:07:48 -0800342};
343
344inline
spirosmastorakisa46eee42016-04-05 14:24:45 -0700345TorrentManager::TorrentManager(const ndn::Name& torrentFileName,
346 const std::string& dataPath,
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700347 bool seed,
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700348 std::shared_ptr<Face> face)
Mickey Sweatt527b0492016-03-02 11:07:48 -0800349: m_fileStates()
350, m_torrentSegments()
351, m_fileManifests()
352, m_torrentFileName(torrentFileName)
353, m_dataPath(dataPath)
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700354, m_seedFlag(seed)
spirosmastorakisa46eee42016-04-05 14:24:45 -0700355, m_face(face)
356, m_retries(0)
357, m_sortingCounter(0)
358, m_keyChain(new KeyChain())
Mickey Sweatt527b0492016-03-02 11:07:48 -0800359{
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700360 if(face == nullptr) {
spirosmastorakis4ff8c872016-04-14 09:51:38 -0700361 m_face = make_shared<Face>();
Mickey Sweatte908a5c2016-04-08 14:10:45 -0700362 }
spirosmastorakis4ff8c872016-04-14 09:51:38 -0700363
spirosmastorakisa46eee42016-04-05 14:24:45 -0700364 // Hardcoded prefixes for now
365 // TODO(Spyros): Think of something more clever to bootstrap...
366 m_statsTable.insert("/ucla");
367 m_statsTable.insert("/arizona");
368 m_stats_table_iter = m_statsTable.begin();
Mickey Sweatt527b0492016-03-02 11:07:48 -0800369}
370
Mickey Sweattb7ee19c2016-04-21 12:18:15 -0700371inline
372void
373TorrentManager::processEvents(const time::milliseconds& timeout)
374{
375 m_face->processEvents(timeout);
376}
377
Mickey Sweatt15dde2d2016-04-28 23:42:45 -0700378inline
379bool
380TorrentManager::hasAllTorrentSegments() const
381{
382 return findTorrentFileSegmentToDownload() == nullptr;
383}
384
Mickey Sweatt44e4fd92016-05-02 15:43:11 -0700385inline
386void
387TorrentManager::shutdown()
388{
389 // TODO(msweatt) Consider unregistering all prefix to exit more gracefully
390 m_face->shutdown();
391}
392
Mickey Sweatt527b0492016-03-02 11:07:48 -0800393} // end ntorrent
394} // end ndn
395
spirosmastorakisa46eee42016-04-05 14:24:45 -0700396#endif // INCLUDED_TORRENT_FILE_MANAGER_H